#!/usr/bin/env python import unittest from unittest.mock import MagicMock import string import random import sys import types from i3pystatus.core import util def get_random_string(length=6, chars=string.printable): return ''.join(random.choice(chars) for x in range(length)) def lchop(prefix, string): chopped = util.lchop(string, prefix) if string.startswith(prefix): assert len(chopped) == len(string) - len(prefix) assert not (prefix and chopped.startswith(prefix)) def lchop_test_generator(): cases = [ ('\x0b,S0I', "=t5Bk+\x0b'_;duq=9"), ('H1@\x0b_\t', '<&9<;Q(0L";^$7'), ('NWV#B0u', '~Q_wx5"$W\x0b[(T'), ('by=uHfZ>', 'AdytjC5-OUR\\'), ('/FD|^6=m3', 'l0:%ibW7*}S'), ('!.LsV90/$\x0b', 'nD}L&4]MGC'), ('S0zOIvbm:~L', 'jJ\\UGOMY<'), ("'aZ@,AA\x0b;Dmm", 'ZD +9}f;'), ('Ae;=SoHD(1VPJ', 'x\r)du,+'), ('ANDT \t>tZ1Iy9}', 'wpz%F`'), ('|w?>bv,t# BkGeE', '07mZR'), ('1MSN\x0c\r}Tu8r3uCe)', '\\I6-'), ('0;-!MVW4+\rz]J3@SQ', 'lvG'), ('Zi\x0b^5E?}a\\KJ(,O4n(', '8:'), ('I-X[~2sQigGLA_~ZD%G', '#'), (':xio!', 'v=ApOu\t_,}'), ('%bM%W\n', '7$wZ]gaL!U/%'), ('~\x0bILq>+', '[4}LhQ1zz1m?D&'), ('+"a)\x0c:4Q', '\niNQ+GY8+)UtQ\r#M'), ('R3PHi/[S*', 'J7fV19X(c^^J(9BAY+'), ('%Z?=m|3Wtr', 'O7K%GpQRJR%y}wIDq~H\\'), ('wyR0V*y^+D)', '>tsX)@%=u[83/hf0j?+xXV'), ('4}joZKr?j$u)', '\r\x0cje4QAha\\Y2]V\n`5[.TG~$0'), ('at%Y)%g5.*vKJ', 'MnucPcE!h;gkhAdI*`Jv$%dafJ'), ('XKH\\n\'gf4@"6`-', '#~:Nt[^9wtE.4\\@', "9)#qPuEF-A@ -DTE\r= (KgM\\h'i$XU"), ("<|(h|P9Wz9d9'u,M", '7d-A\nY{}5"\' !*gHHh`x0!B2Ox?yeKb\x0b'), ('bV?:f\x0b#HDhuwSvys3', ";\r,L![\x0cU7@ne@'?[*&VVI,[}pHM\nB65@LfE16VJPw=r'zU\x0bzWj@"), ('^|j7N!mV0o(?*1>p?dy', '\\ZdA&:\t\x0b:8\t|7.Kl,oHw-\x0cS\nwZlND~uC@le`Sm'), ] for prefix, string in cases: yield lchop, prefix, prefix+string yield lchop, prefix, string yield lchop, string, string yield lchop, string, prefix yield lchop, "", string yield lchop, prefix, "" yield lchop, prefix+prefix, prefix+prefix+prefix+string def partition(iterable, limit, assrt): partitions = util.partition(iterable, limit) partitions = [sorted(partition) for partition in partitions] for item in assrt: assert sorted(item) in partitions def partition_test_generator(): cases = [ ([1, 2, 3, 4], 3, [[1,2], [3], [4]]), ([2, 1, 3, 4], 3, [[1,2], [3], [4]]), ([0.33, 0.45, 0.89], 1, [[0.33, 0.45, 0.89]]), ([], 10, []), ] for iterable, limit, assrt in cases: yield partition, iterable, limit, assrt def popwhile(iterable, predicate, assrt): assert list(util.popwhile(predicate, iterable)) == assrt def popwhile_test_generator(): cases = [ ([1, 2, 3, 4], lambda x: x < 2, []), ([1, 2, 3, 4], lambda x: x < 5 and x > 2, [4, 3]), ([1, 2, 3, 4], lambda x: x == 4, [4]), ([1, 2, 3, 4], lambda x: True, [4, 3, 2, 1]), ([1, 2], lambda x: False, []), ] for iterable, predicate, assrt in cases: yield popwhile, iterable, predicate, assrt def keyconstraintdict_missing(valid, required, feedkeys, assrt_missing): kcd = util.KeyConstraintDict(valid_keys=valid, required_keys=required) kcd.update(dict.fromkeys(feedkeys)) assert kcd.missing() == set(assrt_missing) def keyconstraintdict_missing_test_generator(): cases = [ # ( valid, required, feed, missing ) (("foo", "bar", "baz"), ("foo",), ("bar",), ("foo",)), (("foo", "bar", "baz"), ("foo",), tuple(), ("foo",)), (("foo", "bar", "baz"), ("bar", "baz"), ("bar", "baz"), tuple()), (("foo", "bar", "baz"), ("bar", "baz"), ("bar", "foo", "baz"), tuple()), ] for valid, required, feed, missing in cases: yield keyconstraintdict_missing, valid, required, feed, missing class ModuleListTests(unittest.TestCase): class ModuleBase: pass def setUp(self): self.status_handler = MagicMock() self.ml = util.ModuleList(self.status_handler, self.ModuleBase) def test_append_simple(self): module = self.ModuleBase() module.registered = MagicMock() self.ml.append(module) module.registered.assert_called_with(self.status_handler) def _create_module_class(self, name, bases=None): if not bases: bases = (self.ModuleBase,) return type(name, bases, { "registered": MagicMock(), "__init__": MagicMock(return_value=None), }) def test_append_class_instanciation(self): module_class = self._create_module_class("module_class") self.ml.append(module_class) module_class.__init__.assert_called_with() module_class.registered.assert_called_with(self.status_handler) def test_append_module(self): pymod = types.ModuleType("test_mod") pymod.some_class = self._create_module_class("some_class") pymod.some_class.__module__ = "test_mod" self.ml.append(pymod) pymod.some_class.__init__.assert_called_with() pymod.some_class.registered.assert_called_with(self.status_handler) def test_append_module2(self): # Here we test if imported classes are ignored as they should pymod = types.ModuleType("test_mod") pymod.some_class = self._create_module_class("some_class") pymod.some_class.__module__ = "other_module" with self.assertRaises(IndexError): self.ml.append(pymod) assert not pymod.some_class.__init__.called assert not pymod.some_class.registered.called def test_append_class_inheritance(self): in_between = self._create_module_class("in_between") cls = self._create_module_class("cls", (in_between,)) self.ml.append(cls) cls.__init__.assert_called_with() cls.registered.assert_called_with(self.status_handler) class PrefixedKeyDictTests(unittest.TestCase): def test_no_prefix(self): dict = util.PrefixedKeyDict("") dict["foo"] = None dict["bar"] = 42 assert dict["foo"] == None assert dict["bar"] == 42 def test_prefix(self): dict = util.PrefixedKeyDict("pfx_") dict["foo"] = None dict["bar"] = 42 assert dict["pfx_foo"] == None assert dict["pfx_bar"] == 42 def test_dict(self): d = util.PrefixedKeyDict("pfx_") d["foo"] = None d["bar"] = 42 realdict = dict(d) assert realdict["pfx_foo"] == None assert realdict["pfx_bar"] == 42 class KeyConstraintDictAdvancedTests(unittest.TestCase): def test_invalid_1(self): kcd = util.KeyConstraintDict(valid_keys=tuple(), required_keys=tuple()) with self.assertRaises(KeyError): kcd["invalid"] = True def test_invalid_2(self): kcd = util.KeyConstraintDict(valid_keys=("foo", "bar"), required_keys=tuple()) with self.assertRaises(KeyError): kcd["invalid"] = True def test_incomplete_iteration(self): kcd = util.KeyConstraintDict(valid_keys=("foo", "bar"), required_keys=("foo",)) with self.assertRaises(util.KeyConstraintDict.MissingKeys): for x in kcd: pass def test_completeness(self): kcd = util.KeyConstraintDict(valid_keys=("foo", "bar"), required_keys=("foo",)) kcd["foo"] = False for x in kcd: pass assert kcd.missing() == set() def test_remove_required(self): kcd = util.KeyConstraintDict(valid_keys=("foo", "bar"), required_keys=("foo",)) kcd["foo"] = None assert kcd.missing() == set() del kcd["foo"] assert kcd.missing() == set(["foo"]) def test_set_twice(self): kcd = util.KeyConstraintDict(valid_keys=("foo", "bar"), required_keys=("foo",)) kcd["foo"] = 1 kcd["foo"] = 2 assert kcd.missing() == set() del kcd["foo"] assert kcd.missing() == set(["foo"]) class FormatPTests(unittest.TestCase): def test_escaping(self): assert util.formatp("[razamba \[ mabe \]]") == "razamba [ mabe ]" def test_numerical(self): assert util.formatp("[{t} - [schmuh {x}]]", t=1, x=2) == "1 - schmuh 2" assert util.formatp("[{t} - [schmuh {x}]]", t=1, x=0) == "1 - " assert util.formatp("[{t} - [schmuh {x}]]", t=0, x=0) == "" def test_nesting(self): s = "[[{artist} - ]{album} - ]{title}" assert util.formatp(s, title="Black rose") == "Black rose" assert util.formatp(s, artist="In Flames", title="Gyroscope") == "Gyroscope" assert util.formatp(s, artist="SOAD", album="Toxicity", title="Science") == "SOAD - Toxicity - Science" assert util.formatp(s, album="Toxicity", title="Science") == "Toxicity - Science" def test_bare(self): assert util.formatp("{foo} blar", foo="bar") == "bar blar" def test_presuffix(self): assert util.formatp("ALINA[{title} schnacke]KOMMAHER", title="") == "ALINAKOMMAHER" assert util.formatp("grml[{title}]") == "grml" assert util.formatp("[{t}]grml") == "grml" def test_side_by_side(self): s = "{status} [{artist} / [{album} / ]]{title}[ {song_elapsed}/{song_length}]" assert util.formatp(s, status="▷", title="Only For The Weak", song_elapsed="1:41", song_length="4:55") == "▷ Only For The Weak 1:41/4:55" assert util.formatp(s, status="", album="Foo", title="Die, Die, Crucified", song_elapsed="2:52") == " Die, Die, Crucified" assert util.formatp("[[{a}][{b}]]", b=1) == "1" def test_complex_field(self): class NS: pass obj = NS() obj.attr = "bar" s = "[{a:.3f} m]{obj.attr}" assert util.formatp(s, a=3.14123456789, obj=obj) == "3.141 mbar" assert util.formatp(s, a=0.0, obj=obj) == "bar"