Esempio n. 1
0
class MyServer(Server):

    program_name = "Mnemosyne"
    program_version = "test"
    capabilities = "TODO"

    def __init__(self):
        shutil.rmtree(os.path.abspath("dot_sync_server"), ignore_errors=True)
        self.mnemosyne = Mnemosyne(upload_science_logs=False,
                                   interested_in_old_reps=True)
        self.mnemosyne.components.insert(
            0,
            ("mnemosyne.libmnemosyne.gui_translator", "GetTextGuiTranslator"))
        self.mnemosyne.components.append(("test_sync", "Widget"))
        self.mnemosyne.gui_for_component["ScheduledForgottenNew"] = \
            [("mnemosyne_test", "TestReviewWidget")]
        self.mnemosyne.initialise(os.path.abspath("dot_sync_server"),
                                  automatic_upgrades=False)
        self.mnemosyne.config().change_user_id("user_id")
        self.mnemosyne.review_controller().reset()
        self.supports_binary_transfer = lambda x: False
        # Add 20 cards to database.
        card_type = self.mnemosyne.card_type_with_id("1")
        for i in range(20):
            fact_data = {"f": "question %d" % (i, ), "b": "answer"}
            self.mnemosyne.controller().create_new_cards(fact_data,
                                                         card_type,
                                                         grade=-1,
                                                         tag_names=["default"
                                                                    ])[0]
        self.mnemosyne.database().save()
        self.mnemosyne.database().release_connection()

    def authorise(self, login, password):
        return login == "user" and password == "pass"

    def load_database(self, database_name):
        self.mnemosyne.database().load(database_name)
        return self.mnemosyne.database()

    def unload_database(self, database):
        self.mnemosyne.database().release_connection()
        # Dirty way to make sure we restart the server and create a new database
        # (as opposed to keep sending old history back and forth)'
        self.wsgi_server.stop()

    def run(self):
        Server.__init__(self, "client_machine_id", 8186,
                        self.mnemosyne.main_widget())
        self.serve_until_stopped()
Esempio n. 2
0
class MyServer(Server):

    program_name = "Mnemosyne"
    program_version = "test"
    capabilities = "TODO"

    def __init__(self):
        shutil.rmtree(os.path.abspath("dot_sync_server"), ignore_errors=True)
        self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True)
        self.mnemosyne.components.insert(0, ("mnemosyne.libmnemosyne.translator",
            "GetTextTranslator"))
        self.mnemosyne.components.append(("test_sync", "Widget"))
        self.mnemosyne.components.append(("mnemosyne_test", "TestReviewWidget"))
        self.mnemosyne.initialise(os.path.abspath("dot_sync_server"), automatic_upgrades=False)
        self.mnemosyne.config().change_user_id("user_id")
        self.mnemosyne.review_controller().reset()
        self.supports_binary_transfer = lambda x : False
        # Add 20 cards to database.
        card_type = self.mnemosyne.card_type_with_id("1")
        for i in range (20):
            fact_data = {"f": "question %d" % (i,),
                         "b": "answer"}
            self.mnemosyne.controller().create_new_cards(fact_data, card_type,
                grade=-1, tag_names=["default"])[0]
        self.mnemosyne.database().save()
        self.mnemosyne.database().release_connection()
     
    def authorise(self, login, password):
        return login == "user" and password == "pass"
    
    def load_database(self, database_name):
        self.mnemosyne.database().load(database_name)
        return self.mnemosyne.database()

    def unload_database(self, database):
        self.mnemosyne.database().release_connection()
        # Dirty way to make sure we restart the server and create a new database
        # (as opposed to keep sending old history back and forth)'
        self.wsgi_server.stop()
        
    def run(self):
        Server.__init__(self, "client_machine_id", 8186,
                        self.mnemosyne.main_widget())
        self.serve_until_stopped()
