Example #1
0
    def __init__(self, name):
        config_script = {
            'VERSION' : { '*' : [('VERSION', CANTO_PROTOCOL_COMPATIBLE)] },
            'CONFIGS' : { '*' : [('CONFIGS', { "CantoCurses" : config.template_config })] },
            'PING' : { '*' : [("PONG", [])]}
        }

        self.config_backend = TestBackend("config", config_script)

        config.init(self.config_backend, CANTO_PROTOCOL_COMPATIBLE)

        self.config_backend.inject("NEWTAGS", [ "maintag:Tag(0)", "maintag:Tag(1)", "maintag:Tag(2)" ])

        tagcore_script = generate_item_script(3, 20, "maintag:Tag(%d)", "Story(%d,%d)",
                { "title" : "%d,%d - title", "link" : "http://example.com/%d/%d",
                    "description" : "Description(%d,%d)", "canto-tags" : "",
                    "canto-state" : "" }
        ) 

        self.tag_backend = TestBackend("tagcore", tagcore_script)

        gui_script = {}

        self.gui_backend = TestBackend("gui", gui_script)

        self.glog = GraphicalLog()
        self.gui = CantoCursesGui(self.gui_backend, self.glog)
        tag_updater.init(self.tag_backend)

        # The standard opening of c-c, the tags can be in any state, but for the
        # purposes of testing, we want to make sure that the tagcores are in a known
        # state or the rest of this stuff will be racy.

        # Fortunately the real-world opening case is tested every time you run c-c =P

        # 9 = 3 tags * 3 responses per ITEMS call (ITEMS, ITEMSDONE, and ATTRIBUTES)

        while len(self.tag_backend.procd) != 9:
            print("len: %s" % len(self.tag_backend.procd))
            time.sleep(0.1)

        if len(alltagcores) != 3:
            raise Exception("Didn't get all tags!")
        if len(alltagcores[0]) != 20:
            raise Exception("Didn't get all items in tagcore[0]")
        if len(alltagcores[1]) != 20:
            raise Exception("Didn't get all items in tagcore[1]")

        self.wait_on_update()

        Test.__init__(self, name)
Example #2
0
    def __init__(self, name):
        config_script = {
            'VERSION' : { '*' : [('VERSION', CANTO_PROTOCOL_COMPATIBLE)] },
            'CONFIGS' : { '*' : [('CONFIGS', { "CantoCurses" : config.template_config })] },
                
        }

        self.config_backend = TestBackend("config", config_script)

        config.init(self.config_backend, CANTO_PROTOCOL_COMPATIBLE)

        self.config_backend.inject("NEWTAGS", [ "maintag:Tag(0)", "maintag:Tag(1)", "maintag:Tag(2)" ])

        tagcore_script = generate_item_script(3, 20, "maintag:Tag(%d)", "Story(%d,%d)",
                { "title" : "%d,%d - title", "link" : "http://example.com/%d/%d",
                    "description" : "Description(%d,%d)", "canto-tags" : "",
                    "canto-state" : "" }
        ) 

        self.tag_backend = TestBackend("tagcore", tagcore_script)

        tag_updater.init(self.tag_backend)

        # The standard opening of c-c, the tags can be in any state, but for the
        # purposes of testing, we want to make sure that the tagcores are in a known
        # state or the rest of this stuff will be racy.

        # Fortunately the real-world opening case is tested every time you run c-c =P

        # 9 = 3 tags * 3 responses per ITEMS call (ITEMS, ITEMSDONE, and ATTRIBUTES)

        while len(self.tag_backend.procd) != 9:
            print("len: %s" % len(self.tag_backend.procd))
            time.sleep(0.1)

        if len(alltagcores) != 3:
            raise Exception("Didn't get all tags!")
        if len(alltagcores[0]) != 20:
            raise Exception("Didn't get all items in tagcore[0]")
        if len(alltagcores[1]) != 20:
            raise Exception("Didn't get all items in tagcore[1]")

        gui_script = {}

        self.gui_backend = TestBackend("gui", gui_script)

        self.gui = CantoCursesGui(self.gui_backend)
        self.wait_on_update()

        Test.__init__(self, name)
    def check(self):
        script = {
            'VERSION' : { '*' : [('VERSION', CANTO_PROTOCOL_COMPATIBLE)] },
            'CONFIGS' : { '*' : [('CONFIGS', { "CantoCurses" : config.template_config })] },
                
        }

        backend = TestBackend("config", script)

        config.init(backend, CANTO_PROTOCOL_COMPATIBLE)

        # We only want to test failure. Success will be tested by the config hook test.

        really_bad_config = eval(repr(config.template_config))
        really_bad_config["browser"]["text"] = "badoption"

        backend.inject("CONFIGS", { "CantoCurses" : really_bad_config })
        
        return config.config == config.template_config
