Esempio n. 1
0
class WorkflowTests(unittest.TestCase):

    def setUp(self):
        self.libs = [os.path.join(os.path.dirname(__file__), 'lib')]
        self.wf = Workflow(libraries=self.libs)
        self.account = 'this-is-my-test-account'
        self.password = '******'
        self.password2 = 'this-is-my-other-safe-password'
        self.search_items = [
            ('Test Item One', MATCH_STARTSWITH),
            ('test item two', MATCH_STARTSWITH),
            ('TwoExtraSpecialTests', MATCH_CAPITALS),
            ('this-is-a-test', MATCH_ATOM),
            ('the extra special trials', MATCH_INITIALS_STARTSWITH),
            ('not the extra special trials', MATCH_INITIALS_CONTAIN),
            ('intestinal fortitude', MATCH_SUBSTRING),
            ('the splits', MATCH_ALLCHARS),
            ('nomatch', 0),
        ]

    def tearDown(self):
        self.wf.clear_cache()
        self.wf.clear_settings()
        try:
            self.wf.delete_password(self.account)
        except PasswordNotFound:
            pass
        for dirpath in (self.wf.cachedir, self.wf.datadir):
            if os.path.exists(dirpath):
                shutil.rmtree(dirpath)

    def test_item_creation(self):
        """XML generation"""
        self.wf.add_item('title', 'subtitle', 'arg',
                         autocomplete='autocomplete',
                         valid=True, uid='uid', icon='icon.png',
                         icontype='fileicon',
                         type='file')
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        self.assertEqual(item.attrib['uid'], 'uid')
        self.assertEqual(item.attrib['autocomplete'], 'autocomplete')
        self.assertEqual(item.attrib['valid'], 'yes')
        self.assertEqual(item.attrib['uid'], 'uid')
        title, subtitle, arg, icon = list(item)
        self.assertEqual(title.text, 'title')
        self.assertEqual(title.tag, 'title')
        self.assertEqual(subtitle.text, 'subtitle')
        self.assertEqual(subtitle.tag, 'subtitle')
        self.assertEqual(arg.text, 'arg')
        self.assertEqual(arg.tag, 'arg')
        self.assertEqual(icon.text, 'icon.png')
        self.assertEqual(icon.tag, 'icon')
        self.assertEqual(icon.attrib['type'], 'fileicon')

    def test_item_creation_no_optionals(self):
        """XML generation (no optionals)"""
        self.wf.add_item('title',
                         valid=False)
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        for key in ['uid', 'arg', 'autocomplete']:
            self.assertFalse(key in item.attrib)
        self.assertEqual(item.attrib['valid'], 'no')
        title, subtitle = list(item)
        self.assertEqual(title.text, 'title')
        self.assertEqual(title.tag, 'title')
        self.assertEqual(subtitle.text, None)
        tags = [elem.tag for elem in list(item)]
        for tag in ['icon', 'arg']:
            self.assert_(tag not in tags)

    def test_additional_libs(self):
        """Additional libraries"""
        for path in self.libs:
            self.assert_(path in sys.path)
        self.assertEqual(sys.path[0:len(self.libs)], self.libs)
        import youcanimportme

    def test_info_plist(self):
        """info.plist"""
        self.assertEqual(self.wf.name, WORKFLOW_NAME)
        self.assertEqual(self.wf.bundleid, BUNDLE_ID)

    def test_args(self):
        """ARGV"""
        args = ['arg1', 'arg2', 'füntíme']
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode('utf-8') for s in args]
        wf = Workflow()
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_arg_normalisation(self):
        """ARGV normalisation"""
        def nfdme(s):
            return normalize('NFD', s)
        args = map(nfdme, ['arg1', 'arg2', 'füntíme'])
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode('utf-8') for s in args]
        wf = Workflow(normalization='NFD')
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_magic_args(self):
        """Magic args"""
        # cache original sys.argv
        oargs = sys.argv[:]

        # # openlog
        # sys.argv = [oargs[0]] + [b'workflow:openlog']
        # try:
        #     wf = Workflow()
        #     wf.logger.debug('This is a test message')  # ensure log file exists
        #     with self.assertRaises(SystemExit):
        #         wf.args
        # finally:
        #     sys.argv = oargs[:]

        # delsettings
        sys.argv = [oargs[0]] + [b'workflow:delsettings']
        try:
            wf = Workflow(default_settings={'arg1': 'value1'})
            self.assertEqual(wf.settings['arg1'], 'value1')
            self.assertTrue(os.path.exists(wf.settings_path))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.settings_path))
        finally:
            sys.argv = oargs[:]

        # delcache
        sys.argv = [oargs[0]] + [b'workflow:delcache']

        def somedata():
            return {'arg1': 'value1'}

        try:
            wf = Workflow()
            cachepath = wf.cachefile('somedir')
            os.makedirs(cachepath)
            wf.cached_data('test', somedata)
            self.assertTrue(os.path.exists(wf.cachefile('test.cache')))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.cachefile('test.cache')))
        finally:
            sys.argv = oargs[:]

    def test_logger(self):
        """Logger"""
        self.assert_(isinstance(self.wf.logger, logging.Logger))
        logger = logging.Logger('')
        self.wf.logger = logger
        self.assertEqual(self.wf.logger, logger)

    def test_cached_data(self):
        """Cached data stored"""
        data = {'key1': 'value1'}
        d = self.wf.cached_data('test', lambda: data, max_age=10)
        self.assertEqual(data, d)

    def test_cached_data_callback(self):
        """Cached data callback"""
        called = {'called': False}
        data = [1, 2, 3]

        def getdata():
            called['called'] = True
            return data

        d = self.wf.cached_data('test', getdata, max_age=10)
        self.assertEqual(d, data)
        self.assertTrue(called['called'])

    def test_cached_data_no_callback(self):
        """Cached data no callback"""
        d = self.wf.cached_data('nonexistent', None)
        self.assertEqual(d, None)

    def test_cache_expires(self):
        """Cached data expires"""
        data = ('hello', 'goodbye')
        called = {'called': False}

        def getdata():
            called['called'] = True
            return data

        d = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(called['called'])
        # should be loaded from cache
        called['called'] = False
        d2 = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d2, data)
        self.assertFalse(called['called'])
        # cache has expired
        time.sleep(1)
        # should be loaded from cache (no expiry)
        d3 = self.wf.cached_data('test', getdata, max_age=0)
        self.assertEqual(d3, data)
        self.assertFalse(called['called'])
        # should hit data func (cached data older than 1 sec)
        d4 = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d4, data)
        self.assertTrue(called['called'])

    def test_cache_fresh(self):
        """Cached data is fresh"""
        data = 'This is my data'
        d = self.wf.cached_data('test', lambda: data, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(self.wf.cached_data_fresh('test', max_age=10))

    def test_cache_fresh_non_existent(self):
        """Non-existant cache data is not fresh"""
        self.assertEqual(self.wf.cached_data_fresh('popsicle', max_age=10000),
                          False)

    def test_keychain(self):
        """Save/get/delete password"""
        self.assertRaises(PasswordNotFound,
                          self.wf.delete_password, self.account)
        self.assertRaises(PasswordNotFound, self.wf.get_password, self.account)
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        self.assertEqual(self.wf.get_password(self.account, BUNDLE_ID),
                          self.password)
        # try to set same password
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        # try to set different password
        self.wf.save_password(self.account, self.password2)
        self.assertEqual(self.wf.get_password(self.account), self.password2)
        # bad call to _call_security
        with self.assertRaises(KeychainError):
            self.wf._call_security('pants', BUNDLE_ID, self.account)

    def test_run_fails(self):
        """Run fails"""
        def cb(wf):
            self.assertEqual(wf, self.wf)
            raise ValueError('Have an error')
        self.wf.name  # cause info.plist to be parsed
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)
        # named after bundleid
        self.wf = Workflow()
        self.wf.bundleid
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)

    def test_run_okay(self):
        """Run okay"""
        def cb(wf):
            self.assertEqual(wf, self.wf)
        ret = self.wf.run(cb)
        self.assertEqual(ret, 0)

    def test_filter_all_rules(self):
        """Filter: all rules"""
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 ascending=True)
        self.assertEqual(len(results), 8)
        # now with scores, rules
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 include_score=True)
        self.assertEqual(len(results), 8)
        for item, score, rule in results:
            self.wf.logger.debug('{} : {}'.format(item, score))
            for value, r in self.search_items:
                if value == item[0]:
                    self.assertEqual(rule, r)
        # self.assertTrue(False)

    def test_filter_no_caps(self):
        """Filter: no caps"""
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 ascending=True,
                                 match_on=MATCH_ALL ^ MATCH_CAPITALS,
                                 include_score=True)
        self._print_results(results)
        for item, score, rule in results:
            self.assertNotEqual(rule, MATCH_CAPITALS)
        # self.assertEqual(len(results), 7)

    def test_filter_only_caps(self):
        """Filter: only caps"""
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 ascending=True,
                                 match_on=MATCH_CAPITALS,
                                 include_score=True)
        self._print_results(results)
        self.assertEqual(len(results), 1)

    def test_filter_max_results(self):
        """Filter: max results"""
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 ascending=True, max_results=4)
        self.assertEqual(len(results), 4)

    def test_filter_min_score(self):
        """Filter: min score"""
        results = self.wf.filter('test', self.search_items, key=lambda x: x[0],
                                 ascending=True, min_score=90,
                                 include_score=True)
        self.assertEqual(len(results), 6)

    def test_icons(self):
        """Icons"""
        import workflow
        for name in dir(workflow):
            if name.startswith('ICON_'):
                path = getattr(workflow, name)
                print(name, path)
                self.assert_(os.path.exists(path))

    def _print_results(self, results):
        """Print results of Workflow.filter"""
        for item, score, rule in results:
            print('{0} (rule {1}) : {2}'.format(item[0], rule, score))