Esempio n. 3
0
class TestPlugin(MnemosyneTest):
    def setup(self):
        self.initialise_data_dir()
        self.mnemosyne = Mnemosyne(upload_science_logs=False,
                                   interested_in_old_reps=True,
                                   asynchronous_database=True)
        self.mnemosyne.components.insert(
            0,
            ("mnemosyne.libmnemosyne.gui_translators.gettext_gui_translator",
             "GetTextGuiTranslator"))
        self.mnemosyne.components.append(\
            ("test_plugin", "Widget"))
        self.mnemosyne.gui_for_component["ScheduledForgottenNew"] = \
            [("mnemosyne_test", "TestReviewWidget")]
        self.mnemosyne.initialise(os.path.abspath("dot_test"),
                                  automatic_upgrades=False)
        self.review_controller().reset()

    @raises(AssertionError)
    def test_1(self):
        from mnemosyne.libmnemosyne.plugin import Plugin
        p = Plugin(self.mnemosyne.component_manager)

    def test_2(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]
            supported_API_level = 3

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("666")
        self.controller().create_new_cards(fact_data,
                                           card_type,
                                           grade=-1,
                                           tag_names=["default"])
        p.deactivate()  # Pops up an information box that this is not possible.
        global last_error
        assert last_error.startswith("Cannot deactivate")
        last_error = ""

    def test_deactivate_clone(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]
            supported_API_level = 3

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        card_type = self.mnemosyne.card_type_with_id("666")

        self.mnemosyne.controller().clone_card_type(card_type, "new_name")

        p.deactivate()  # Pops up an information box that this is not possible.
        global last_error
        assert last_error.startswith("Cannot deactivate")
        last_error = ""

    def test_3(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]
            supported_API_level = 3

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("666")
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["default"])[0]
        fact = card.fact
        self.controller().delete_facts_and_their_cards([fact])

        p.deactivate()  # Should work without problems.

    def test_4(self):

        from mnemosyne.libmnemosyne.plugin import Plugin
        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.pyqt_ui.card_type_wdgt_generic import GenericCardTypeWdgt

        class RedGenericCardTypeWdgt(GenericCardTypeWdgt):

            used_for = FrontToBack

            def __init__(self, parent, component_manager):
                GenericCardTypeWdgt.__init__(self, component_manager, parent,
                                             FrontToBack())

        class RedPlugin(Plugin):
            name = "Red"
            description = "Red widget for front-to-back cards"
            components = [RedGenericCardTypeWdgt]
            supported_API_level = 3

        p = RedPlugin(self.mnemosyne.component_manager)
        p.activate()
        assert self.mnemosyne.component_manager.current\
                    ("generic_card_type_widget", used_for=FrontToBack) != None
        p.deactivate()
        assert self.mnemosyne.component_manager.current\
                    ("generic_card_type_widget", used_for=FrontToBack) == None

    def test_5(self):
        for plugin in self.plugins():
            component = plugin.components[0]
            if component.component_type == "card_type" and component.id == "4":
                plugin.activate()
                plugin.activate()

    @raises(NotImplementedError)
    def test_6(self):
        from mnemosyne.libmnemosyne.hook import Hook
        Hook(self.mnemosyne.component_manager).run()

    def test_install_plugin(self):
        global filename
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "hide_toolbar.plugin")
        self.controller().install_plugin()
        assert os.path.exists(
            os.path.join(os.getcwd(), "dot_test", "plugins", "plugin_data"))
        assert len(self.plugins()) == 4
        # Try to install twice.
        self.controller().install_plugin()
        assert len(self.plugins()) == 4
        # Uninstall.
        for plugin in self.plugins():
            if plugin.__class__.__name__ == "HideToolbarPlugin":
                self.controller().delete_plugin(plugin)
                break
        assert not os.path.exists(
            os.path.join(os.getcwd(), "dot_test", "plugins", "plugin_data"))
        assert not os.path.exists(
            os.path.join(os.getcwd(), "dot_test", "plugins",
                         "HideToolbarPlugin.manifest"))
        assert os.path.exists(os.path.join(os.getcwd(), "dot_test", "plugins"))
        assert len(self.plugins()) == 3
        # Try to reinstall immediately.
        self.controller().install_plugin()
        assert len(self.plugins()) == 4

    def test_install_plugin_cancel(self):
        global filename
        filename = ""
        self.controller().install_plugin()

    def test_install_plugin_missing(self):
        global filename
        global last_error
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "hide_toolbar_missing.plugin")
        self.controller().install_plugin()
        assert last_error.startswith("No plugin found")
        last_error = None

    def test_install_plugin_corrupt(self):
        global filename
        global last_error
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "hide_toolbar_corrupt.plugin")
        self.controller().install_plugin()
        assert last_error.startswith("Error when running")
        last_error = None