Example #4
0
    def check(self):
        script = {
            'VERSION': {
                '*': [('VERSION', CANTO_PROTOCOL_COMPATIBLE)]
            },
            'CONFIGS': {
                '*': [('CONFIGS', {
                    "CantoCurses": config.template_config
                })]
            },
        }

        backend = TestBackend("config", script)

        config.init(backend, CANTO_PROTOCOL_COMPATIBLE)

        # We only want to test failure. Success will be tested by the config hook test.

        really_bad_config = eval(repr(config.template_config))
        really_bad_config["browser"]["text"] = "badoption"

        backend.inject("CONFIGS", {"CantoCurses": really_bad_config})

        return config.config == config.template_config
    def check(self):
        config_script = {
            'VERSION' : { '*' : [('VERSION', CANTO_PROTOCOL_COMPATIBLE)] },
            'CONFIGS' : { '*' : [('CONFIGS', { "CantoCurses" : config.template_config })] },
                
        }

        config_backend = TestBackend("config", config_script)

        config.init(config_backend, CANTO_PROTOCOL_COMPATIBLE)

        config_backend.inject("NEWTAGS", [ "maintag:Slashdot", "maintag:reddit" ])

        tagcore_script = {}

        tag_backend = TestBackend("tagcore", tagcore_script)

        on_hook("curses_items_removed", self.on_items_removed)
        on_hook("curses_items_added", self.on_items_added)
        on_hook("curses_new_tagcore", self.on_new_tagcore)
        on_hook("curses_del_tagcore", self.on_del_tagcore)
        on_hook("curses_attributes", self.on_attributes)
        on_hook("curses_update_complete", self.on_update_complete)
        on_hook("curses_tag_updated", self.on_tag_updated)

        # 1. Previously existing tags in config should be populated on init

        self.reset_flags()

        tag_updater.init(tag_backend)

        for tag in config.vars["strtags"]:
            for tc in alltagcores:
                if tc.tag == tag:
                    break
            else:
                raise Exception("Couldn't find TC for tag %s" % tag)

        self.compare_flags(NEW_TC)

        self.reset_flags()

        # 2. Getting empty ITEMS responses should cause no events

        tag_backend.inject("ITEMS", { "maintag:Slashdot" : [] })
        tag_backend.inject("ITEMSDONE", {})

        tag_backend.inject("ITEMS", { "maintag:reddit" : [] })
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(0)

        # 3. Getting a non-empty ITEMS response should cause items_added

        tag_backend.inject("ITEMS", { "maintag:Slashdot" : [ "id1", "id2" ] })
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(ITEMS_ADDED)

        # 4. Getting attributes should cause attributes hook

        self.reset_flags()

        id1_content = { "title" : "id1", "canto-state" : [], "canto-tags" : [],
                "link" : "id1-link", "enclosures" : "" }

        id2_content = { "title" : "id2", "canto-state" : [], "canto-tags" : [],
                "link" : "id2-link", "enclosures" : "" }

        all_content = { "id1" : id1_content, "id2" : id2_content }

        tag_backend.inject("ATTRIBUTES", all_content)

        self.compare_flags(ATTRIBUTES)
        self.compare_var("attributes", all_content)

        id1_got = tag_updater.get_attributes("id1")
        if id1_got != id1_content:
            raise Exception("Bad content: wanted %s - got %s" % (id1_content, id1_got))
        id2_got = tag_updater.get_attributes("id2")
        if id2_got != id2_content:
            raise Exception("Bad content: wanted %s - got %s" % (id2_content, id2_got))

        # 5. Removing an item should *NOT* cause its attributes to be forgotten
        # that happens on stories_removed, and should cause ITEMS_REMOVED

        self.reset_flags()

        tag_backend.inject("ITEMS", { "maintag:Slashdot" : [ "id1" ] })
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(ITEMS_REMOVED)

        id2_got = tag_updater.get_attributes("id2")
        if id2_got != id2_content:
            raise Exception("Bad content: wanted %s - got %s" % (id2_content, id2_got))

        # 6. Getting a stories_removed hook should make it forget attributes

        self.reset_flags()
        
        call_hook("curses_stories_removed", [ FakeTag("maintag:Slashdot"), [ FakeStory("id2") ] ])
                
        if "id2" in tag_updater.attributes:
            raise Exception("Expected id2 to be removed, but it isn't!")

        self.compare_flags(0)

        # 7. Getting attributes for non-existent IDs should return empty

        id2_got = tag_updater.get_attributes("id2")
        if id2_got != {}:
            raise Exception("Expected non-existent id to return empty! Got %s" % id2_got)

        self.compare_flags(0)

        # 8. Getting stories_removed for item still in tag should do nothing

        call_hook("curses_stories_removed", [ FakeTag("maintag:Slashdot"), [ FakeStory("id1") ] ])

        if "id1" not in tag_updater.attributes:
            raise Exception("Expected id1 to remain in attributes!")

        self.compare_flags(0)

        # 9. Config adding a tag should create a new tagcore

        config_backend.inject("NEWTAGS", [ "maintag:Test1" ])

        self.compare_flags(NEW_TC)

        # 10. Config removing an empty tag should delete a tagcore

        self.reset_flags()

        config_backend.inject("DELTAGS", [ "maintag:reddit" ])

        self.compare_flags(DEL_TC)
        self.compare_var("del_tc", "maintag:reddit")

        # 11. Config removing an populated tag should delete a tagcore and
        # cause items_removed. NOTE for now tagcores are never deleted, they
        # just exist empty

        self.reset_flags()

        config_backend.inject("DELTAGS", [ "maintag:Slashdot" ])

        self.compare_flags(DEL_TC | ITEMS_REMOVED)
        self.compare_var("del_tc", "maintag:Slashdot")
        self.compare_var("oir_tctag", "maintag:Slashdot")
        self.compare_var("oir_tcids", [ "id1" ])

        # 12. Update should cause all tags to generate a tag_update
        # hook call on ITEMS, and update_complete when all done.

        self.reset_flags()

        tag_updater.update()

        tag_backend.inject("ITEMS", { "maintag:Test1" : [ "id3", "id4" ] })
        tag_backend.inject("ITEMSDONE", {})
        tag_backend.inject("ATTRIBUTES", { "id3" : { "test" : "test" }, "id4" : { "test" : "test" }})

        print(tag_updater.updating)
        self.compare_flags(TAG_UPDATED | UPDATE_COMPLETE | ITEMS_ADDED | ATTRIBUTES)
        self.compare_var("otu_tag", "maintag:Test1")


        return True