class WorkflowTests(unittest.TestCase):
    def setUp(self):
        self.libs = [os.path.join(os.path.dirname(__file__), "lib")]
        self.wf = Workflow(libraries=self.libs)
        self.account = "this-is-my-test-account"
        self.password = "******"
        self.password2 = "this-is-my-other-safe-password"
        self.search_items = [
            ("Test Item One", MATCH_STARTSWITH),
            ("test item two", MATCH_STARTSWITH),
            ("TwoExtraSpecialTests", MATCH_CAPITALS),
            ("this-is-a-test", MATCH_ATOM),
            ("the extra special trials", MATCH_INITIALS_STARTSWITH),
            ("not the extra special trials", MATCH_INITIALS_CONTAIN),
            ("intestinal fortitude", MATCH_SUBSTRING),
            ("the splits", MATCH_ALLCHARS),
            ("nomatch", 0),
        ]

        self.search_items_diacritics = [
            # search key, query
            ("Änderungen vorbehalten", "av"),
            ("Änderungen", "anderungen"),
            ("überwiegend bewolkt", "ub"),
            ("überwiegend", "uberwiegend"),
            ("Öffnungszeiten an Feiertagen", "offnungszeiten"),
            ("Öffnungszeiten an Feiertagen", "oaf"),
            ("Fußpilz", "fuss"),
            ("salé", "sale"),
        ]

    def tearDown(self):
        self.wf.clear_cache()
        self.wf.clear_settings()
        try:
            self.wf.delete_password(self.account)
        except PasswordNotFound:
            pass
        for dirpath in (self.wf.cachedir, self.wf.datadir):
            if os.path.exists(dirpath):
                shutil.rmtree(dirpath)

    def test_item_creation(self):
        """XML generation"""
        self.wf.add_item(
            "title",
            "subtitle",
            "arg",
            autocomplete="autocomplete",
            valid=True,
            uid="uid",
            icon="icon.png",
            icontype="fileicon",
            type="file",
        )
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        self.assertEqual(item.attrib["uid"], "uid")
        self.assertEqual(item.attrib["autocomplete"], "autocomplete")
        self.assertEqual(item.attrib["valid"], "yes")
        self.assertEqual(item.attrib["uid"], "uid")
        title, subtitle, arg, icon = list(item)
        self.assertEqual(title.text, "title")
        self.assertEqual(title.tag, "title")
        self.assertEqual(subtitle.text, "subtitle")
        self.assertEqual(subtitle.tag, "subtitle")
        self.assertEqual(arg.text, "arg")
        self.assertEqual(arg.tag, "arg")
        self.assertEqual(icon.text, "icon.png")
        self.assertEqual(icon.tag, "icon")
        self.assertEqual(icon.attrib["type"], "fileicon")

    def test_item_creation_no_optionals(self):
        """XML generation (no optionals)"""
        self.wf.add_item("title", valid=False)
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        for key in ["uid", "arg", "autocomplete"]:
            self.assertFalse(key in item.attrib)
        self.assertEqual(item.attrib["valid"], "no")
        title, subtitle = list(item)
        self.assertEqual(title.text, "title")
        self.assertEqual(title.tag, "title")
        self.assertEqual(subtitle.text, None)
        tags = [elem.tag for elem in list(item)]
        for tag in ["icon", "arg"]:
            self.assert_(tag not in tags)

    def test_additional_libs(self):
        """Additional libraries"""
        for path in self.libs:
            self.assert_(path in sys.path)
        self.assertEqual(sys.path[0 : len(self.libs)], self.libs)
        import youcanimportme

    def test_info_plist(self):
        """info.plist"""
        self.assertEqual(self.wf.name, WORKFLOW_NAME)
        self.assertEqual(self.wf.bundleid, BUNDLE_ID)

    def test_args(self):
        """ARGV"""
        args = ["arg1", "arg2", "füntíme"]
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode("utf-8") for s in args]
        wf = Workflow()
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_arg_normalisation(self):
        """ARGV normalisation"""

        def nfdme(s):
            return normalize("NFD", s)

        args = map(nfdme, ["arg1", "arg2", "füntíme"])
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode("utf-8") for s in args]
        wf = Workflow(normalization="NFD")
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_magic_args(self):
        """Magic args"""
        # cache original sys.argv
        oargs = sys.argv[:]

        # # openlog
        # sys.argv = [oargs[0]] + [b'workflow:openlog']
        # try:
        #     wf = Workflow()
        #     wf.logger.debug('This is a test message')  # ensure log file exists
        #     with self.assertRaises(SystemExit):
        #         wf.args
        # finally:
        #     sys.argv = oargs[:]

        # delsettings
        sys.argv = [oargs[0]] + [b"workflow:delsettings"]
        try:
            wf = Workflow(default_settings={"arg1": "value1"})
            self.assertEqual(wf.settings["arg1"], "value1")
            self.assertTrue(os.path.exists(wf.settings_path))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.settings_path))
        finally:
            sys.argv = oargs[:]

        # delcache
        sys.argv = [oargs[0]] + [b"workflow:delcache"]

        def somedata():
            return {"arg1": "value1"}

        try:
            wf = Workflow()
            cachepath = wf.cachefile("somedir")
            os.makedirs(cachepath)
            wf.cached_data("test", somedata)
            self.assertTrue(os.path.exists(wf.cachefile("test.cache")))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.cachefile("test.cache")))
        finally:
            sys.argv = oargs[:]

    def test_logger(self):
        """Logger"""
        self.assert_(isinstance(self.wf.logger, logging.Logger))
        logger = logging.Logger("")
        self.wf.logger = logger
        self.assertEqual(self.wf.logger, logger)

    def test_cached_data(self):
        """Cached data stored"""
        data = {"key1": "value1"}
        d = self.wf.cached_data("test", lambda: data, max_age=10)
        self.assertEqual(data, d)

    def test_cached_data_callback(self):
        """Cached data callback"""
        called = {"called": False}
        data = [1, 2, 3]

        def getdata():
            called["called"] = True
            return data

        d = self.wf.cached_data("test", getdata, max_age=10)
        self.assertEqual(d, data)
        self.assertTrue(called["called"])

    def test_cached_data_no_callback(self):
        """Cached data no callback"""
        d = self.wf.cached_data("nonexistent", None)
        self.assertEqual(d, None)

    def test_cache_expires(self):
        """Cached data expires"""
        data = ("hello", "goodbye")
        called = {"called": False}

        def getdata():
            called["called"] = True
            return data

        d = self.wf.cached_data("test", getdata, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(called["called"])
        # should be loaded from cache
        called["called"] = False
        d2 = self.wf.cached_data("test", getdata, max_age=1)
        self.assertEqual(d2, data)
        self.assertFalse(called["called"])
        # cache has expired
        time.sleep(1)
        # should be loaded from cache (no expiry)
        d3 = self.wf.cached_data("test", getdata, max_age=0)
        self.assertEqual(d3, data)
        self.assertFalse(called["called"])
        # should hit data func (cached data older than 1 sec)
        d4 = self.wf.cached_data("test", getdata, max_age=1)
        self.assertEqual(d4, data)
        self.assertTrue(called["called"])

    def test_cache_fresh(self):
        """Cached data is fresh"""
        data = "This is my data"
        d = self.wf.cached_data("test", lambda: data, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(self.wf.cached_data_fresh("test", max_age=10))

    def test_cache_fresh_non_existent(self):
        """Non-existant cache data is not fresh"""
        self.assertEqual(self.wf.cached_data_fresh("popsicle", max_age=10000), False)

    def test_keychain(self):
        """Save/get/delete password"""
        self.assertRaises(PasswordNotFound, self.wf.delete_password, self.account)
        self.assertRaises(PasswordNotFound, self.wf.get_password, self.account)
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        self.assertEqual(self.wf.get_password(self.account, BUNDLE_ID), self.password)
        # try to set same password
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        # try to set different password
        self.wf.save_password(self.account, self.password2)
        self.assertEqual(self.wf.get_password(self.account), self.password2)
        # bad call to _call_security
        with self.assertRaises(KeychainError):
            self.wf._call_security("pants", BUNDLE_ID, self.account)

    def test_run_fails(self):
        """Run fails"""

        def cb(wf):
            self.assertEqual(wf, self.wf)
            raise ValueError("Have an error")

        self.wf.name  # cause info.plist to be parsed
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)
        # named after bundleid
        self.wf = Workflow()
        self.wf.bundleid
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)

    def test_run_okay(self):
        """Run okay"""

        def cb(wf):
            self.assertEqual(wf, self.wf)

        ret = self.wf.run(cb)
        self.assertEqual(ret, 0)

    def test_filter_all_rules(self):
        """Filter: all rules"""
        results = self.wf.filter("test", self.search_items, key=lambda x: x[0], ascending=True, match_on=MATCH_ALL)
        self.assertEqual(len(results), 8)
        # now with scores, rules
        results = self.wf.filter("test", self.search_items, key=lambda x: x[0], include_score=True, match_on=MATCH_ALL)
        self.assertEqual(len(results), 8)
        for item, score, rule in results:
            self.wf.logger.debug("{} : {}".format(item, score))
            for value, r in self.search_items:
                if value == item[0]:
                    self.assertEqual(rule, r)
        # self.assertTrue(False)

    def test_filter_no_caps(self):
        """Filter: no caps"""
        results = self.wf.filter(
            "test",
            self.search_items,
            key=lambda x: x[0],
            ascending=True,
            match_on=MATCH_ALL ^ MATCH_CAPITALS,
            include_score=True,
        )
        self._print_results(results)
        for item, score, rule in results:
            self.assertNotEqual(rule, MATCH_CAPITALS)
        # self.assertEqual(len(results), 7)

    def test_filter_only_caps(self):
        """Filter: only caps"""
        results = self.wf.filter(
            "test", self.search_items, key=lambda x: x[0], ascending=True, match_on=MATCH_CAPITALS, include_score=True
        )
        self._print_results(results)
        self.assertEqual(len(results), 1)

    def test_filter_max_results(self):
        """Filter: max results"""
        results = self.wf.filter("test", self.search_items, key=lambda x: x[0], ascending=True, max_results=4)
        self.assertEqual(len(results), 4)

    def test_filter_min_score(self):
        """Filter: min score"""
        results = self.wf.filter(
            "test", self.search_items, key=lambda x: x[0], ascending=True, min_score=90, include_score=True
        )
        self.assertEqual(len(results), 6)

    def test_filter_folding(self):
        """Filter: diacritic folding"""
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key], min_score=90, include_score=True)
            self.assertEqual(len(results), 1)

    def test_filter_folding_off(self):
        """Filter: diacritic folding off"""
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key], min_score=90, include_score=True, fold_diacritics=False)
            self.assertEqual(len(results), 0)

    def test_filter_folding_force_on(self):
        """Filter: diacritic folding forced on"""
        self.wf.settings["__workflows_diacritic_folding"] = True
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key], min_score=90, include_score=True, fold_diacritics=False)
            self.assertEqual(len(results), 1)

    def test_filter_folding_force_off(self):
        """Filter: diacritic folding forced off"""
        self.wf.settings["__workflows_diacritic_folding"] = False
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key], min_score=90, include_score=True)
            self.assertEqual(len(results), 0)

    def test_icons(self):
        """Icons"""
        import workflow

        for name in dir(workflow):
            if name.startswith("ICON_"):
                path = getattr(workflow, name)
                print(name, path)
                self.assert_(os.path.exists(path))

    def _print_results(self, results):
        """Print results of Workflow.filter"""
        for item, score, rule in results:
            print("{!r} (rule {}) : {}".format(item[0], rule, score))