class MyClient(Client):
    
    program_name = "Mnemosyne"
    program_version = "test"
    capabilities = "TODO"
    
    def __init__(self):
        shutil.rmtree(os.path.abspath("dot_sync_client"), ignore_errors=True)
        self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True)
        self.mnemosyne.components = [
            ("mnemosyne.libmnemosyne.translator",
             "NoTranslation"),    
            ("mnemosyne.libmnemosyne.databases.SQLite",
             "SQLite"), 
            ("mnemosyne.libmnemosyne.configuration",
             "Configuration"), 
            ("mnemosyne.libmnemosyne.loggers.database_logger",
             "DatabaseLogger"),          
            ("mnemosyne.libmnemosyne.schedulers.SM2_mnemosyne",
             "SM2Mnemosyne"),
            ("mnemosyne.libmnemosyne.stopwatch",
             "Stopwatch"), 
            ("mnemosyne.libmnemosyne.card_types.front_to_back",
             "FrontToBack"),
            ("mnemosyne.libmnemosyne.card_types.both_ways",
             "BothWays"),
            ("mnemosyne.libmnemosyne.card_types.vocabulary",
             "Vocabulary"),
            ("mnemosyne.libmnemosyne.renderers.html_css",
             "HtmlCss"),
            ("mnemosyne.libmnemosyne.filters.escape_to_html",
             "EscapeToHtml"),
            ("mnemosyne.libmnemosyne.filters.expand_paths",
             "ExpandPaths"),
            ("mnemosyne.libmnemosyne.filters.latex",
             "Latex"),
            ("mnemosyne.libmnemosyne.render_chains.default_render_chain",
             "DefaultRenderChain"),
            ("mnemosyne.libmnemosyne.render_chains.plain_text_chain",
             "PlainTextChain"), 
            ("mnemosyne.libmnemosyne.controllers.default_controller",
             "DefaultController"),
            ("mnemosyne.libmnemosyne.review_controllers.SM2_controller",
             "SM2Controller"),
            ("mnemosyne.libmnemosyne.card_types.map",
             "MapPlugin"),
            ("mnemosyne.libmnemosyne.card_types.cloze",
             "ClozePlugin"),
            ("mnemosyne.libmnemosyne.criteria.default_criterion",
             "DefaultCriterion"),
            ("mnemosyne.libmnemosyne.databases.SQLite_criterion_applier",
             "DefaultCriterionApplier"), 
            ("mnemosyne.libmnemosyne.plugins.cramming_plugin",
             "CrammingPlugin") ]
        self.mnemosyne.components.append(("benchmark_sync_client", "Widget"))
        self.mnemosyne.components.append(("benchmark_sync_client", "MyReviewWidget"))
        self.mnemosyne.initialise(os.path.abspath(os.path.join(os.getcwd(),
                                  "dot_sync_client")), automatic_upgrades=False)
        self.mnemosyne.config().change_user_id("user_id")
        self.check_for_edited_local_media_files = False
        self.do_backup = False     
        self.mnemosyne.review_controller().reset()
        # Do 200 reviews.
        card_type = self.mnemosyne.card_type_with_id("1")
        fact_data = {"f": "question",
                     "b": "answer"}
        card = self.mnemosyne.controller().create_new_cards(fact_data, card_type,
                grade=-1, tag_names=["default"])[0]
        self.mnemosyne.database().save()
        self.mnemosyne.review_controller().show_new_question()
        for i in range(200):
            self.mnemosyne.review_controller().show_answer()
            self.mnemosyne.review_controller().grade_answer(0)
        Client.__init__(self, "client_machine_id", self.mnemosyne.database(),
                        self.mnemosyne.main_widget())
        
    def do_sync(self):
        #self.BUFFER_SIZE = 10*8192
        #self.behind_proxy = True
        self.sync("localhost", 8186, "user", "pass")
        self.mnemosyne.database().save()
