def _add_plugin(self, plugin_dir): """ Adds a plugin to the jig initialized Git repository. """ config = get_jigconfig(self.gitrepodir) pm = PluginManager(config) pm.add(plugin_dir) set_jigconfig(self.gitrepodir, pm.config)
def test_add_plugin_no_settings_section(self): """ Adds a plugin if it has no settings. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin05')) self.assertEqual(1, len(pm.plugins))
def test_add_plugin_from_directory_of_plugins(self): """ Adds all the plugins in a directory of plugins. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin07')) self.assertEqual(2, len(pm.plugins))
def test_missing_bundle_and_name(self): """ Will not add a plugin if it is missing a bundle or name. """ pm = PluginManager(self.jigconfig) with self.assertRaises(PluginError) as ec: pm.add(join(self.fixturesdir, 'plugin06')) self.assertIn('Could not find the bundle or name', str(ec.exception))
def test_contains_parsing_errors(self): """ Adding a bad plugin catches parsing errors. """ pm = PluginManager(self.jigconfig) with self.assertRaises(PluginError) as ec: pm.add(join(self.fixturesdir, 'plugin02')) self.assertIn('File contains parsing errors', str(ec.exception))
def test_add_plugin_no_config_file(self): """ Will handle a plugin that has no config file. """ pm = PluginManager(self.jigconfig) with self.assertRaises(PluginError) as ec: pm.add(join(self.fixturesdir, 'plugin03')) self.assertIn('The plugin file', str(ec.exception)) self.assertIn('is missing', str(ec.exception))
def test_cannot_add_plugin_twice(self): """ After a plugin has been added, it can't be added again. """ pm = PluginManager(self.jigconfig) with self.assertRaises(PluginError) as ec: pm.add(join(self.fixturesdir, 'plugin01')) # And the second time pm.add(join(self.fixturesdir, 'plugin01')) self.assertEqual('The plugin is already installed.', str(ec.exception))
def test_add_plugin_no_plugin_section(self): """ Will not add a plugin with missing plugin section. """ pm = PluginManager(self.jigconfig) with self.assertRaises(PluginError) as ec: pm.add(join(self.fixturesdir, 'plugin04')) self.assertEqual( 'The plugin config does not contain a [plugin] section.', str(ec.exception))
def test_new_plugin_compat_plugin_manager(self): """ New plugins are compatible with the :py:class:`PluginManager` """ plugin_dir = create_plugin( self.plugindir, template='python', bundle='test', name='plugin') pm = PluginManager(self.jigconfig) pm.add(plugin_dir) self.assertEqual(1, len(pm.plugins)) self.assertEqual('plugin', pm.plugins[0].name)
def test_remove_plugin(self): """ Remove a plugin. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) self.assertTrue(pm.config.has_section('plugin:test01:plugin01')) pm.remove('test01', 'plugin01') self.assertFalse(pm.config.has_section('plugin:test01:plugin01')) self.assertEqual([], pm.plugins)
def test_unicode_in_stream(self): """ UTF-8 encoded streams get converted back to unicode. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) gdi = self.git_diff_index(self.testrepo, self.testdiffs[0]) with patch.object(Popen, 'communicate'): # Send it encoded unicode to see if it will convert it back Popen.communicate.return_value = (u'å∫ç'.encode('utf-8'), '') retcode, stdout, stderr = pm.plugins[0].pre_commit(gdi) self.assertEqual(u'å∫ç', stdout) self.assertEqual(u'', stderr)
def test_oserror(self): """ If a generic OSError is received, don't blow up either. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) gdi = self.git_diff_index(self.testrepo, self.testdiffs[0]) with patch.object(Popen, 'communicate'): ose = OSError('Gazoonkle was discombobulated') ose.errno = 1 Popen.communicate.side_effect = ose retcode, stdout, stderr = pm.plugins[0].pre_commit(gdi) self.assertEqual(1, retcode) self.assertEqual('', stdout) self.assertEqual('Gazoonkle was discombobulated', stderr)
def test_sigpipe_error(self): """ If a SIGPIPE is received, handle it without blowing up. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) gdi = self.git_diff_index(self.testrepo, self.testdiffs[0]) with patch.object(Popen, 'communicate'): ose = OSError('SIGPIPE') ose.errno = 32 Popen.communicate.side_effect = ose retcode, stdout, stderr = pm.plugins[0].pre_commit(gdi) self.assertEqual(1, retcode) self.assertEqual('', stdout) self.assertEqual('Error: received SIGPIPE from the command', stderr)
def test_new_file_pre_commit(self): """ Test a new file pre-commit. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) gdi = self.git_diff_index(self.testrepo, self.testdiffs[0]) retcode, stdout, stderr = pm.plugins[0].pre_commit(gdi) data = json.loads(stdout) # Everything should have gone splendidly self.assertEqual(0, retcode) # Should contain our file that was added self.assertIn('argument.txt', data) # And our first line should be a warning self.assertEqual([1, u'warn', u'The cast: is +'], data['argument.txt'][0])
def test_new_file_pre_commit(self): """ Test a new file pre-commit. """ pm = PluginManager(self.jigconfig) pm.add(join(self.fixturesdir, 'plugin01')) gdi = self.git_diff_index(self.testrepo, self.testdiffs[0]) retcode, stdout, stderr = pm.plugins[0].pre_commit(gdi) data = json.loads(stdout) # Everything should have gone splendidly self.assertEqual(0, retcode) # Should contain our file that was added self.assertIn('argument.txt', data) # And our first line should be a warning self.assertEqual( [1, u'warn', u'The cast: is +'], data['argument.txt'][0])
def test_add_plugin(self): """ Test the add method on the plugin manager. """ # Config is empty pm = PluginManager(self.jigconfig) plugin = pm.add(join(self.fixturesdir, 'plugin01'))[0] self.assertEqual(1, len(pm.plugins)) self.assertTrue(pm.config.has_section('plugin:test01:plugin01')) self.assertEqual('plugin01', plugin.name) self.assertEqual('test01', plugin.bundle)
def run(self, test_range=None): """ Run the tests for this plugin. Returns a list of :py:class:`Result` objects which represent the results from the test run. :param list test_range: None or the parsed range from :function:`parse_range` """ # Use an empty config, we are not going to save this to disk pm = PluginManager(SafeConfigParser()) # Add the plugin we are testing pm.add(self.plugin_dir) # The instance of our plugin we will run the pre_commit test on plugin = pm.plugins[0] # Capture the default plugin config for resets while testing default_settings = plugin.config results = [] for exp in self.expectations: # Make sure that the range is off by 1 assert exp.range[1] == exp.range[0] + 1 # Is this expectation in the specified test range? if test_range and (exp.range not in test_range): # Skip this one, it's not one of the tests we should be running continue # Update the plugin config (settings) if available if exp.settings: plugin.config = exp.settings else: plugin.config = default_settings # View to help us create the output view = ConsoleView(collect_output=True, exit_on_exception=False) # Get a GitDiffIndex object from gdi = InstrumentedGitDiffIndex( self.timeline.repo.working_dir, self.timeline.diffs()[exp.range[0] - 1]) # What is the numbered test directory reprsenting our commit? wd = abspath(join( self.plugin_dir, PLUGIN_TESTS_DIRECTORY, '{0:02d}'.format(exp.range[1]))) with cwd_bounce(wd): # Patch up the filename to be within our numbered directory # instead of the Git repository gdi.replace_path = (self.timeline.repo.working_dir, wd) # Gather up the input to the plugin for logging stdin = json.dumps({ 'config': plugin.config, 'files': gdi}, indent=2, cls=PluginDataJSONEncoder) # Now run the actual pre_commit hook for this plugin res = plugin.pre_commit(gdi) # Break apart into its pieces retcode, stdout, stderr = res # pragma: no branch try: # Is it JSON data? data = json.loads(stdout) except ValueError: # Not JSON data = stdout if retcode == 0: # Format the results according to what you normally see in the # console. view.print_results({plugin: (retcode, data, stderr)}) else: results.append(FailureResult( exp, 'Exit code: {0}\n\nStd out:\n{1}\n\nStd err:\n{2}'.format( retcode, stdout or '(none)', stderr or '(none)'), plugin)) continue # Now remove the color character sequences to make things a little # easier to read, copy, and paste. actual = strip_paint( view._collect['stdout'].getvalue() or view._collect['stderr'].getvalue()) # Also remove the summary and count at the end, these are not # really all that useful to test and just end up making the # expect.rst files overly verbose actual = RESULTS_SUMMARY_SIGNATURE_RE.sub('', actual) actual = RESULTS_SUMMARY_COUNT_RE.sub('', actual) resargs = (exp, actual, plugin, stdin, stdout) if actual.strip() != exp.output.strip(): results.append(FailureResult(*resargs)) else: results.append(SuccessResult(*resargs)) return results
def run(self, test_range=None): """ Run the tests for this plugin. Returns a list of :py:class:`Result` objects which represent the results from the test run. :param list test_range: None or the parsed range from :function:`parse_range` """ # Use an empty config, we are not going to save this to disk pm = PluginManager(SafeConfigParser()) # Add the plugin we are testing pm.add(self.plugin_dir) # The instance of our plugin we will run the pre_commit test on plugin = pm.plugins[0] # Capture the default plugin config for resets while testing default_settings = plugin.config results = [] for exp in self.expectations: # Make sure that the range is off by 1 assert exp.range[1] == exp.range[0] + 1 # Is this expectation in the specified test range? if test_range and (exp.range not in test_range): # Skip this one, it's not one of the tests we should be running continue # Update the plugin config (settings) if available if exp.settings: plugin.config = exp.settings else: plugin.config = default_settings # View to help us create the output view = ConsoleView(collect_output=True, exit_on_exception=False) # Get a GitDiffIndex object from gdi = InstrumentedGitDiffIndex( self.timeline.repo.working_dir, self.timeline.diffs()[exp.range[0] - 1]) # What is the numbered test directory reprsenting our commit? wd = abspath( join(self.plugin_dir, PLUGIN_TESTS_DIRECTORY, '{0:02d}'.format(exp.range[1]))) with cwd_bounce(wd): # Patch up the filename to be within our numbered directory # instead of the Git repository gdi.replace_path = (self.timeline.repo.working_dir, wd) # Gather up the input to the plugin for logging stdin = json.dumps({ 'config': plugin.config, 'files': gdi }, indent=2, cls=PluginDataJSONEncoder) # Now run the actual pre_commit hook for this plugin res = plugin.pre_commit(gdi) # Break apart into its pieces retcode, stdout, stderr = res # pragma: no branch try: # Is it JSON data? data = json.loads(stdout) except ValueError: # Not JSON data = stdout if retcode == 0: # Format the results according to what you normally see in the # console. view.print_results({plugin: (retcode, data, stderr)}) else: results.append( FailureResult( exp, 'Exit code: {0}\n\nStd out:\n{1}\n\nStd err:\n{2}'. format(retcode, stdout or '(none)', stderr or '(none)'), plugin)) continue # Now remove the color character sequences to make things a little # easier to read, copy, and paste. actual = strip_paint(view._collect['stdout'].getvalue() or view._collect['stderr'].getvalue()) # Also remove the summary and count at the end, these are not # really all that useful to test and just end up making the # expect.rst files overly verbose actual = RESULTS_SUMMARY_SIGNATURE_RE.sub('', actual) actual = RESULTS_SUMMARY_COUNT_RE.sub('', actual) resargs = (exp, actual, plugin, stdin, stdout) if actual.strip() != exp.output.strip(): results.append(FailureResult(*resargs)) else: results.append(SuccessResult(*resargs)) return results