Example #6
0
    def check(self):
        config_script = {
            'VERSION': {
                '*': [('VERSION', CANTO_PROTOCOL_COMPATIBLE)]
            },
            'CONFIGS': {
                '*': [('CONFIGS', {
                    "CantoCurses": config.template_config
                })]
            },
        }

        config_backend = TestBackend("config", config_script)

        config.init(config_backend, CANTO_PROTOCOL_COMPATIBLE)

        config_backend.inject("NEWTAGS",
                              ["maintag:Slashdot", "maintag:reddit"])

        tagcore_script = {}

        tag_backend = TestBackend("tagcore", tagcore_script)

        on_hook("curses_items_removed", self.on_items_removed)
        on_hook("curses_items_added", self.on_items_added)
        on_hook("curses_new_tagcore", self.on_new_tagcore)
        on_hook("curses_del_tagcore", self.on_del_tagcore)
        on_hook("curses_attributes", self.on_attributes)
        on_hook("curses_update_complete", self.on_update_complete)
        on_hook("curses_tag_updated", self.on_tag_updated)

        # 1. Previously existing tags in config should be populated on init

        self.reset_flags()

        tag_updater.init(tag_backend)

        for tag in config.vars["strtags"]:
            for tc in alltagcores:
                if tc.tag == tag:
                    break
            else:
                raise Exception("Couldn't find TC for tag %s" % tag)

        self.compare_flags(NEW_TC)

        self.reset_flags()

        # 2. Getting empty ITEMS responses should cause no events

        tag_backend.inject("ITEMS", {"maintag:Slashdot": []})
        tag_backend.inject("ITEMSDONE", {})

        tag_backend.inject("ITEMS", {"maintag:reddit": []})
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(0)

        # 3. Getting a non-empty ITEMS response should cause items_added

        tag_backend.inject("ITEMS", {"maintag:Slashdot": ["id1", "id2"]})
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(ITEMS_ADDED)

        # 4. Getting attributes should cause attributes hook

        self.reset_flags()

        id1_content = {
            "title": "id1",
            "canto-state": [],
            "canto-tags": [],
            "link": "id1-link",
            "enclosures": ""
        }

        id2_content = {
            "title": "id2",
            "canto-state": [],
            "canto-tags": [],
            "link": "id2-link",
            "enclosures": ""
        }

        all_content = {"id1": id1_content, "id2": id2_content}

        tag_backend.inject("ATTRIBUTES", all_content)

        self.compare_flags(ATTRIBUTES)
        self.compare_var("attributes", all_content)

        id1_got = tag_updater.get_attributes("id1")
        if id1_got != id1_content:
            raise Exception("Bad content: wanted %s - got %s" %
                            (id1_content, id1_got))
        id2_got = tag_updater.get_attributes("id2")
        if id2_got != id2_content:
            raise Exception("Bad content: wanted %s - got %s" %
                            (id2_content, id2_got))

        # 5. Removing an item should *NOT* cause its attributes to be forgotten
        # that happens on stories_removed, and should cause ITEMS_REMOVED

        self.reset_flags()

        tag_backend.inject("ITEMS", {"maintag:Slashdot": ["id1"]})
        tag_backend.inject("ITEMSDONE", {})

        self.compare_flags(ITEMS_REMOVED)

        id2_got = tag_updater.get_attributes("id2")
        if id2_got != id2_content:
            raise Exception("Bad content: wanted %s - got %s" %
                            (id2_content, id2_got))

        # 6. Getting a stories_removed hook should make it forget attributes

        self.reset_flags()

        call_hook("curses_stories_removed",
                  [FakeTag("maintag:Slashdot"), [FakeStory("id2")]])

        if "id2" in tag_updater.attributes:
            raise Exception("Expected id2 to be removed, but it isn't!")

        self.compare_flags(0)

        # 7. Getting attributes for non-existent IDs should return empty

        id2_got = tag_updater.get_attributes("id2")
        if id2_got != {}:
            raise Exception(
                "Expected non-existent id to return empty! Got %s" % id2_got)

        self.compare_flags(0)

        # 8. Getting stories_removed for item still in tag should do nothing

        call_hook("curses_stories_removed",
                  [FakeTag("maintag:Slashdot"), [FakeStory("id1")]])

        if "id1" not in tag_updater.attributes:
            raise Exception("Expected id1 to remain in attributes!")

        self.compare_flags(0)

        # 9. Config adding a tag should create a new tagcore

        config_backend.inject("NEWTAGS", ["maintag:Test1"])

        self.compare_flags(NEW_TC)

        # 10. Config removing an empty tag should delete a tagcore

        self.reset_flags()

        config_backend.inject("DELTAGS", ["maintag:reddit"])

        self.compare_flags(DEL_TC)
        self.compare_var("del_tc", "maintag:reddit")

        # 11. Config removing an populated tag should delete a tagcore and
        # cause items_removed. NOTE for now tagcores are never deleted, they
        # just exist empty

        self.reset_flags()

        config_backend.inject("DELTAGS", ["maintag:Slashdot"])

        self.compare_flags(DEL_TC | ITEMS_REMOVED)
        self.compare_var("del_tc", "maintag:Slashdot")
        self.compare_var("oir_tctag", "maintag:Slashdot")
        self.compare_var("oir_tcids", ["id1"])

        # 12. Update should cause all tags to generate a tag_update
        # hook call on ITEMS, and update_complete when all done.

        self.reset_flags()

        tag_updater.update()

        tag_backend.inject("ITEMS", {"maintag:Test1": ["id3", "id4"]})
        tag_backend.inject("ITEMSDONE", {})
        tag_backend.inject("ATTRIBUTES", {
            "id3": {
                "test": "test"
            },
            "id4": {
                "test": "test"
            }
        })

        print(tag_updater.updating)
        self.compare_flags(TAG_UPDATED | UPDATE_COMPLETE | ITEMS_ADDED
                           | ATTRIBUTES)
        self.compare_var("otu_tag", "maintag:Test1")

        return True