Esempio n. 5
0
class MyClient(Client):
    
    program_name = "Mnemosyne"
    program_version = "test"
    capabilities = "TODO"
    
    def __init__(self):
        shutil.rmtree(os.path.abspath("dot_sync_client"), ignore_errors=True)
        self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True)
        self.mnemosyne.components = [
            ("mnemosyne.libmnemosyne.translator",
             "NoTranslation"),    
            ("mnemosyne.libmnemosyne.databases.SQLite",
             "SQLite"), 
            ("mnemosyne.libmnemosyne.configuration",
             "Configuration"), 
            ("mnemosyne.libmnemosyne.loggers.database_logger",
             "DatabaseLogger"),          
            ("mnemosyne.libmnemosyne.schedulers.SM2_mnemosyne",
             "SM2Mnemosyne"),
            ("mnemosyne.libmnemosyne.stopwatch",
             "Stopwatch"), 
            ("mnemosyne.libmnemosyne.card_types.front_to_back",
             "FrontToBack"),
            ("mnemosyne.libmnemosyne.card_types.both_ways",
             "BothWays"),
            ("mnemosyne.libmnemosyne.card_types.vocabulary",
             "Vocabulary"),
            ("mnemosyne.libmnemosyne.renderers.html_css",
             "HtmlCss"),
            ("mnemosyne.libmnemosyne.filters.escape_to_html",
             "EscapeToHtml"),
            ("mnemosyne.libmnemosyne.filters.expand_paths",
             "ExpandPaths"),
            ("mnemosyne.libmnemosyne.filters.latex",
             "Latex"),
            ("mnemosyne.libmnemosyne.render_chains.default_render_chain",
             "DefaultRenderChain"),
            ("mnemosyne.libmnemosyne.render_chains.plain_text_chain",
             "PlainTextChain"), 
            ("mnemosyne.libmnemosyne.controllers.default_controller",
             "DefaultController"),
            ("mnemosyne.libmnemosyne.review_controllers.SM2_controller",
             "SM2Controller"),
            ("mnemosyne.libmnemosyne.card_types.map",
             "MapPlugin"),
            ("mnemosyne.libmnemosyne.card_types.cloze",
             "ClozePlugin"),
            ("mnemosyne.libmnemosyne.criteria.default_criterion",
             "DefaultCriterion"),
            ("mnemosyne.libmnemosyne.databases.SQLite_criterion_applier",
             "DefaultCriterionApplier"), 
            ("mnemosyne.libmnemosyne.plugins.cramming_plugin",
             "CrammingPlugin") ]
        self.mnemosyne.components.append(("benchmark_sync_client", "Widget"))
        self.mnemosyne.components.append(("benchmark_sync_client", "MyReviewWidget"))
        self.mnemosyne.initialise(os.path.abspath(os.path.join(os.getcwdu(),
                                  "dot_sync_client")), automatic_upgrades=False)
        self.mnemosyne.config().change_user_id("user_id")
        self.check_for_edited_local_media_files = False
        self.do_backup = False     
        self.mnemosyne.review_controller().reset()
        # Do 200 reviews.
        card_type = self.mnemosyne.card_type_with_id("1")
        fact_data = {"f": "question",
                     "b": "answer"}
        card = self.mnemosyne.controller().create_new_cards(fact_data, card_type,
                grade=-1, tag_names=["default"])[0]
        self.mnemosyne.database().save()
        self.mnemosyne.review_controller().show_new_question()
        for i in range(200):
            self.mnemosyne.review_controller().show_answer()
            self.mnemosyne.review_controller().grade_answer(0)
        Client.__init__(self, "client_machine_id", self.mnemosyne.database(),
                        self.mnemosyne.main_widget())
        
    def do_sync(self):
        #self.BUFFER_SIZE = 10*8192
        #self.behind_proxy = True
        self.sync("localhost", 8186, "user", "pass")
        self.mnemosyne.database().save()
