# Copyright (C) 2017 Lucas Hoffmann # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file """Test suite for alot.settings.manager module.""" from __future__ import absolute_import import os import tempfile import textwrap import unittest import mock from alot.settings.manager import SettingsManager from alot.settings.errors import ConfigError, NoMatchingAccount from .. import utilities class TestSettingsManager(unittest.TestCase): def test_reading_synchronize_flags_from_notmuch_config(self): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [maildir] synchronize_flags = true """)) self.addCleanup(os.unlink, f.name) manager = SettingsManager(notmuch_rc=f.name) actual = manager.get_notmuch_setting('maildir', 'synchronize_flags') self.assertTrue(actual) def test_parsing_notmuch_config_with_non_bool_synchronize_flag_fails(self): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [maildir] synchronize_flags = not bool """)) self.addCleanup(os.unlink, f.name) with self.assertRaises(ConfigError): SettingsManager(notmuch_rc=f.name) def test_reload_notmuch_config(self): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [maildir] synchronize_flags = false """)) self.addCleanup(os.unlink, f.name) manager = SettingsManager(notmuch_rc=f.name) with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [maildir] synchronize_flags = true """)) self.addCleanup(os.unlink, f.name) manager.notmuch_rc_path = f.name manager.reload() actual = manager.get_notmuch_setting('maildir', 'synchronize_flags') self.assertTrue(actual) def test_read_config_doesnt_exist(self): """If there is not an alot config things don't break. This specifically tests for issue #1094, which is caused by the defaults not being loaded if there isn't an alot config files, and thus calls like `get_theming_attribute` fail with strange exceptions. """ with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [maildir] synchronize_flags = true """)) self.addCleanup(os.unlink, f.name) manager = SettingsManager(notmuch_rc=f.name) manager.get_theming_attribute('global', 'body') def test_unknown_settings_in_config_are_logged(self): # todo: For py3, don't mock the logger, use assertLogs unknown_settings = ['templates_dir', 'unknown_section', 'unknown_1', 'unknown_2'] with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ {x[0]} = /templates/dir [{x[1]}] # Values in unknown sections are not reported. barfoo = barfoo [tags] [[foobar]] {x[2]} = baz translated = translation {x[3]} = bar """.format(x=unknown_settings))) self.addCleanup(os.unlink, f.name) with mock.patch('alot.settings.utils.logging') as mock_logger: SettingsManager(alot_rc=f.name) success = any(all([s in call_args[0][0] for s in unknown_settings]) for call_args in mock_logger.info.call_args_list) self.assertTrue(success, msg='Could not find all unknown settings in ' 'logging.info.\nUnknown settings:\n{}\nCalls to mocked' ' logging.info:\n{}'.format( unknown_settings, mock_logger.info.call_args_list)) def test_read_notmuch_config_doesnt_exist(self): with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [accounts] [[default]] realname = That Guy address = thatguy@example.com """)) self.addCleanup(os.unlink, f.name) manager = SettingsManager(alot_rc=f.name) setting = manager.get_notmuch_setting('foo', 'bar') self.assertIsNone(setting) def test_dont_choke_on_regex_special_chars_in_tagstring(self): tag = 'to**do' with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ [tags] [[{tag}]] normal = '','', 'white','light red', 'white','#d66' """.format(tag=tag))) self.addCleanup(os.unlink, f.name) manager = SettingsManager(alot_rc=f.name) manager.get_tagstring_representation(tag) class TestSettingsManagerExpandEnvironment(unittest.TestCase): """ Tests SettingsManager._expand_config_values """ setting_name = 'template_dir' xdg_name = 'XDG_CONFIG_HOME' default = '$%s/alot/templates' % xdg_name xdg_fallback = '~/.config' xdg_custom = '/foo/bar/.config' default_expanded = default.replace('$%s' % xdg_name, xdg_fallback) def test_no_user_setting_and_env_not_set(self): with mock.patch.dict('os.environ'): if self.xdg_name in os.environ: del os.environ[self.xdg_name] manager = SettingsManager() self.assertEqual(manager._config.get(self.setting_name), os.path.expanduser(self.default_expanded)) def test_no_user_setting_and_env_empty(self): with mock.patch.dict('os.environ', {self.xdg_name: ''}): manager = SettingsManager() self.assertEqual(manager._config.get(self.setting_name), os.path.expanduser(self.default_expanded)) def test_no_user_setting_and_env_not_empty(self): with mock.patch.dict('os.environ', {self.xdg_name: self.xdg_custom}): manager = SettingsManager() actual = manager._config.get(self.setting_name) expected = self.default.replace('$%s' % self.xdg_name, self.xdg_custom) self.assertEqual(actual, expected) def test_user_setting_and_env_not_empty(self): user_setting = '/path/to/template/dir' with mock.patch.dict('os.environ', {self.xdg_name: self.xdg_custom}): with tempfile.NamedTemporaryFile(delete=False) as f: f.write('template_dir = {}'.format(user_setting)) self.addCleanup(os.unlink, f.name) manager = SettingsManager(alot_rc=f.name) self.assertEqual(manager._config.get(self.setting_name), os.path.expanduser(user_setting)) def test_configobj_and_env_expansion(self): """ Three expansion styles: %(FOO)s - expanded by ConfigObj (string interpolation) $FOO and ${FOO} - should be expanded with environment variable """ foo_env = 'foo_set_from_env' with mock.patch.dict('os.environ', {self.xdg_name: self.xdg_custom, 'foo': foo_env}): foo_in_config = 'foo_set_in_config' with tempfile.NamedTemporaryFile(delete=False) as f: f.write(textwrap.dedent("""\ foo = {} template_dir = ${{XDG_CONFIG_HOME}}/$foo/%(foo)s/${{foo}} """.format(foo_in_config))) self.addCleanup(os.unlink, f.name) manager = SettingsManager(alot_rc=f.name) self.assertEqual(manager._config.get(self.setting_name), os.path.join(self.xdg_custom, foo_env, foo_in_config, foo_env)) class TestSettingsManagerGetAccountByAddress(utilities.TestCaseClassCleanup): """Test the get_account_by_address helper.""" @classmethod def setUpClass(cls): config = textwrap.dedent("""\ [accounts] [[default]] realname = That Guy address = that_guy@example.com sendmail_command = /bin/true [[other]] realname = A Dude address = a_dude@example.com sendmail_command = /bin/true """) # Allow settings.reload to work by not deleting the file until the end with tempfile.NamedTemporaryFile(delete=False) as f: f.write(config) cls.addClassCleanup(os.unlink, f.name) # Replace the actual settings object with our own using mock, but # ensure it's put back afterwards cls.manager = SettingsManager(alot_rc=f.name) def test_exists_addr(self): acc = self.manager.get_account_by_address(u'that_guy@example.com') self.assertEqual(acc.realname, 'That Guy') def test_doesnt_exist_return_default(self): acc = self.manager.get_account_by_address(u'doesntexist@example.com', return_default=True) self.assertEqual(acc.realname, 'That Guy') def test_doesnt_exist_raise(self): with self.assertRaises(NoMatchingAccount): self.manager.get_account_by_address(u'doesntexist@example.com') def test_doesnt_exist_no_default(self): with tempfile.NamedTemporaryFile() as f: f.write('') settings = SettingsManager(alot_rc=f.name) with self.assertRaises(NoMatchingAccount): settings.get_account_by_address('that_guy@example.com', return_default=True) def test_real_name_will_be_stripped_before_matching(self): acc = self.manager.get_account_by_address( 'That Guy ') self.assertEqual(acc.realname, 'A Dude') def test_address_case(self): """Some servers do not differentiate addresses by case. So, for example, "foo@example.com" and "Foo@example.com" would be considered the same. Among servers that do this gmail, yahoo, fastmail, anything running Exchange (i.e., most large corporations), and others. """ acc1 = self.manager.get_account_by_address('That_guy@example.com') acc2 = self.manager.get_account_by_address('that_guy@example.com') self.assertIs(acc1, acc2)