Example #7
0
    def check(self):
        version_check_script = { 'VERSION' : { '*' : [('VERSION', 0.1)] } }

        backend = TestBackend("config", version_check_script)

        return config.init(backend, CANTO_PROTOCOL_COMPATIBLE) == False
    def check(self):
        script = {
            "VERSION": {"*": [("VERSION", CANTO_PROTOCOL_COMPATIBLE)]},
            "CONFIGS": {"*": [("CONFIGS", {"CantoCurses": config.template_config})]},
        }

        backend = TestBackend("config", script)

        config.init(backend, CANTO_PROTOCOL_COMPATIBLE)

        on_hook("curses_tag_opt_change", self.on_tag_opt_change)
        on_hook("curses_opt_change", self.on_opt_change)
        on_hook("curses_def_opt_change", self.on_def_opt_change)
        on_hook("curses_feed_opt_change", self.on_feed_opt_change)
        on_hook("curses_new_tag", self.on_new_tag)
        on_hook("curses_del_tag", self.on_del_tag)
        on_hook("curses_eval_tags_changed", self.on_eval_tags_changed)

        # 1. Only Opt_change

        self.reset_flags()

        test_config = eval(repr(config.template_config))
        test_config["browser"]["path"] = "testoption"

        backend.inject("CONFIGS", {"CantoCurses": test_config})

        self.compare_flags(OPT_CHANGE)
        self.compare_config(config.config, "browser.path", "testoption")

        # Check that the opt change hook got the smallest possible changeset

        self.compare_var("oc_opts", {"browser": {"path": "testoption"}})

        # 2. Invalid Tag_opt_change

        self.reset_flags()

        test_config = {"tags": {"test": eval(repr(config.tag_template_config))}}

        backend.inject("CONFIGS", test_config)

        self.compare_flags(0)

        # 3. NEWTAG (also causes OPT_CHANGE because of tag order being
        # expanded) Does not cause an EVAL CHANGE because test1 doesn't get
        # match by the tags setting (i.e. tags starting with maintag:)

        self.reset_flags()

        backend.inject("NEWTAGS", ["test1"])

        self.compare_flags(NEW_TAG | OPT_CHANGE)
        self.compare_config(config.config, "tagorder", ["test1"])
        self.compare_config(config.vars, "curtags", [])
        self.compare_var("new_tags", "test1")
        self.compare_var("oc_opts", {"tagorder": ["test1"]})

        # 4. NEWTAG, this time with EVAL because "maintag:Slashdot" does match tags

        self.reset_flags()

        backend.inject("NEWTAGS", ["maintag:Slashdot"])

        self.compare_flags(NEW_TAG | OPT_CHANGE | EVAL_TAGS)

        self.compare_config(config.config, "tagorder", ["test1", "maintag:Slashdot"])
        self.compare_config(config.vars, "curtags", ["maintag:Slashdot"])

        # 5. switch_tags (promote demote)

        self.reset_flags()

        # These are fodder
        backend.inject("NEWTAGS", ["maintag:Test2"])
        backend.inject("NEWTAGS", ["maintag:Test3"])
        backend.inject("NEWTAGS", ["maintag:Test4"])

        self.compare_flags(NEW_TAG | OPT_CHANGE | EVAL_TAGS)
        self.compare_config(
            config.config, "tagorder", ["test1", "maintag:Slashdot", "maintag:Test2", "maintag:Test3", "maintag:Test4"]
        )
        self.compare_config(
            config.vars, "curtags", ["maintag:Slashdot", "maintag:Test2", "maintag:Test3", "maintag:Test4"]
        )

        self.reset_flags()

        config.switch_tags("maintag:Test2", "maintag:Test3")

        self.compare_flags(OPT_CHANGE | EVAL_TAGS)
        self.compare_config(
            config.config, "tagorder", ["test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2", "maintag:Test4"]
        )
        self.compare_config(
            config.vars, "curtags", ["maintag:Slashdot", "maintag:Test3", "maintag:Test2", "maintag:Test4"]
        )
        self.compare_var(
            "oc_opts", {"tagorder": ["test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2", "maintag:Test4"]}
        )

        # 6. DELTAG

        self.reset_flags()

        backend.inject("DELTAGS", ["maintag:Test4"])

        self.compare_flags(DEL_TAG | OPT_CHANGE | EVAL_TAGS)
        self.compare_config(config.config, "tagorder", ["test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.compare_config(config.vars, "curtags", ["maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.compare_var("del_tags", "maintag:Test4")

        # 7. Changing the tags regex

        self.reset_flags()

        # More fodder
        backend.inject("NEWTAGS", ["alt:t1"])
        backend.inject("NEWTAGS", ["alt:t2"])
        backend.inject("NEWTAGS", ["alt:t3"])

        self.compare_flags(NEW_TAG | OPT_CHANGE)
        self.compare_config(
            config.config,
            "tagorder",
            ["test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2", "alt:t1", "alt:t2", "alt:t3"],
        )
        self.compare_config(config.vars, "curtags", ["maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.reset_flags()

        c = config.get_conf()
        c["tags"] = "alt:.*"
        config.set_conf(c)

        self.compare_flags(OPT_CHANGE | EVAL_TAGS)
        return True
Example #9
0
    def check(self):
        script = {
            'VERSION': {
                '*': [('VERSION', CANTO_PROTOCOL_COMPATIBLE)]
            },
            'CONFIGS': {
                '*': [('CONFIGS', {
                    "CantoCurses": config.template_config
                })]
            },
        }

        backend = TestBackend("config", script)

        config.init(backend, CANTO_PROTOCOL_COMPATIBLE)

        on_hook("curses_tag_opt_change", self.on_tag_opt_change)
        on_hook("curses_opt_change", self.on_opt_change)
        on_hook("curses_def_opt_change", self.on_def_opt_change)
        on_hook("curses_feed_opt_change", self.on_feed_opt_change)
        on_hook("curses_new_tag", self.on_new_tag)
        on_hook("curses_del_tag", self.on_del_tag)
        on_hook("curses_eval_tags_changed", self.on_eval_tags_changed)

        # 1. Only Opt_change

        self.reset_flags()

        test_config = eval(repr(config.template_config))
        test_config["browser"]["path"] = "testoption"

        backend.inject("CONFIGS", {"CantoCurses": test_config})

        self.compare_flags(OPT_CHANGE)
        self.compare_config(config.config, "browser.path", "testoption")

        # Check that the opt change hook got the smallest possible changeset

        self.compare_var("oc_opts", {"browser": {"path": "testoption"}})

        # 2. Invalid Tag_opt_change

        self.reset_flags()

        test_config = {
            "tags": {
                "test": eval(repr(config.tag_template_config))
            }
        }

        backend.inject("CONFIGS", test_config)

        self.compare_flags(0)

        # 3. NEWTAG (also causes OPT_CHANGE because of tag order being
        # expanded) Does not cause an EVAL CHANGE because test1 doesn't get
        # match by the tags setting (i.e. tags starting with maintag:)

        self.reset_flags()

        backend.inject("NEWTAGS", ["test1"])

        self.compare_flags(NEW_TAG | OPT_CHANGE)
        self.compare_config(config.config, "tagorder", ["test1"])
        self.compare_config(config.vars, "curtags", [])
        self.compare_var("new_tags", "test1")
        self.compare_var("oc_opts", {"tagorder": ["test1"]})

        # 4. NEWTAG, this time with EVAL because "maintag:Slashdot" does match tags

        self.reset_flags()

        backend.inject("NEWTAGS", ["maintag:Slashdot"])

        self.compare_flags(NEW_TAG | OPT_CHANGE | EVAL_TAGS)

        self.compare_config(config.config, "tagorder",
                            ["test1", "maintag:Slashdot"])
        self.compare_config(config.vars, "curtags", ["maintag:Slashdot"])

        # 5. switch_tags (promote demote)

        self.reset_flags()

        # These are fodder
        backend.inject("NEWTAGS", ["maintag:Test2"])
        backend.inject("NEWTAGS", ["maintag:Test3"])
        backend.inject("NEWTAGS", ["maintag:Test4"])

        self.compare_flags(NEW_TAG | OPT_CHANGE | EVAL_TAGS)
        self.compare_config(config.config, "tagorder", [
            "test1", "maintag:Slashdot", "maintag:Test2", "maintag:Test3",
            "maintag:Test4"
        ])
        self.compare_config(config.vars, "curtags", [
            "maintag:Slashdot", "maintag:Test2", "maintag:Test3",
            "maintag:Test4"
        ])

        self.reset_flags()

        config.switch_tags("maintag:Test2", "maintag:Test3")

        self.compare_flags(OPT_CHANGE | EVAL_TAGS)
        self.compare_config(config.config, "tagorder", [
            "test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2",
            "maintag:Test4"
        ])
        self.compare_config(config.vars, "curtags", [
            "maintag:Slashdot", "maintag:Test3", "maintag:Test2",
            "maintag:Test4"
        ])
        self.compare_var(
            "oc_opts", {
                "tagorder": [
                    "test1", "maintag:Slashdot", "maintag:Test3",
                    "maintag:Test2", "maintag:Test4"
                ]
            })

        # 6. DELTAG

        self.reset_flags()

        backend.inject("DELTAGS", ["maintag:Test4"])

        self.compare_flags(DEL_TAG | OPT_CHANGE | EVAL_TAGS)
        self.compare_config(
            config.config, "tagorder",
            ["test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.compare_config(
            config.vars, "curtags",
            ["maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.compare_var("del_tags", "maintag:Test4")

        # 7. Changing the tags regex

        self.reset_flags()

        # More fodder
        backend.inject("NEWTAGS", ["alt:t1"])
        backend.inject("NEWTAGS", ["alt:t2"])
        backend.inject("NEWTAGS", ["alt:t3"])

        self.compare_flags(NEW_TAG | OPT_CHANGE)
        self.compare_config(config.config, "tagorder", [
            "test1", "maintag:Slashdot", "maintag:Test3", "maintag:Test2",
            "alt:t1", "alt:t2", "alt:t3"
        ])
        self.compare_config(
            config.vars, "curtags",
            ["maintag:Slashdot", "maintag:Test3", "maintag:Test2"])
        self.reset_flags()

        c = config.get_conf()
        c["tags"] = "alt:.*"
        config.set_conf(c)

        self.compare_flags(OPT_CHANGE | EVAL_TAGS)
        return True