Example #1
0
    def remove(self, argv):
        """
        Remove a plugin.

        This method is smart enough to work with only the plugin name if it
        happens to be unique. If there is more than one plugin with the same
        name but in a different bundle it will exit with an error.
        """
        path = argv.path
        name = argv.name
        bundle = argv.bundle

        with self.out() as out:
            config = get_jigconfig(path)

            pm = PluginManager(config)

            plugins = plugins_by_name(pm)

            # Find the bundle if it's not specified
            if name in plugins and not bundle:
                if len(plugins[name]) > 1:
                    # There are more than one plugin by this name
                    raise CommandError(
                        'More than one plugin has the name of '
                        '{0}. Use the list command to see installed '
                        'plugins.'.format(name))

                bundle = plugins[name][0].bundle

            pm.remove(bundle, name)

            set_jigconfig(path, pm.config)

            out.append('Removed plugin {0}'.format(name))
Example #2
0
 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)
Example #3
0
 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)
Example #4
0
    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))
Example #5
0
    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))
Example #6
0
    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))
Example #7
0
    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))
Example #8
0
    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))
Example #9
0
    def test_remove_non_existent_section(self):
        """
        Try to remove a plugin that does not exist.
        """
        pm = PluginManager(self.jigconfig)

        with self.assertRaises(PluginError) as ec:
            pm.remove('bundle', 'name')

        self.assertEqual('This plugin does not exist.', str(ec.exception))
Example #10
0
    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))
Example #11
0
    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))
Example #12
0
    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))
Example #13
0
    def test_remove_non_existent_section(self):
        """
        Try to remove a plugin that does not exist.
        """
        pm = PluginManager(self.jigconfig)

        with self.assertRaises(PluginError) as ec:
            pm.remove('bundle', 'name')

        self.assertEqual('This plugin does not exist.',
            str(ec.exception))
Example #14
0
    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))
Example #15
0
    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))
Example #16
0
    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))
Example #17
0
    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))
Example #18
0
    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))
Example #19
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)
Example #20
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)
Example #21
0
    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)
Example #22
0
    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)
Example #23
0
    def process(self, argv):
        path = argv.path
        plugins_file = argv.pluginsfile

        with self.out() as out:
            try:
                plugin_list = read_plugin_list(plugins_file)
            except IOError as e:
                # Grab the human-readable part of the IOError and raise that
                raise PluginError(e[1])

            for plugin in plugin_list:
                config = get_jigconfig(path)
                pm = PluginManager(config)

                try:
                    added = add_plugin(pm, plugin, path)
                except Exception as e:
                    out.append('From {0}:\n - {1}'.format(plugin, e))
                    continue

                set_jigconfig(path, pm.config)

                out.append('From {0}:'.format(plugin))
                for p in added:
                    out.append(' - Added plugin {0} in bundle {1}'.format(
                        p.name, p.bundle))

            out.extend(USE_RUNNOW)
Example #24
0
    def set(self, argv):
        """
        Change a single setting for an installed plugin.
        """
        path = argv.path
        key = argv.key
        key_parts = key.split('.', 3)
        config_value = argv.value

        with self.out():
            if len(key_parts) != 3:
                # The key is not correct
                raise ConfigKeyInvalid(
                    '{0} is an invalid config key.'.format(key))

            bundle, plugin, config_key = key_parts

            config = get_jigconfig(path)

            pm = PluginManager(config)

            if not self._has_plugin(pm, bundle, plugin):
                raise CommandError(
                    'Could not locate plugin {0}.'.format(plugin))

            section_name = 'plugin:{0}:{1}'.format(bundle, plugin)

            # Finally change the setting
            pm.config.set(section_name, config_key, config_value)

            set_jigconfig(path, pm.config)
Example #25
0
    def list(self, argv):
        """
        List the current settings for all plugins.
        """
        path = argv.path

        with self.out() as out:
            config = get_jigconfig(path)

            pm = PluginManager(config)

            if not pm.plugins:
                out.append(u'No plugins installed.')
                out.extend(NO_PLUGINS_INSTALLED)
                return

            for meta in self._settings(pm):
                out.append(
                    u'{bundle}.{plugin}.{config_key}={config_value}'.format(
                        bundle=meta.plugin.bundle,
                        plugin=meta.plugin.name,
                        config_key=meta.key,
                        config_value=meta.value))

            if not out:
                out.append(u'Installed plugins have no settings.')

            out.extend(CHANGE_PLUGIN_SETTINGS)
