# Copyright (C) 2009-2017 by the Free Software Foundation, Inc. # # This file is part of GNU Mailman. # # GNU Mailman is free software: you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. # # GNU Mailman is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # more details. # # You should have received a copy of the GNU General Public License along with # GNU Mailman. If not, see . """Test configuration file searching.""" import os import sys import shutil import tempfile import unittest from contextlib import contextmanager from mailman.core.initialize import search_for_configuration_file from mailman.testing.helpers import hackenv # Here are a couple of context managers that make our tests easier to read. @contextmanager def fakedirs(path): """Create and clean up a directory hierarchy.""" os.makedirs(path) try: yield finally: shutil.rmtree(path) @contextmanager def chdir(new_cwd): """Change to the directory, then back again.""" old_cwd = os.getcwd() os.chdir(new_cwd) try: yield finally: os.chdir(old_cwd) @contextmanager def argv0(new_argv0): """Change argv0, then back again.""" old_argv0 = sys.argv[0] sys.argv[0] = new_argv0 try: yield finally: sys.argv[0] = old_argv0 class TestConfigFileBase(unittest.TestCase): """Common test infrastructure.""" def setUp(self): self._root = tempfile.mkdtemp() # Ensure that the environment can't cause test failures. self.mailman_config_file = os.environ.get('MAILMAN_CONFIG_FILE') if self.mailman_config_file is not None: del os.environ['MAILMAN_CONFIG_FILE'] def tearDown(self): shutil.rmtree(self._root) # Restore the environment, though I'm not actually sure this is # necessary. if self.mailman_config_file is not None: os.environ['MAILMAN_CONFIG_FILE'] = self.mailman_config_file def _make_fake(self, path): if path.startswith('/'): path = path[1:] return os.path.join(self._root, path) class TestConfigFileSearch(TestConfigFileBase): """Test various aspects of searching for configuration files. Note that the command line -C option is not tested here. """ def test_current_working_directory(self): fake_cwd = '/home/alex/mailman/hacking' fake_testdir = self._make_fake(fake_cwd) config_file = os.path.realpath( os.path.join(fake_testdir, 'mailman.cfg')) with fakedirs(fake_testdir): # Write a mostly empty configuration file. with open(os.path.join(fake_testdir, 'mailman.cfg'), 'w') as fp: print('# Fake mailman.cfg file', file=fp) with chdir(fake_testdir): # Sometimes symlinks bite us (eg. OS X /var -> /private/var). found = os.path.realpath(search_for_configuration_file()) self.assertEqual(found, config_file) class TestConfigFileSearchWithChroot(TestConfigFileBase): """Like `TestConfigFileSearch` but with a special os.path.exists().""" def setUp(self): TestConfigFileBase.setUp(self) # We can't actually call os.chroot() unless we're root. Neither can # we write to say /etc/mailman.cfg without being root (of course we # wouldn't want to even if we could). The easiest way to fake a file # system that we can write to and test is to hack os.path.exists() to # prepend a temporary directory onto the path it tests. self._os_path_exists = os.path.exists def exists(path): # noqa: E306 # Strip off the leading slash, otherwise we'll end up with path. return self._os_path_exists(self._make_fake(path)) os.path.exists = exists def tearDown(self): os.path.exists = self._os_path_exists TestConfigFileBase.tearDown(self) def test_baseline(self): # With nothing set, and no configuration files, just return None. self.assertEqual(search_for_configuration_file(), None) def test_environment_variable_to_missing_path(self): # Test that $MAILMAN_CONFIG_FILE pointing to a non-existent path still # returns None. with hackenv('MAILMAN_CONFIG_FILE', '/does/not/exist'): self.assertEqual(search_for_configuration_file(), None) def test_environment_variable(self): # Test that $MAILMAN_CONFIG_FILE pointing to an existing path returns # that path. fake_home = '/home/geddy/testing/mailman' fake_testdir = self._make_fake(fake_home) config_file = os.path.join(fake_home, 'mailman.cfg') with fakedirs(fake_testdir): # Write a mostly empty configuration file. with open(os.path.join(fake_testdir, 'mailman.cfg'), 'w') as fp: print('# Fake mailman.cfg file', file=fp) with hackenv('MAILMAN_CONFIG_FILE', config_file): self.assertEqual(search_for_configuration_file(), config_file) def test_home_dot_file(self): # Test ~/.mailman.cfg fake_home = '/home/neil' fake_testdir = self._make_fake(fake_home) config_file = os.path.join(fake_home, '.mailman.cfg') with fakedirs(fake_testdir): # Write a mostly empty configuration file. with open(os.path.join(fake_testdir, '.mailman.cfg'), 'w') as fp: print('# Fake mailman.cfg file', file=fp) with hackenv('HOME', '/home/neil'): self.assertEqual(search_for_configuration_file(), config_file) def test_etc_file(self): # Test /etc/mailman.cfg fake_etc = '/etc' fake_testdir = self._make_fake(fake_etc) config_file = os.path.join(fake_etc, 'mailman.cfg') with fakedirs(fake_testdir): # Write a mostly empty configuration file. with open(os.path.join(fake_testdir, 'mailman.cfg'), 'w') as fp: print('# Fake mailman.cfg file', file=fp) self.assertEqual(search_for_configuration_file(), config_file) def test_sibling_directory(self): # Test $argv0/../../etc/mailman.cfg fake_root = '/usr/local/mm3' fake_testdir = self._make_fake(fake_root) config_file = os.path.join(fake_testdir, 'etc', 'mailman.cfg') fake_config_file = os.path.join(fake_root, 'etc', 'mailman.cfg') fake_argv0 = os.path.join(fake_root, 'bin', 'mailman') with fakedirs(fake_testdir): with argv0(fake_argv0): os.mkdir(os.path.dirname(config_file)) # Write a mostly empty configuration file. with open(config_file, 'w') as fp: print('# Fake mailman.cfg file', file=fp) self.assertEqual(search_for_configuration_file(), fake_config_file)