Esempio n. 6
0
class TestPlugin(MnemosyneTest):

    def setup(self):
        self.initialise_data_dir()
        self.mnemosyne = Mnemosyne(upload_science_logs=False, interested_in_old_reps=True,
            asynchronous_database=True)
        self.mnemosyne.components.insert(0,
           ("mnemosyne.libmnemosyne.translators.gettext_translator", "GetTextTranslator"))
        self.mnemosyne.components.append(\
            ("test_plugin", "Widget"))
        self.mnemosyne.components.append(\
            ("mnemosyne_test", "TestReviewWidget"))
        self.mnemosyne.initialise(os.path.abspath("dot_test"), automatic_upgrades=False)
        self.review_controller().reset()

    @raises(AssertionError)
    def test_1(self):
        from mnemosyne.libmnemosyne.plugin import Plugin
        p = Plugin(self.mnemosyne.component_manager)

    def test_2(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        fact_data = {"f": "question",
                     "b": "answer"}
        card_type = self.card_type_with_id("666")
        self.controller().create_new_cards(fact_data, card_type,
                                              grade=-1, tag_names=["default"])
        p.deactivate() # Pops up an information box that this is not possible.
        global last_error
        assert last_error.startswith("Cannot deactivate")
        last_error = ""

    def test_deactivate_clone(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        card_type = self.mnemosyne.card_type_with_id("666")

        self.mnemosyne.controller().clone_card_type(card_type, "new_name")

        p.deactivate() # Pops up an information box that this is not possible.
        global last_error
        assert last_error.startswith("Cannot deactivate")
        last_error = ""



    def test_3(self):

        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.libmnemosyne.plugin import Plugin

        class MyCardType(FrontToBack):
            id = "666"

        class MyPlugin(Plugin):
            name = "myplugin"
            description = "MyPlugin"
            components = [MyCardType]

        p = MyPlugin(self.mnemosyne.component_manager)

        old_length = len(self.card_types())
        p.activate()
        assert len(self.card_types()) == old_length + 1
        p.deactivate()
        assert len(self.card_types()) == old_length

        p.activate()

        fact_data = {"f": "question",
                     "b": "answer"}
        card_type = self.card_type_with_id("666")
        card = self.controller().create_new_cards(fact_data, card_type,
                                          grade=-1, tag_names=["default"])[0]
        fact = card.fact
        self.controller().delete_facts_and_their_cards([fact])

        p.deactivate() # Should work without problems.

    def test_4(self):

        from mnemosyne.libmnemosyne.plugin import Plugin
        from mnemosyne.libmnemosyne.card_types.front_to_back import FrontToBack
        from mnemosyne.pyqt_ui.card_type_wdgt_generic import GenericCardTypeWdgt

        class RedGenericCardTypeWdgt(GenericCardTypeWdgt):

            used_for = FrontToBack

            def __init__(self, parent, component_manager):
                GenericCardTypeWdgt.__init__(self, component_manager,
                                             parent, FrontToBack())

        class RedPlugin(Plugin):
            name = "Red"
            description = "Red widget for front-to-back cards"
            components = [RedGenericCardTypeWdgt]

        p = RedPlugin(self.mnemosyne.component_manager)
        p.activate()
        assert self.mnemosyne.component_manager.current\
                    ("generic_card_type_widget", used_for=FrontToBack) != None
        p.deactivate()
        assert self.mnemosyne.component_manager.current\
                    ("generic_card_type_widget", used_for=FrontToBack) == None

    def test_5(self):
        for plugin in self.plugins():
            component = plugin.components[0]
            if component.component_type == "card_type" and component.id == "4":
                plugin.activate()
                plugin.activate()

    @raises(NotImplementedError)
    def test_6(self):
        from mnemosyne.libmnemosyne.hook import Hook
        Hook(self.mnemosyne.component_manager).run()

    def test_install_plugin(self):
        global filename
        filename = os.path.join(os.getcwd(), "tests", "files", "hide_toolbar.plugin")
        self.controller().install_plugin()
        assert os.path.exists(os.path.join(os.getcwd(), "dot_test", "plugins", "plugin_data"))
        assert len(self.plugins()) == 5
        # Try to install twice.
        self.controller().install_plugin()
        assert len(self.plugins()) == 5
        # Uninstall.
        for plugin in self.plugins():
            if plugin.__class__.__name__ == "HideToolbarPlugin":
                self.controller().delete_plugin(plugin)
                break
        assert not os.path.exists(os.path.join(os.getcwd(), "dot_test", "plugins", "plugin_data"))
        assert not os.path.exists(os.path.join(os.getcwd(), "dot_test", "plugins", "HideToolbarPlugin.manifest"))
        assert os.path.exists(os.path.join(os.getcwd(), "dot_test", "plugins"))
        assert len(self.plugins()) == 4
        # Try to reinstall immediately.
        self.controller().install_plugin()
        assert len(self.plugins()) == 5

    def test_install_plugin_cancel(self):
        global filename
        filename = ""
        self.controller().install_plugin()

    def test_install_plugin_missing(self):
        global filename
        global last_error
        filename = os.path.join(os.getcwd(), "tests", "files", "hide_toolbar_missing.plugin")
        self.controller().install_plugin()
        assert last_error.startswith("No plugin found")
        last_error = None

    def test_install_plugin_corrupt(self):
        global filename
        global last_error
        filename = os.path.join(os.getcwd(), "tests", "files", "hide_toolbar_corrupt.plugin")
        self.controller().install_plugin()
        assert last_error.startswith("Error when running")
        last_error = None