Esempio n. 3
0
class WorkflowTests(unittest.TestCase):
    def setUp(self):
        self.libs = [os.path.join(os.path.dirname(__file__), 'lib')]
        self.wf = Workflow(libraries=self.libs)
        self.account = 'this-is-my-test-account'
        self.password = '******'
        self.password2 = 'this-is-my-other-safe-password'
        self.search_items = [
            ('Test Item One', MATCH_STARTSWITH),
            ('test item two', MATCH_STARTSWITH),
            ('TwoExtraSpecialTests', MATCH_CAPITALS),
            ('this-is-a-test', MATCH_ATOM),
            ('the extra special trials', MATCH_INITIALS_STARTSWITH),
            ('not the extra special trials', MATCH_INITIALS_CONTAIN),
            ('intestinal fortitude', MATCH_SUBSTRING),
            ('the splits', MATCH_ALLCHARS),
            ('nomatch', 0),
        ]

        self.search_items_diacritics = [
            # search key, query
            ('Änderungen vorbehalten', 'av'),
            ('Änderungen', 'anderungen'),
            ('überwiegend bewolkt', 'ub'),
            ('überwiegend', 'uberwiegend'),
            ('Öffnungszeiten an Feiertagen', 'offnungszeiten'),
            ('Öffnungszeiten an Feiertagen', 'oaf'),
            ('Fußpilz', 'fuss'),
            ('salé', 'sale')
        ]

    def tearDown(self):
        self.wf.clear_cache()
        self.wf.clear_settings()
        try:
            self.wf.delete_password(self.account)
        except PasswordNotFound:
            pass
        for dirpath in (self.wf.cachedir, self.wf.datadir):
            if os.path.exists(dirpath):
                shutil.rmtree(dirpath)

    def test_item_creation(self):
        """XML generation"""
        self.wf.add_item('title',
                         'subtitle',
                         'arg',
                         autocomplete='autocomplete',
                         valid=True,
                         uid='uid',
                         icon='icon.png',
                         icontype='fileicon',
                         type='file')
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        self.assertEqual(item.attrib['uid'], 'uid')
        self.assertEqual(item.attrib['autocomplete'], 'autocomplete')
        self.assertEqual(item.attrib['valid'], 'yes')
        self.assertEqual(item.attrib['uid'], 'uid')
        title, subtitle, arg, icon = list(item)
        self.assertEqual(title.text, 'title')
        self.assertEqual(title.tag, 'title')
        self.assertEqual(subtitle.text, 'subtitle')
        self.assertEqual(subtitle.tag, 'subtitle')
        self.assertEqual(arg.text, 'arg')
        self.assertEqual(arg.tag, 'arg')
        self.assertEqual(icon.text, 'icon.png')
        self.assertEqual(icon.tag, 'icon')
        self.assertEqual(icon.attrib['type'], 'fileicon')

    def test_item_creation_no_optionals(self):
        """XML generation (no optionals)"""
        self.wf.add_item('title', valid=False)
        stdout = sys.stdout
        sio = StringIO()
        sys.stdout = sio
        self.wf.send_feedback()
        sys.stdout = stdout
        output = sio.getvalue()
        sio.close()
        # pprint(output)
        root = ET.fromstring(output)
        item = list(root)[0]
        for key in ['uid', 'arg', 'autocomplete']:
            self.assertFalse(key in item.attrib)
        self.assertEqual(item.attrib['valid'], 'no')
        title, subtitle = list(item)
        self.assertEqual(title.text, 'title')
        self.assertEqual(title.tag, 'title')
        self.assertEqual(subtitle.text, None)
        tags = [elem.tag for elem in list(item)]
        for tag in ['icon', 'arg']:
            self.assert_(tag not in tags)

    def test_additional_libs(self):
        """Additional libraries"""
        for path in self.libs:
            self.assert_(path in sys.path)
        self.assertEqual(sys.path[0:len(self.libs)], self.libs)
        import youcanimportme

    def test_info_plist(self):
        """info.plist"""
        self.assertEqual(self.wf.name, WORKFLOW_NAME)
        self.assertEqual(self.wf.bundleid, BUNDLE_ID)

    def test_args(self):
        """ARGV"""
        args = ['arg1', 'arg2', 'füntíme']
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode('utf-8') for s in args]
        wf = Workflow()
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_arg_normalisation(self):
        """ARGV normalisation"""
        def nfdme(s):
            return normalize('NFD', s)

        args = map(nfdme, ['arg1', 'arg2', 'füntíme'])
        oargs = sys.argv[:]
        sys.argv = [oargs[0]] + [s.encode('utf-8') for s in args]
        wf = Workflow(normalization='NFD')
        try:
            self.assertEqual(wf.args, args)
        finally:
            sys.argv = oargs[:]

    def test_magic_args(self):
        """Magic args"""
        # cache original sys.argv
        oargs = sys.argv[:]

        # # openlog
        # sys.argv = [oargs[0]] + [b'workflow:openlog']
        # try:
        #     wf = Workflow()
        #     wf.logger.debug('This is a test message')  # ensure log file exists
        #     with self.assertRaises(SystemExit):
        #         wf.args
        # finally:
        #     sys.argv = oargs[:]

        # delsettings
        sys.argv = [oargs[0]] + [b'workflow:delsettings']
        try:
            wf = Workflow(default_settings={'arg1': 'value1'})
            self.assertEqual(wf.settings['arg1'], 'value1')
            self.assertTrue(os.path.exists(wf.settings_path))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.settings_path))
        finally:
            sys.argv = oargs[:]

        # delcache
        sys.argv = [oargs[0]] + [b'workflow:delcache']

        def somedata():
            return {'arg1': 'value1'}

        try:
            wf = Workflow()
            cachepath = wf.cachefile('somedir')
            os.makedirs(cachepath)
            wf.cached_data('test', somedata)
            self.assertTrue(os.path.exists(wf.cachefile('test.cache')))
            with self.assertRaises(SystemExit):
                wf.args
            self.assertFalse(os.path.exists(wf.cachefile('test.cache')))
        finally:
            sys.argv = oargs[:]

    def test_logger(self):
        """Logger"""
        self.assert_(isinstance(self.wf.logger, logging.Logger))
        logger = logging.Logger('')
        self.wf.logger = logger
        self.assertEqual(self.wf.logger, logger)

    def test_cached_data(self):
        """Cached data stored"""
        data = {'key1': 'value1'}
        d = self.wf.cached_data('test', lambda: data, max_age=10)
        self.assertEqual(data, d)

    def test_cached_data_callback(self):
        """Cached data callback"""
        called = {'called': False}
        data = [1, 2, 3]

        def getdata():
            called['called'] = True
            return data

        d = self.wf.cached_data('test', getdata, max_age=10)
        self.assertEqual(d, data)
        self.assertTrue(called['called'])

    def test_cached_data_no_callback(self):
        """Cached data no callback"""
        d = self.wf.cached_data('nonexistent', None)
        self.assertEqual(d, None)

    def test_cache_expires(self):
        """Cached data expires"""
        data = ('hello', 'goodbye')
        called = {'called': False}

        def getdata():
            called['called'] = True
            return data

        d = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(called['called'])
        # should be loaded from cache
        called['called'] = False
        d2 = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d2, data)
        self.assertFalse(called['called'])
        # cache has expired
        time.sleep(1)
        # should be loaded from cache (no expiry)
        d3 = self.wf.cached_data('test', getdata, max_age=0)
        self.assertEqual(d3, data)
        self.assertFalse(called['called'])
        # should hit data func (cached data older than 1 sec)
        d4 = self.wf.cached_data('test', getdata, max_age=1)
        self.assertEqual(d4, data)
        self.assertTrue(called['called'])

    def test_cache_fresh(self):
        """Cached data is fresh"""
        data = 'This is my data'
        d = self.wf.cached_data('test', lambda: data, max_age=1)
        self.assertEqual(d, data)
        self.assertTrue(self.wf.cached_data_fresh('test', max_age=10))

    def test_cache_fresh_non_existent(self):
        """Non-existant cache data is not fresh"""
        self.assertEqual(self.wf.cached_data_fresh('popsicle', max_age=10000),
                         False)

    def test_keychain(self):
        """Save/get/delete password"""
        self.assertRaises(PasswordNotFound, self.wf.delete_password,
                          self.account)
        self.assertRaises(PasswordNotFound, self.wf.get_password, self.account)
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        self.assertEqual(self.wf.get_password(self.account, BUNDLE_ID),
                         self.password)
        # try to set same password
        self.wf.save_password(self.account, self.password)
        self.assertEqual(self.wf.get_password(self.account), self.password)
        # try to set different password
        self.wf.save_password(self.account, self.password2)
        self.assertEqual(self.wf.get_password(self.account), self.password2)
        # bad call to _call_security
        with self.assertRaises(KeychainError):
            self.wf._call_security('pants', BUNDLE_ID, self.account)

    def test_run_fails(self):
        """Run fails"""
        def cb(wf):
            self.assertEqual(wf, self.wf)
            raise ValueError('Have an error')

        self.wf.name  # cause info.plist to be parsed
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)
        # named after bundleid
        self.wf = Workflow()
        self.wf.bundleid
        ret = self.wf.run(cb)
        self.assertEqual(ret, 1)

    def test_run_okay(self):
        """Run okay"""
        def cb(wf):
            self.assertEqual(wf, self.wf)

        ret = self.wf.run(cb)
        self.assertEqual(ret, 0)

    def test_filter_all_rules(self):
        """Filter: all rules"""
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 ascending=True,
                                 match_on=MATCH_ALL)
        self.assertEqual(len(results), 8)
        # now with scores, rules
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 include_score=True,
                                 match_on=MATCH_ALL)
        self.assertEqual(len(results), 8)
        for item, score, rule in results:
            self.wf.logger.debug('{} : {}'.format(item, score))
            for value, r in self.search_items:
                if value == item[0]:
                    self.assertEqual(rule, r)
        # self.assertTrue(False)

    def test_filter_no_caps(self):
        """Filter: no caps"""
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 ascending=True,
                                 match_on=MATCH_ALL ^ MATCH_CAPITALS,
                                 include_score=True)
        self._print_results(results)
        for item, score, rule in results:
            self.assertNotEqual(rule, MATCH_CAPITALS)
        # self.assertEqual(len(results), 7)

    def test_filter_only_caps(self):
        """Filter: only caps"""
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 ascending=True,
                                 match_on=MATCH_CAPITALS,
                                 include_score=True)
        self._print_results(results)
        self.assertEqual(len(results), 1)

    def test_filter_max_results(self):
        """Filter: max results"""
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 ascending=True,
                                 max_results=4)
        self.assertEqual(len(results), 4)

    def test_filter_min_score(self):
        """Filter: min score"""
        results = self.wf.filter('test',
                                 self.search_items,
                                 key=lambda x: x[0],
                                 ascending=True,
                                 min_score=90,
                                 include_score=True)
        self.assertEqual(len(results), 6)

    def test_filter_folding(self):
        """Filter: diacritic folding"""
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key],
                                     min_score=90,
                                     include_score=True)
            self.assertEqual(len(results), 1)

    def test_filter_folding_off(self):
        """Filter: diacritic folding off"""
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key],
                                     min_score=90,
                                     include_score=True,
                                     fold_diacritics=False)
            self.assertEqual(len(results), 0)

    def test_filter_folding_force_on(self):
        """Filter: diacritic folding forced on"""
        self.wf.settings['__workflows_diacritic_folding'] = True
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key],
                                     min_score=90,
                                     include_score=True,
                                     fold_diacritics=False)
            self.assertEqual(len(results), 1)

    def test_filter_folding_force_off(self):
        """Filter: diacritic folding forced off"""
        self.wf.settings['__workflows_diacritic_folding'] = False
        for key, query in self.search_items_diacritics:
            results = self.wf.filter(query, [key],
                                     min_score=90,
                                     include_score=True)
            self.assertEqual(len(results), 0)

    def test_icons(self):
        """Icons"""
        import workflow
        for name in dir(workflow):
            if name.startswith('ICON_'):
                path = getattr(workflow, name)
                print(name, path)
                self.assert_(os.path.exists(path))

    def _print_results(self, results):
        """Print results of Workflow.filter"""
        for item, score, rule in results:
            print('{!r} (rule {}) : {}'.format(item[0], rule, score))