summary refs log tree commit diff stats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/tc_commandlist.py100
-rw-r--r--test/tc_direction.py88
-rw-r--r--test/tc_newkeys.py605
-rw-r--r--test/tc_ui.py2
4 files changed, 694 insertions, 101 deletions
diff --git a/test/tc_commandlist.py b/test/tc_commandlist.py
deleted file mode 100644
index 9af2cf05..00000000
--- a/test/tc_commandlist.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-if __name__ == '__main__': from __init__ import init; init()
-
-from unittest import TestCase, main
-from ranger.container.commandlist import CommandList as CL
-
-class Test(TestCase):
-	def assertKeyError(self, obj, key):
-		self.assertRaises(KeyError, obj.__getitem__, key)
-
-	def test_commandist(self):
-		cl = CL()
-		fnc = lambda arg: 1
-		fnc2 = lambda arg: 2
-		dmy = cl.dummy_object
-
-		cl.bind(fnc, 'aaaa')
-		cl.rebuild_paths()
-
-		self.assertEqual(dmy, cl['a'])
-		self.assertEqual(dmy, cl['aa'])
-		self.assertEqual(dmy, cl['aaa'])
-		self.assertEqual(fnc, cl['aaaa'].execute)
-		self.assertKeyError(cl, 'aabb')
-		self.assertKeyError(cl, 'aaaaa')
-
-		cl.bind(fnc, 'aabb')
-		cl.rebuild_paths()
-
-		self.assertEqual(dmy, cl['a'])
-		self.assertEqual(dmy, cl['aa'])
-		self.assertEqual(dmy, cl['aab'])
-		self.assertEqual(fnc, cl['aabb'].execute)
-		self.assertEqual(dmy, cl['aaa'])
-		self.assertEqual(fnc, cl['aaaa'].execute)
-
-		cl.unbind('aabb')
-		cl.rebuild_paths()
-
-		self.assertEqual(dmy, cl['a'])
-		self.assertEqual(dmy, cl['aa'])
-		self.assertKeyError(cl, 'aabb')
-		self.assertKeyError(cl, 'aab')
-		self.assertEqual(dmy, cl['aaa'])
-		self.assertEqual(fnc, cl['aaaa'].execute)
-
-		# Hints work different now.  Since a rework of this system
-		# is planned anyway, there is no need to fix the test.
-		# hint_text = 'some tip blablablba'
-		# cl.hint(hint_text, 'aa')
-		# cl.rebuild_paths()
-
-		self.assertEqual(dmy, cl['a'])
-		# self.assertEqual(hint_text, cl['aa'].text)
-		self.assertEqual(dmy, cl['aaa'])
-		self.assertEqual(fnc, cl['aaaa'].execute)
-
-		# ------------------------ test aliases
-		cl.alias('aaaa', 'cc')
-		cl.rebuild_paths()
-
-		self.assertEqual(dmy, cl['c'])
-		self.assertEqual(cl['cc'].execute, cl['aaaa'].execute)
-
-		cl.bind(fnc2, 'aaaa')
-		cl.rebuild_paths()
-
-		self.assertEqual(cl['cc'].execute, cl['aaaa'].execute)
-
-		cl.unbind('cc')
-		cl.rebuild_paths()
-
-		self.assertEqual(fnc2, cl['aaaa'].execute)
-		self.assertKeyError(cl, 'cc')
-
-		# ----------------------- test clearing
-		cl.clear()
-		self.assertKeyError(cl, 'a')
-		self.assertKeyError(cl, 'aa')
-		self.assertKeyError(cl, 'aaa')
-		self.assertKeyError(cl, 'aaaa')
-		self.assertKeyError(cl, 'aab')
-		self.assertKeyError(cl, 'aabb')
-
-
-if __name__ == '__main__': main()
diff --git a/test/tc_direction.py b/test/tc_direction.py
new file mode 100644
index 00000000..124a7001
--- /dev/null
+++ b/test/tc_direction.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if __name__ == '__main__': from __init__ import init; init()
+
+import unittest
+from ranger.ext.direction import Direction
+from ranger.ext.openstruct import OpenStruct
+
+class TestDirections(unittest.TestCase):
+	def test_symmetry(self):
+		d1 = Direction(right=4, down=7, relative=True)
+		d2 = Direction(left=-4, up=-7, absolute=False)
+
+		def subtest(d):
+			self.assertEqual(4, d.right())
+			self.assertEqual(7, d.down())
+			self.assertEqual(-4, d.left())
+			self.assertEqual(-7, d.up())
+			self.assertEqual(True, d.relative())
+			self.assertEqual(False, d.absolute())
+
+			self.assertTrue(d.horizontal())
+			self.assertTrue(d.vertical())
+
+		subtest(d1)
+		subtest(d2)
+
+	def test_conflicts(self):
+		d3 = Direction(right=5, left=2, up=3, down=6,
+				absolute=True, relative=True)
+		self.assertEqual(d3.right(), -d3.left())
+		self.assertEqual(d3.left(), -d3.right())
+		self.assertEqual(d3.up(), -d3.down())
+		self.assertEqual(d3.down(), -d3.up())
+		self.assertEqual(d3.absolute(), not d3.relative())
+		self.assertEqual(d3.relative(), not d3.absolute())
+
+	def test_copy(self):
+		d = Direction(right=5)
+		c = d.copy()
+		self.assertEqual(c.right(), d.right())
+		d['right'] += 3
+		self.assertNotEqual(c.right(), d.right())
+		c['right'] += 3
+		self.assertEqual(c.right(), d.right())
+
+		self.assertFalse(d.vertical())
+		self.assertTrue(d.horizontal())
+
+#	Doesn't work in python2?
+#	def test_duck_typing(self):
+#		dct = dict(right=7, down=-3)
+#		self.assertEqual(-7, Direction.left(dct))
+#		self.assertEqual(3, Direction.up(dct))
+
+	def test_move(self):
+		d = Direction(pages=True)
+		self.assertEqual(3, d.move(direction=3))
+		self.assertEqual(5, d.move(direction=3, current=2))
+		self.assertEqual(15, d.move(direction=3, pagesize=5))
+		self.assertEqual(9, d.move(direction=3, pagesize=5, maximum=10))
+		self.assertEqual(18, d.move(direction=9, override=2))
+		d2 = Direction(absolute=True)
+		self.assertEqual(5, d2.move(direction=9, override=5))
+
+	def test_select(self):
+		d = Direction(down=3)
+		lst = list(range(100))
+		self.assertEqual((6, [3,4,5]), d.select(current=3, pagesize=10, override=None, lst=lst))
+		d = Direction(down=3, pages=True)
+		self.assertEqual((9, [3,4,5,6,7,8]), d.select(current=3, pagesize=2, override=None, lst=lst))
+
+if __name__ == '__main__':
+	unittest.main()
+
diff --git a/test/tc_newkeys.py b/test/tc_newkeys.py
new file mode 100644
index 00000000..8aa043ae
--- /dev/null
+++ b/test/tc_newkeys.py
@@ -0,0 +1,605 @@
+# coding=utf-8
+# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+if __name__ == '__main__': from __init__ import init; init()
+from unittest import TestCase, main
+
+from ranger.ext.tree import Tree
+from ranger.container.keymap import *
+from ranger.container.keybuffer import KeyBuffer
+from ranger.ext.keybinding_parser import parse_keybinding
+
+import sys
+
+def simulate_press(self, string):
+	for char in parse_keybinding(string):
+		self.add(char)
+		if self.done:
+			return self.command
+		if self.failure:
+			break
+	return self.command
+
+class PressTestCase(TestCase):
+	"""Some useful methods for the actual test"""
+	def _mkpress(self, keybuffer, _=0):
+		def press(keys):
+			keybuffer.clear()
+			match = simulate_press(keybuffer, keys)
+			self.assertFalse(keybuffer.failure,
+					"parsing keys '"+keys+"' did fail!")
+			self.assertTrue(keybuffer.done,
+					"parsing keys '"+keys+"' did not complete!")
+			arg = CommandArgs(None, None, keybuffer)
+			self.assert_(match.function, "No function found! " + \
+					str(match.__dict__))
+			return match.function(arg)
+		return press
+
+	def assertPressFails(self, kb, keys):
+		kb.clear()
+		simulate_press(kb, keys)
+		self.assertTrue(kb.failure, "Keypress did not fail as expected")
+		kb.clear()
+
+	def assertPressIncomplete(self, kb, keys):
+		kb.clear()
+		simulate_press(kb, keys)
+		self.assertFalse(kb.failure, "Keypress failed, expected incomplete")
+		self.assertFalse(kb.done, "Keypress done which was unexpected")
+		kb.clear()
+
+class Test(PressTestCase):
+	"""The test cases"""
+	def test_passive_action(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		def n(value):
+			"""return n or value"""
+			def fnc(arg=None):
+				if arg is None or arg.n is None:
+					return value
+				return arg.n
+			return fnc
+
+		km.map('ppp', n(5))
+		km.map('pp<bg>', n(8))
+		km.map('pp<dir>', n(2))
+		directions.map('j', dir=Direction(down=1))
+
+		press = self._mkpress(kb, km)
+		self.assertEqual(5, press('ppp'))
+		self.assertEqual(3, press('3ppp'))
+
+		self.assertEqual(2, press('ppj'))
+
+		kb.clear()
+		match = simulate_press(kb, 'pp')
+		args = CommandArgs(0, 0, kb)
+		self.assert_(match)
+		self.assert_(match.function)
+		self.assertEqual(8, match.function(args))
+
+	def test_translate_keys(self):
+		def test(string, *args):
+			if not args:
+				args = (string, )
+			self.assertEqual(ordtuple(*args), tuple(parse_keybinding(string)))
+
+		def ordtuple(*args):
+			lst = []
+			for arg in args:
+				if isinstance(arg, str):
+					lst.extend(ord(c) for c in arg)
+				else:
+					lst.append(arg)
+			return tuple(lst)
+
+		# 1 argument means: assume nothing is translated.
+		test('k')
+		test('kj')
+		test('k<dir>', 'k', DIRKEY)
+		test('k<ANY>z<any>', 'k', ANYKEY, 'z', ANYKEY)
+		test('k<anY>z<dir>', 'k', ANYKEY, 'z', DIRKEY)
+		test('<cr>', "\n")
+		test('<tab><tab><cr>', "\t\t\n")
+		test('<')
+		test('>')
+		test('<C-a>', 1)
+		test('<C-b>', 2)
+		for i in range(1, 26):
+			test('<C-' + chr(i+ord('a')-1) + '>', i)
+		test('<A-x>', 27, ord('x'))
+		test('<a-o>', 27, ord('o'))
+		test('k<a')
+		test('k<anz>')
+		test('k<a<nz>')
+		test('k<a<nz>')
+		test('k<a<>nz>')
+		test('>nz>')
+
+	def test_alias(self):
+		def add_dirs(arg):
+			return sum(dir.down() for dir in arg.directions)
+		def return5(_):
+			return 5
+
+		directions = KeyMap()
+		directions.map('j', dir=Direction(down=1))
+		directions.map('k', dir=Direction(down=-1))
+		directions.map('<CR>', alias='j')
+		directions.map('@', alias='<CR>')
+
+		base = KeyMap()
+		base.map('a<dir>', add_dirs)
+		base.map('b<dir>', add_dirs)
+		base.map('x<dir>x<dir>', add_dirs)
+		base.map('f', return5)
+		base.map('yy', alias='y')
+		base.map('!', alias='!')
+
+		other = KeyMap()
+		other.map('b<dir>b<dir>', alias='x<dir>x<dir>')
+		other.map('c<dir>', add_dirs)
+		other.map('g', alias='f')
+
+		km = base.merge(other, copy=True)
+		kb = KeyBuffer(km, directions)
+
+		press = self._mkpress(kb, km)
+
+		self.assertEqual(1, press('aj'))
+		self.assertEqual(2, press('bjbj'))
+		self.assertEqual(1, press('cj'))
+		self.assertEqual(1, press('c<CR>'))
+
+		self.assertEqual(5, press('f'))
+		self.assertEqual(5, press('g'))
+		self.assertEqual(press('c<CR>'), press('c@'))
+		self.assertEqual(press('c<CR>'), press('c@'))
+		self.assertEqual(press('c<CR>'), press('c@'))
+
+		for n in range(1, 10):
+			self.assertPressIncomplete(kb, 'y' * n)
+
+		for n in range(1, 5):
+			self.assertPressFails(kb, '!' * n)
+
+	def test_tree(self):
+		t = Tree()
+		t.set('abcd', "Yes")
+		self.assertEqual("Yes", t.traverse('abcd'))
+		self.assertRaises(KeyError, t.traverse, 'abcde')
+		self.assertRaises(KeyError, t.traverse, 'xyz')
+		self.assert_(isinstance(t.traverse('abc'), Tree))
+
+		t2 = Tree()
+		self.assertRaises(KeyError, t2.set, 'axy', "Lol", force=False)
+		t2.set('axx', 'ololol')
+		t2.set('axyy', "Lol")
+		self.assertEqual("Yes", t.traverse('abcd'))
+		self.assertRaises(KeyError, t2.traverse, 'abcd')
+		self.assertEqual("Lol", t2.traverse('axyy'))
+		self.assertEqual("ololol", t2.traverse('axx'))
+
+		t2.unset('axyy')
+		self.assertEqual("ololol", t2.traverse('axx'))
+		self.assertRaises(KeyError, t2.traverse, 'axyy')
+		self.assertRaises(KeyError, t2.traverse, 'axy')
+
+		t2.unset('a')
+		self.assertRaises(KeyError, t2.traverse, 'abcd')
+		self.assertRaises(KeyError, t2.traverse, 'a')
+		self.assert_(t2.empty())
+
+	def test_merge_trees(self):
+		def makeTreeA():
+			t = Tree()
+			t.set('aaaX', 1)
+			t.set('aaaY', 2)
+			t.set('aaaZ', 3)
+			t.set('bbbA', 11)
+			t.set('bbbB', 12)
+			t.set('bbbC', 13)
+			t.set('bbbD', 14)
+			t.set('bP', 21)
+			t.set('bQ', 22)
+			return t
+
+		def makeTreeB():
+			u = Tree()
+			u.set('aaaX', 0)
+			u.set('bbbC', 'Yes')
+			u.set('bbbD', None)
+			u.set('bbbE', 15)
+			u.set('bbbF', 16)
+			u.set('bQ', 22)
+			u.set('bR', 23)
+			u.set('ffff', 1337)
+			return u
+
+		# test 1
+		t = Tree('a')
+		u = Tree('b')
+		merged = t.merge(u, copy=True)
+		self.assertEqual('b', merged._tree)
+
+		# test 2
+		t = Tree('a')
+		u = makeTreeA()
+		merged = t.merge(u, copy=True)
+		self.assertEqual(u._tree, merged._tree)
+
+		# test 3
+		t = makeTreeA()
+		u = makeTreeB()
+		v = t.merge(u, copy=True)
+
+		self.assertEqual(0, v['aaaX'])
+		self.assertEqual(2, v['aaaY'])
+		self.assertEqual(3, v['aaaZ'])
+		self.assertEqual(11, v['bbbA'])
+		self.assertEqual('Yes', v['bbbC'])
+		self.assertEqual(None, v['bbbD'])
+		self.assertEqual(15, v['bbbE'])
+		self.assertEqual(16, v['bbbF'])
+		self.assertRaises(KeyError, t.__getitem__, 'bbbG')
+		self.assertEqual(21, v['bP'])
+		self.assertEqual(22, v['bQ'])
+		self.assertEqual(23, v['bR'])
+		self.assertEqual(1337, v['ffff'])
+
+		# merge shouldn't be destructive
+		self.assertEqual(makeTreeA()._tree, t._tree)
+		self.assertEqual(makeTreeB()._tree, u._tree)
+
+		v['fff'].replace('Lolz')
+		self.assertEqual('Lolz', v['fff'])
+
+		v['aaa'].replace('Very bad')
+		v.plow('qqqqqqq').replace('eww.')
+
+		self.assertEqual(makeTreeA()._tree, t._tree)
+		self.assertEqual(makeTreeB()._tree, u._tree)
+
+	def test_add(self):
+		c = KeyMap()
+		c.map('aa', 'b', lambda *_: 'lolz')
+		self.assert_(c['aa'].function(), 'lolz')
+		@c.map('a', 'c')
+		def test():
+			return 5
+		self.assert_(c['b'].function(), 'lolz')
+		self.assert_(c['c'].function(), 5)
+		self.assert_(c['a'].function(), 5)
+
+	def test_quantifier(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		def n(value):
+			"""return n or value"""
+			def fnc(arg=None):
+				if arg is None or arg.n is None:
+					return value
+				return arg.n
+			return fnc
+		km.map('p', n(5))
+		press = self._mkpress(kb, km)
+		self.assertEqual(5, press('p'))
+		self.assertEqual(3, press('3p'))
+		self.assertEqual(6223, press('6223p'))
+
+	def test_direction(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		directions.map('j', dir=Direction(down=1))
+		directions.map('k', dir=Direction(down=-1))
+		def nd(arg):
+			""" n * direction """
+			n = arg.n is None and 1 or arg.n
+			dir = arg.direction is None and Direction(down=1) \
+					or arg.direction
+			return n * dir.down()
+		km.map('d<dir>', nd)
+		km.map('dd', func=nd)
+
+		press = self._mkpress(kb, km)
+
+		self.assertPressIncomplete(kb, 'd')
+		self.assertEqual(  1, press('dj'))
+		self.assertEqual(  3, press('3ddj'))
+		self.assertEqual( 15, press('3d5j'))
+		self.assertEqual(-15, press('3d5k'))
+		# supporting this kind of key combination would be too confusing:
+		# self.assertEqual( 15, press('3d5d'))
+		self.assertEqual(  3, press('3dd'))
+		self.assertEqual(  33, press('33dd'))
+		self.assertEqual(  1, press('dd'))
+
+		km.map('x<dir>', nd)
+		km.map('xxxx', func=nd)
+
+		self.assertEqual(1, press('xxxxj'))
+		self.assertEqual(1, press('xxxxjsomeinvalitchars'))
+
+		# these combinations should break:
+		self.assertPressFails(kb, 'xxxj')
+		self.assertPressFails(kb, 'xxj')
+		self.assertPressFails(kb, 'xxkldfjalksdjklsfsldkj')
+		self.assertPressFails(kb, 'xyj')
+		self.assertPressIncomplete(kb, 'x') # direction missing
+
+	def test_any_key(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		directions.map('j', dir=Direction(down=1))
+		directions.map('k', dir=Direction(down=-1))
+
+		directions.map('g<any>', dir=Direction(down=-1))
+
+		def cat(arg):
+			n = arg.n is None and 1 or arg.n
+			return ''.join(chr(c) for c in arg.matches) * n
+
+		km.map('return<any>', cat)
+		km.map('cat4<any><any><any><any>', cat)
+		km.map('foo<dir><any>', cat)
+
+		press = self._mkpress(kb, km)
+
+		self.assertEqual('x', press('returnx'))
+		self.assertEqual('abcd', press('cat4abcd'))
+		self.assertEqual('abcdabcd', press('2cat4abcd'))
+		self.assertEqual('55555', press('5return5'))
+
+		self.assertEqual('x', press('foojx'))
+		self.assertPressFails(kb, 'fooggx')  # ANYKEY forbidden in DIRECTION
+
+		km.map('<any>', lambda _: Ellipsis)
+		self.assertEqual('x', press('returnx'))
+		self.assertEqual('abcd', press('cat4abcd'))
+		self.assertEqual(Ellipsis, press('2cat4abcd'))
+		self.assertEqual(Ellipsis, press('5return5'))
+		self.assertEqual(Ellipsis, press('g'))
+		self.assertEqual(Ellipsis, press('ß'))
+		self.assertEqual(Ellipsis, press('ア'))
+		self.assertEqual(Ellipsis, press('9'))
+
+	def test_multiple_directions(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		directions.map('j', dir=Direction(down=1))
+		directions.map('k', dir=Direction(down=-1))
+
+		def add_dirs(arg):
+			return sum(dir.down() for dir in arg.directions)
+
+		km.map('x<dir>y<dir>', add_dirs)
+		km.map('four<dir><dir><dir><dir>', add_dirs)
+
+		press = self._mkpress(kb, km)
+
+		self.assertEqual(2, press('xjyj'))
+		self.assertEqual(0, press('fourjkkj'))
+		self.assertEqual(2, press('four2j4k2j2j'))
+		self.assertEqual(10, press('four1j2j3j4j'))
+		self.assertEqual(10, press('four1j2j3j4jafslkdfjkldj'))
+
+	def test_corruptions(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		press = self._mkpress(kb, km)
+		directions.map('j', dir=Direction(down=1))
+		directions.map('k', dir=Direction(down=-1))
+		km.map('xxx', lambda _: 1)
+
+		self.assertEqual(1, press('xxx'))
+
+		# corrupt the tree
+		tup = tuple(parse_keybinding('xxx'))
+		x = ord('x')
+		km._tree[x][x][x] = "Boo"
+
+		self.assertPressFails(kb, 'xxy')
+		self.assertPressFails(kb, 'xzy')
+		self.assertPressIncomplete(kb, 'xx')
+		self.assertPressIncomplete(kb, 'x')
+		if not sys.flags.optimize:
+			self.assertRaises(AssertionError, simulate_press, kb, 'xxx')
+		kb.clear()
+
+	def test_directions_as_functions(self):
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		press = self._mkpress(kb, km)
+
+		def move(arg):
+			return arg.direction.down()
+
+		directions.map('j', dir=Direction(down=1))
+		directions.map('s', alias='j')
+		directions.map('k', dir=Direction(down=-1))
+		km.map('<dir>', func=move)
+
+		self.assertEqual(1, press('j'))
+		self.assertEqual(1, press('j'))
+		self.assertEqual(1, press('j'))
+		self.assertEqual(1, press('j'))
+		self.assertEqual(1, press('j'))
+		self.assertEqual(1, press('s'))
+		self.assertEqual(1, press('s'))
+		self.assertEqual(1, press('s'))
+		self.assertEqual(1, press('s'))
+		self.assertEqual(1, press('s'))
+		self.assertEqual(-1, press('k'))
+		self.assertEqual(-1, press('k'))
+		self.assertEqual(-1, press('k'))
+
+		km.map('k', func=lambda _: 'love')
+
+		self.assertEqual(1, press('j'))
+		self.assertEqual('love', press('k'))
+
+		self.assertEqual(1, press('40j'))
+		self.assertEqual(40, kb.quant)
+
+		km.map('<dir><dir><any><any>', func=move)
+
+		self.assertEqual(1, press('40jkhl'))
+		self.assertEqual(40, kb.quant)
+
+	def test_tree_deep_copy(self):
+		t = Tree()
+		s = t.plow('abcd')
+		s.replace('X')
+		u = t.copy()
+		self.assertEqual(t._tree, u._tree)
+		s = t.traverse('abc')
+		s.replace('Y')
+		self.assertNotEqual(t._tree, u._tree)
+
+	def test_keymanager(self):
+		def func(arg):
+			return 5
+		def getdown(arg):
+			return arg.direction.down()
+
+		buffer = KeyBuffer(None, None)
+		press = self._mkpress(buffer)
+		keymanager = KeyManager(buffer, ['foo', 'bar'])
+
+		map = keymanager.get_context('foo')
+		map('a', func)
+		map('b', func)
+		map = keymanager.get_context('bar')
+		map('c', func)
+		map('<dir>', getdown)
+
+		keymanager.dir('foo', 'j', down=1)
+		keymanager.dir('bar', 'j', down=1)
+
+		keymanager.use_context('foo')
+		self.assertEqual(5, press('a'))
+		self.assertEqual(5, press('b'))
+		self.assertPressFails(buffer, 'c')
+
+		keymanager.use_context('bar')
+		self.assertPressFails(buffer, 'a')
+		self.assertPressFails(buffer, 'b')
+		self.assertEqual(5, press('c'))
+		self.assertEqual(1, press('j'))
+		keymanager.use_context('foo')
+		keymanager.use_context('foo')
+		keymanager.use_context('foo')
+		keymanager.use_context('bar')
+		keymanager.use_context('foo')
+		keymanager.use_context('bar')
+		keymanager.use_context('bar')
+		self.assertEqual(1, press('j'))
+
+	def test_alias_to_direction(self):
+		def func(arg):
+			return arg.direction.down()
+
+		km = KeyMapWithDirections()
+		kb = KeyBuffer(km, km.directions)
+		press = self._mkpress(kb)
+
+		km.map('<dir>', func)
+		km.map('d<dir>', func)
+		km.dir('j', down=42)
+		km.dir('k', alias='j')
+		self.assertEqual(42, press('j'))
+
+		km.dir('o', alias='j')
+		km.dir('ick', alias='j')
+		self.assertEqual(42, press('o'))
+		self.assertEqual(42, press('dj'))
+		self.assertEqual(42, press('dk'))
+		self.assertEqual(42, press('do'))
+		self.assertEqual(42, press('dick'))
+		self.assertPressFails(kb, 'dioo')
+
+	def test_both_directory_and_any_key(self):
+		def func(arg):
+			return arg.direction.down()
+		def func2(arg):
+			return "yay"
+
+		km = KeyMap()
+		directions = KeyMap()
+		kb = KeyBuffer(km, directions)
+		press = self._mkpress(kb)
+
+		km.map('abc<dir>', func)
+		directions.map('j', dir=Direction(down=42))
+		self.assertEqual(42, press('abcj'))
+
+		km.unmap('abc<dir>')
+
+		km.map('abc<any>', func2)
+		self.assertEqual("yay", press('abcd'))
+
+		km.map('abc<dir>', func)
+
+		km.map('abc<any>', func2)
+		self.assertEqual("yay", press('abcd'))
+
+	def test_map_collision(self):
+		def add_dirs(arg):
+			return sum(dir.down() for dir in arg.directions)
+		def return5(_):
+			return 5
+
+
+		directions = KeyMap()
+		directions.map('gg', dir=Direction(down=1))
+
+
+		km = KeyMap()
+		km.map('gh', return5)
+		km.map('agh', return5)
+		km.map('a<dir>', add_dirs)
+
+		kb = KeyBuffer(km, directions)
+		press = self._mkpress(kb, km)
+
+		self.assertEqual(5, press('gh'))
+		self.assertEqual(5, press('agh'))
+#		self.assertPressFails(kb, 'agh')
+		self.assertEqual(1, press('agg'))
+
+	def test_keymap_with_dir(self):
+		def func(arg):
+			return arg.direction.down()
+
+		km = KeyMapWithDirections()
+		kb = KeyBuffer(km, km.directions)
+
+		press = self._mkpress(kb)
+
+		km.map('abc<dir>', func)
+		km.dir('j', down=42)
+		self.assertEqual(42, press('abcj'))
+
+if __name__ == '__main__': main()
diff --git a/test/tc_ui.py b/test/tc_ui.py
index affec907..3c659459 100644
--- a/test/tc_ui.py
+++ b/test/tc_ui.py
@@ -28,7 +28,7 @@ class Test(unittest.TestCase):
 	def setUp(self):
 
 		self.fm = Fake()
-		self.ui = ui.UI(env=Fake(), fm=self.fm, commandlist=Fake())
+		self.ui = ui.UI(env=Fake(), fm=self.fm)
 
 		def fakesetup():
 			self.ui.widget = Fake()