Example #26
0
    def list(self, argv):
        """
        List the installed plugins.
        """
        path = argv.path

        with self.out() as out:
            config = get_jigconfig(path)

            pm = PluginManager(config)

            bundles = plugins_by_bundle(pm)

            if not bundles:
                out.append(u'No plugins installed.')
                out.extend(NO_PLUGINS_INSTALLED)
                return

            out.append(u'Installed plugins\n')

            out.append(u'{h1:<25} {h2}'.format(
                h1=u'Plugin name', h2=u'Bundle name'))

            sort_bundles = sorted(bundles.items(), key=lambda b: b[0])

            for name, plugins in sort_bundles:
                sort_plugins = sorted(plugins, key=lambda p: p.name)

                for plugin in sort_plugins:
                    out.append(u'{plugin:.<25} {name}'.format(
                        name=name, plugin=plugin.name))

            out.extend(USE_RUNNOW)
Example #27
0
    def test_has_empty_plugin_list(self):
        """
        A freshly initialized repo has no plugins.
        """
        pm = PluginManager(self.jigconfig)

        self.assertEqual([], pm.plugins)
Example #28
0
    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)
Example #29
0
    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)
Example #30
0
    def test_could_not_parse(self):
        """
        Tests a bad plugin config from the main config.
        """
        self._add_plugin(self.jigconfig, 'plugin02')

        with self.assertRaises(PluginError) as ec:
            PluginManager(self.jigconfig)

        self.assertIn('Could not parse config file', str(ec.exception))
Example #31
0
    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)
Example #32
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])
Example #33
0
    def _set(self, gitrepodir, bundle_name, plugin_name, key, value):
        """
        Change a setting for a plugin and save the Jig config.
        """
        config = get_jigconfig(self.gitrepodir)
        pm = PluginManager(config)

        pm.config.set('plugin:{0}:{1}'.format(bundle_name, plugin_name), key,
                      value)

        set_jigconfig(self.gitrepodir, pm.config)
Example #34
0
    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)
Example #35
0
    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)
Example #36
0
    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)
Example #37
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])
Example #38
0
    def test_ignores_non_plugin_sections(self):
        """
        Other sections of the config do not get seen as a plugin.
        """
        self._add_plugin(self.jigconfig, 'plugin01')

        self.jigconfig.add_section('nonplugin')
        self.jigconfig.set('nonplugin', 'def1', '1')

        pm = PluginManager(self.jigconfig)

        self.assertEqual(1, len(pm.plugins))
Example #39
0
    def test_has_one_plugin(self):
        """
        We can add one plugin to the main config file.
        """
        self._add_plugin(self.jigconfig, 'plugin01')

        pm = PluginManager(self.jigconfig)

        self.assertEqual(1, len(pm.plugins))

        plugin = pm.plugins[0]

        self.assertEqual('test01', plugin.bundle)
        self.assertEqual('plugin01', plugin.name)
Example #40
0
    def _clear_settings(self, gitrepodir):
        """
        Remove all plugin specific settings.
        """
        config = get_jigconfig(self.gitrepodir)
        pm = PluginManager(config)

        for section in pm.config.sections():
            if not section.startswith('plugin'):
                continue
            for option, value in pm.config.items(section):
                if option == 'path':
                    continue
                pm.config.remove_option(section, option)

        set_jigconfig(self.gitrepodir, pm.config)
Example #41
0
    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))
Example #42
0
    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)
Example #43
0
    def add(self, argv):
        """
        Add a plugin.
        """
        path = argv.path
        plugin = argv.plugin

        with self.out() as out:
            config = get_jigconfig(path)

            pm = PluginManager(config)

            added = add_plugin(pm, plugin, path)

            set_jigconfig(path, pm.config)

            for p in added:
                out.append(
                    'Added plugin {0} in bundle {1} to the '
                    'repository.'.format(p.name, p.bundle))

            out.extend(USE_RUNNOW)
Example #44
0
    def about(self, argv):
        """
        Provide about/help on each plugin setting.
        """
        path = argv.path

        def wrap(payload):
            indent = '   '
            tw = TextWrapper(width=70,
                             initial_indent=indent,
                             subsequent_indent=indent)
            return u'\n'.join(tw.wrap(payload))

        with self.out() as out:
            config = get_jigconfig(path)

            pm = PluginManager(config)

            if not pm.plugins:
                out.append(u'No plugins installed.')
                out.extend(NO_PLUGINS_INSTALLED)
                return

            for meta in self._settings(pm):
                out.append(u'{bundle}.{plugin}.{config_key}'.format(
                    bundle=meta.plugin.bundle,
                    plugin=meta.plugin.name,
                    config_key=meta.key))
                out.append(u'(default: {0})'.format(meta.default))

                if meta.about:
                    out.append(wrap(meta.about.strip()))

                out.append(u'')

            if not out:
                out.append(u'Installed plugins have no settings.')
Example #45
0
    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
Example #46
0
    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