def _install_manpage(src_path, man_dir): """Install the given manual page for COT. Args: src_path (str): Path to manual page file. man_dir (str): Base directory where page should be installed. Returns: tuple: (page_previously_installed, page_updated) Raises: IOError: if installation fails under some circumstances OSError: if installation fails under other circumstances """ # Which man section does this belong in? filename = os.path.basename(src_path) section = os.path.splitext(filename)[1][1:] dest = os.path.join(man_dir, "man{0}".format(section)) Helper.mkdir(dest) previously_installed = False dest_path = os.path.join(dest, filename) if os.path.exists(dest_path): previously_installed = True if filecmp.cmp(src_path, dest_path): logger.verbose("File %s does not need to be updated", dest_path) return previously_installed, False Helper.copy_file(src_path, dest_path) return previously_installed, True
def test_nondefault_permissions(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Non-default permissions should be applied whether sudo or not.""" # Non-sudo case self.assertTrue(Helper.mkdir('/foo/bar', 511)) # 511 == 0o777 mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_called_with('/foo/bar') mock_makedirs.assert_called_with('/foo/bar', 511) mock_check_call.assert_not_called() # Sudo case mock_makedirs.reset_mock() mock_makedirs.side_effect = OSError self.assertTrue(Helper.mkdir('/foo/bar', 511)) # 511 == 0o777 mock_makedirs.assert_called_with('/foo/bar', 511) mock_check_call.assert_called_with( ['sudo', 'mkdir', '-p', '--mode=777', '/foo/bar'])
def test_permission_ok(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Successfully create directory with user permissions.""" self.assertTrue(Helper.mkdir('/foo/bar')) mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_called_with('/foo/bar') mock_makedirs.assert_called_with('/foo/bar', 493) # 493 == 0o755 mock_check_call.assert_not_called()
def test_nondefault_permissions(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Non-default permissions should be applied whether sudo or not.""" # Non-sudo case self.assertTrue(Helper.mkdir('/foo/bar', 511)) # 511 == 0o777 mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_called_with('/foo/bar') mock_makedirs.assert_called_with('/foo/bar', 511) mock_check_call.assert_not_called() # Sudo case mock_makedirs.reset_mock() mock_makedirs.side_effect = OSError self.assertTrue(Helper.mkdir('/foo/bar', 511)) # 511 == 0o777 mock_makedirs.assert_called_with('/foo/bar', 511) mock_check_call.assert_called_with( ['sudo', 'mkdir', '-p', '-m', '777', '/foo/bar'])
def test_already_exists(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Test case where the target directory already exists.""" mock_isdir.return_value = True self.assertTrue(Helper.mkdir('/foo/bar')) mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_not_called() mock_makedirs.assert_not_called() mock_check_call.assert_not_called()
def test_need_sudo(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Directory creation needs sudo.""" mock_makedirs.side_effect = OSError self.assertTrue(Helper.mkdir('/foo/bar')) mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_called_with('/foo/bar') mock_makedirs.assert_called_with('/foo/bar', 493) # 493 == 0o755 mock_check_call.assert_called_with( ['sudo', 'mkdir', '-p', '--mode=755', '/foo/bar'])
def test_need_sudo(self, mock_isdir, mock_exists, mock_makedirs, mock_check_call): """Directory creation needs sudo.""" mock_makedirs.side_effect = OSError self.assertTrue(Helper.mkdir('/foo/bar')) mock_isdir.assert_called_with('/foo/bar') mock_exists.assert_called_with('/foo/bar') mock_makedirs.assert_called_with('/foo/bar', 493) # 493 == 0o755 mock_check_call.assert_called_with( ['sudo', 'mkdir', '-p', '-m', '755', '/foo/bar'])
def test_permission_ok(self, mock_copy, mock_check_call): """File copy succeeds with user permissions.""" self.assertTrue(Helper.copy_file('/foo', '/bar')) mock_copy.assert_called_with('/foo', '/bar') mock_check_call.assert_not_called()
def setUp(self): """Test case setup function called automatically prior to each test.""" self.helper = Helper("generic") super(HelperGenericTest, self).setUp()
def test_need_sudo(self, mock_copy, mock_check_call): """File copy needs sudo.""" mock_copy.side_effect = OSError self.assertTrue(Helper.copy_file('/foo', '/bar')) mock_copy.assert_called_with('/foo', '/bar') mock_check_call.assert_called_with(['sudo', 'cp', '/foo', '/bar'])
class HelperGenericTest(HelperTestCase): """Test cases for generic Helper class.""" def setUp(self): """Test case setup function called automatically prior to each test.""" self.helper = Helper("generic") super(HelperGenericTest, self).setUp() def tearDown(self): """Cleanup function called automatically prior to each test.""" self.helper._installed = False Helper._provider_package = {} super(HelperGenericTest, self).tearDown() def test_check_call_helpernotfounderror(self): """HelperNotFoundError if executable doesn't exist.""" self.assertRaises(HelperNotFoundError, check_call, ["not_a_command"]) self.assertRaises(HelperNotFoundError, check_call, ["not_a_command"], require_success=True) def test_check_call_helpererror(self): """HelperError if executable fails and require_success is set.""" with self.assertRaises(HelperError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 1) check_call(["false"], require_success=False) @mock.patch('subprocess.check_call') def test_check_call_permissions_needed(self, mock_check_call): """Test cases where sudo permission is needed.""" def raise_oserror(args, **_): """Raise an OSError unless using 'sudo'.""" if args[0] != 'sudo': raise OSError(13, 'permission denied') return mock_check_call.side_effect = raise_oserror # Without retry_on_sudo, we reraise the permissions error with self.assertRaises(OSError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 13) mock_check_call.assert_called_once_with(["false"]) # With retry_on_sudo, we retry. mock_check_call.reset_mock() check_call(["false"], retry_with_sudo=True) mock_check_call.assert_has_calls([ mock.call(['false']), mock.call(['sudo', 'false']), ]) # Now a variant - the subprocess call actually executed, but the # process exited with a non-zero exit code def raise_subprocess_error(args, **_): """Raise a CalledProcessError unless using 'sudo'.""" if args[0] != 'sudo': raise subprocess.CalledProcessError(1, " ".join(args)) return mock_check_call.reset_mock() mock_check_call.side_effect = raise_subprocess_error # Without retry_on_sudo, we reraise the permissions error with self.assertRaises(HelperError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 1) mock_check_call.assert_called_once_with(["false"]) # With retry_on_sudo, we retry. mock_check_call.reset_mock() check_call(["false"], retry_with_sudo=True) mock_check_call.assert_has_calls([ mock.call(['false']), mock.call(['sudo', 'false']), ]) def test_check_output_helpernotfounderror(self): """HelperNotFoundError if executable doesn't exist.""" self.assertRaises(HelperNotFoundError, check_output, ["not_a_command"]) self.assertRaises(HelperNotFoundError, check_output, ["not_a_command"], require_success=True) def test_check_output_oserror(self): """OSError if requested command isn't an executable.""" self.assertRaises(OSError, check_output, self.input_ovf) def test_check_output_helpererror(self): """HelperError if executable fails and require_success is set.""" with self.assertRaises(HelperError) as catcher: check_output(["false"]) self.assertEqual(catcher.exception.errno, 1) check_output(["false"], require_success=False) @mock.patch('distutils.spawn.find_executable', return_value=None) def test_helper_not_found(self, *_): """Make sure helper.path is None if find_executable fails.""" self.assertEqual(self.helper.path, None) @mock.patch('COT.helpers.Helper._install') def test_install_already_present(self, mock_install): """Make installation is not attempted unnecessarily.""" self.helper._installed = True self.helper.install() mock_install.assert_not_called() @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) def test_install_not_implemented(self, *_): """If installable lies, default _install method should fail cleanly.""" self.helper._installed = False self.assertRaises(NotImplementedError, self.helper.install) @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) @mock.patch('platform.system', return_value='Darwin') def test_install_missing_package_manager_mac(self, *_): """RuntimeError if Mac install supported but brew/port are absent.""" self.helper._installed = False self.helper._provider_package['brew'] = 'install-me-with-brew' self.helper._provider_package['port'] = 'install-me-with-port' self.select_package_manager(None) with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Since both helpers are supported, we should see both messages self.assertRegex(msg, "COT can use Homebrew") self.assertRegex(msg, "COT can use MacPorts") del self.helper._provider_package['brew'] with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Now we should only see the supported one self.assertNotRegex(msg, "COT can use Homebrew") self.assertRegex(msg, "COT can use MacPorts") del self.helper._provider_package['port'] # Now we should fall back to NotImplementedError with self.assertRaises(NotImplementedError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") self.assertNotRegex(msg, "COT can use Homebrew") self.assertNotRegex(msg, "COT can use MacPorts") self.helper._provider_package['brew'] = 'install-me-with-brew' with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Now we should only see the supported one self.assertRegex(msg, "COT can use Homebrew") self.assertNotRegex(msg, "COT can use MacPorts") @mock.patch('COT.helpers.Helper._install') @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) def test_install_bad_implementation(self, *_): """If custom _install() doesn't do its job, install() catches it.""" self.assertRaises(HelperNotFoundError, self.helper.install) def test_call_install(self): """call will call install, which raises an error.""" self.assertRaises(NotImplementedError, self.helper.call, ["Hello!"]) def test_call_no_install(self): """If not installed, and user declines, raise HelperNotFoundError.""" _ui = Helper.USER_INTERFACE Helper.USER_INTERFACE = UI() Helper.USER_INTERFACE.default_confirm_response = False try: self.assertRaises(HelperNotFoundError, self.helper.call, ["Hello!"]) finally: Helper.USER_INTERFACE = _ui def test_download_and_expand_tgz(self): """Validate the download_and_expand_tgz() context_manager.""" try: with self.helper.download_and_expand_tgz( "https://github.com/glennmatthews/cot/archive/master.tar.gz" ) as directory: self.assertTrue(os.path.exists(directory)) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT", "tests"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT", "tests", "ut.py"))) except requests.exceptions.ConnectionError: # unable to connect to github - might be an isolated environment self.fail("ConnectionError when trying to download from GitHub") # Temporary directory should be cleaned up when done self.assertFalse(os.path.exists(directory))
class HelperGenericTest(HelperTestCase): """Test cases for generic Helper class.""" def setUp(self): """Test case setup function called automatically prior to each test.""" self.helper = Helper("generic") super(HelperGenericTest, self).setUp() def tearDown(self): """Cleanup function called automatically prior to each test.""" self.helper._installed = False Helper._provider_package = {} super(HelperGenericTest, self).tearDown() def test_check_call_helpernotfounderror(self): """HelperNotFoundError if executable doesn't exist.""" self.assertRaises(HelperNotFoundError, check_call, ["not_a_command"]) self.assertRaises(HelperNotFoundError, check_call, ["not_a_command"], require_success=True) def test_check_call_helpererror(self): """HelperError if executable fails and require_success is set.""" with self.assertRaises(HelperError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 1) check_call(["false"], require_success=False) @mock.patch('subprocess.check_call') def test_check_call_permissions_needed(self, mock_check_call): """Test cases where sudo permission is needed.""" def raise_oserror(args, **_): """Raise an OSError unless using 'sudo'.""" if args[0] != 'sudo': raise OSError(13, 'permission denied') return mock_check_call.side_effect = raise_oserror # Without retry_on_sudo, we reraise the permissions error with self.assertRaises(OSError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 13) mock_check_call.assert_called_once_with(["false"]) # With retry_on_sudo, we retry. mock_check_call.reset_mock() check_call(["false"], retry_with_sudo=True) mock_check_call.assert_has_calls([ mock.call(['false']), mock.call(['sudo', 'false']), ]) # Now a variant - the subprocess call actually executed, but the # process exited with a non-zero exit code def raise_subprocess_error(args, **_): """Raise a CalledProcessError unless using 'sudo'.""" if args[0] != 'sudo': raise subprocess.CalledProcessError(1, " ".join(args)) return mock_check_call.reset_mock() mock_check_call.side_effect = raise_subprocess_error # Without retry_on_sudo, we reraise the permissions error with self.assertRaises(HelperError) as catcher: check_call(["false"]) self.assertEqual(catcher.exception.errno, 1) mock_check_call.assert_called_once_with(["false"]) # With retry_on_sudo, we retry. mock_check_call.reset_mock() check_call(["false"], retry_with_sudo=True) mock_check_call.assert_has_calls([ mock.call(['false']), mock.call(['sudo', 'false']), ]) def test_check_output_helpernotfounderror(self): """HelperNotFoundError if executable doesn't exist.""" self.assertRaises(HelperNotFoundError, check_output, ["not_a_command"]) self.assertRaises(HelperNotFoundError, check_output, ["not_a_command"], require_success=True) def test_check_output_oserror(self): """OSError if requested command isn't an executable.""" self.assertRaises(OSError, check_output, self.input_ovf) def test_check_output_helpererror(self): """HelperError if executable fails and require_success is set.""" with self.assertRaises(HelperError) as catcher: check_output(["false"]) self.assertEqual(catcher.exception.errno, 1) check_output(["false"], require_success=False) @mock.patch('distutils.spawn.find_executable', return_value=None) def test_helper_not_found(self, *_): """Make sure helper.path is None if find_executable fails.""" self.assertEqual(self.helper.path, None) @mock.patch('COT.helpers.Helper._install') def test_install_already_present(self, mock_install): """Make installation is not attempted unnecessarily.""" self.helper._installed = True self.helper.install() mock_install.assert_not_called() @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) def test_install_not_implemented(self, *_): """If installable lies, default _install method should fail cleanly.""" self.helper._installed = False self.assertRaises(NotImplementedError, self.helper.install) @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) @mock.patch('platform.system', return_value='Darwin') def test_install_missing_package_manager_mac(self, *_): """RuntimeError if Mac install supported but brew/port are absent.""" self.helper._installed = False self.helper._provider_package['brew'] = 'install-me-with-brew' self.helper._provider_package['port'] = 'install-me-with-port' self.select_package_manager(None) with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Since both helpers are supported, we should see both messages self.assertRegex(msg, "COT can use Homebrew") self.assertRegex(msg, "COT can use MacPorts") del self.helper._provider_package['brew'] with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Now we should only see the supported one self.assertNotRegex(msg, "COT can use Homebrew") self.assertRegex(msg, "COT can use MacPorts") del self.helper._provider_package['port'] # Now we should fall back to NotImplementedError with self.assertRaises(NotImplementedError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") self.assertNotRegex(msg, "COT can use Homebrew") self.assertNotRegex(msg, "COT can use MacPorts") self.helper._provider_package['brew'] = 'install-me-with-brew' with self.assertRaises(RuntimeError) as catcher: self.helper.install() msg = str(catcher.exception) self.assertRegex(msg, "Unsure how to install generic.") # Now we should only see the supported one self.assertRegex(msg, "COT can use Homebrew") self.assertNotRegex(msg, "COT can use MacPorts") @mock.patch('COT.helpers.Helper._install') @mock.patch('COT.helpers.Helper.installable', new_callable=mock.PropertyMock, return_value=True) def test_install_bad_implementation(self, *_): """If custom _install() doesn't do its job, install() catches it.""" self.assertRaises(HelperNotFoundError, self.helper.install) def test_call_install(self): """call will call install, which raises an error.""" self.assertRaises(NotImplementedError, self.helper.call, ["Hello!"]) def test_call_no_install(self): """If not installed, and user declines, raise HelperNotFoundError.""" _ui = Helper.USER_INTERFACE Helper.USER_INTERFACE = UI() Helper.USER_INTERFACE.default_confirm_response = False try: self.assertRaises(HelperNotFoundError, self.helper.call, ["Hello!"]) finally: Helper.USER_INTERFACE = _ui def test_download_and_expand_tgz(self): """Validate the download_and_expand_tgz() context_manager.""" try: with self.helper.download_and_expand_tgz( "https://github.com/glennmatthews/cot/archive/master.tar.gz" ) as directory: self.assertTrue(os.path.exists(directory)) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT", "tests"))) self.assertTrue(os.path.exists( os.path.join(directory, "cot-master", "COT", "tests", "cot_testcase.py"))) except requests.exceptions.ConnectionError: # unable to connect to github - might be an isolated environment self.fail("ConnectionError when trying to download from GitHub") # Temporary directory should be cleaned up when done self.assertFalse(os.path.exists(directory))