コード例 #1
0
 def test_dictionary_enabled(self):
     dc = StenoDictionaryCollection()
     d1 = StenoDictionary()
     d1.path = 'd1'
     d1[('TEFT', )] = 'test1'
     d1[('TEFGT', )] = 'Testing'
     d2 = StenoDictionary()
     d2[('TEFT', )] = 'test2'
     d2[('TEFT', '-G')] = 'Testing'
     d2.path = 'd2'
     dc.set_dicts([d2, d1])
     self.assertEqual(dc.lookup(('TEFT', )), 'test2')
     self.assertEqual(dc.raw_lookup(('TEFT', )), 'test2')
     self.assertEqual(dc.casereverse_lookup('testing'), ['Testing'])
     self.assertCountEqual(dc.reverse_lookup('Testing'), [('TEFGT', ),
                                                          ('TEFT', '-G')])
     d2.enabled = False
     self.assertEqual(dc.lookup(('TEFT', )), 'test1')
     self.assertEqual(dc.raw_lookup(('TEFT', )), 'test1')
     self.assertEqual(dc.casereverse_lookup('testing'), ['Testing'])
     self.assertCountEqual(dc.reverse_lookup('Testing'), [('TEFGT', )])
     d1.enabled = False
     self.assertEqual(dc.lookup(('TEST', )), None)
     self.assertEqual(dc.raw_lookup(('TEFT', )), None)
     self.assertEqual(dc.casereverse_lookup('testing'), None)
     self.assertCountEqual(dc.reverse_lookup('Testing'), [])
コード例 #2
0
def test_reverse_lookup():
    dc = StenoDictionaryCollection()

    d1 = StenoDictionary()
    d1['PWAOUFL'] = 'beautiful'
    d1['WAOUFL'] = 'beautiful'

    d2 = StenoDictionary()
    d2['PW-FL'] = 'beautiful'

    d3 = StenoDictionary()
    d3['WAOUFL'] = 'not beautiful'

    # Simple test.
    dc.set_dicts([d1])
    assert dc.reverse_lookup('beautiful') == {'PWAOUFL', 'WAOUFL'}

    # No duplicates.
    d2_copy = StenoDictionary()
    d2_copy.update(d2)
    dc.set_dicts([d2_copy, d2])
    assert dc.reverse_lookup('beautiful') == {'PW-FL'}

    # Don't stop at the first dictionary with matches.
    dc.set_dicts([d2, d1])
    assert dc.reverse_lookup('beautiful') == {'PW-FL', 'PWAOUFL', 'WAOUFL'}

    # Ignore keys overridden by a higher precedence dictionary.
    dc.set_dicts([d3, d2, d1])
    assert dc.reverse_lookup('beautiful') == {'PW-FL', 'PWAOUFL'}
コード例 #3
0
    def test_dictionary_collection(self):
        dc = StenoDictionaryCollection()
        d1 = StenoDictionary()
        d1[('S', )] = 'a'
        d1[('T', )] = 'b'
        d2 = StenoDictionary()
        d2[('S', )] = 'c'
        d2[('W', )] = 'd'
        dc.set_dicts([d1, d2])
        self.assertEqual(dc.lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')
        f = lambda k, v: v == 'c'
        dc.add_filter(f)
        self.assertIsNone(dc.lookup(('S', )))
        self.assertEqual(dc.raw_lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')
        self.assertEqual(dc.reverse_lookup('c'), [('S', )])

        dc.remove_filter(f)
        self.assertEqual(dc.lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')

        self.assertEqual(dc.reverse_lookup('c'), [('S', )])

        dc.set(('S', ), 'e')
        self.assertEqual(dc.lookup(('S', )), 'e')
        self.assertEqual(d2[('S', )], 'e')
コード例 #4
0
 def setup_method(self):
     self.t = Translator()
     self.s = type(self).FakeState()
     self.t._state = self.s
     self.d = StenoDictionary()
     self.dc = StenoDictionaryCollection([self.d])
     self.t.set_dictionary(self.dc)
コード例 #5
0
def test_dictionary_collection_longest_key():

    k1 = ('S', )
    k2 = ('S', 'T')
    k3 = ('S', 'T', 'R')

    dc = StenoDictionaryCollection()
    assert dc.longest_key == 0

    d1 = StenoDictionary()
    d1.path = 'd1'
    d1[k1] = 'a'

    dc.set_dicts([d1])
    assert dc.longest_key == 1

    d1[k2] = 'a'
    assert dc.longest_key == 2

    d2 = StenoDictionary()
    d2.path = 'd2'
    d2[k3] = 'c'

    dc.set_dicts([d2, d1])
    assert dc.longest_key == 3

    del d1[k2]
    assert dc.longest_key == 3

    dc.set_dicts([d1])
    assert dc.longest_key == 1

    dc.set_dicts([])
    assert dc.longest_key == 0
コード例 #6
0
    def test_dictionary_collection_longest_key(self):

        k1 = ('S', )
        k2 = ('S', 'T')
        k3 = ('S', 'T', 'R')

        dc = StenoDictionaryCollection()
        self.assertEqual(dc.longest_key, 0)

        d1 = StenoDictionary()
        d1._path = 'd1'
        d1[k1] = 'a'

        dc.set_dicts([d1])
        self.assertEqual(dc.longest_key, 1)

        d1[k2] = 'a'
        self.assertEqual(dc.longest_key, 2)

        d2 = StenoDictionary()
        d2._path = 'd2'
        d2[k3] = 'c'

        dc.set_dicts([d2, d1])
        self.assertEqual(dc.longest_key, 3)

        del d1[k2]
        self.assertEqual(dc.longest_key, 3)

        dc.set_dicts([d1])
        self.assertEqual(dc.longest_key, 1)

        dc.set_dicts([])
        self.assertEqual(dc.longest_key, 0)
コード例 #7
0
    def test_reverse_lookup(self):
        dc = StenoDictionaryCollection()

        d1 = StenoDictionary()
        d1[('PWAOUFL', )] = 'beautiful'
        d1[('WAOUFL', )] = 'beautiful'

        d2 = StenoDictionary()
        d2[('PW-FL', )] = 'beautiful'

        d3 = StenoDictionary()
        d3[('WAOUFL', )] = 'not beautiful'

        # Simple test.
        dc.set_dicts([d1])
        self.assertCountEqual(dc.reverse_lookup('beautiful'), [('PWAOUFL', ),
                                                               ('WAOUFL', )])

        # No duplicates.
        d2_copy = StenoDictionary()
        d2_copy.update(d2)
        dc.set_dicts([d2_copy, d2])
        self.assertCountEqual(dc.reverse_lookup('beautiful'), [('PW-FL', )])

        # Don't stop at the first dictionary with matches.
        dc.set_dicts([d2, d1])
        self.assertCountEqual(dc.reverse_lookup('beautiful'), [('PWAOUFL', ),
                                                               ('WAOUFL', ),
                                                               ('PW-FL', )])

        # Ignore keys overridden by a higher precedence dictionary.
        dc.set_dicts([d3, d2, d1])
        self.assertCountEqual(dc.reverse_lookup('beautiful'), [('PWAOUFL', ),
                                                               ('PW-FL', )])
コード例 #8
0
def test_dictionary_enabled():
    dc = StenoDictionaryCollection()
    d1 = StenoDictionary()
    d1.path = 'd1'
    d1[('TEFT', )] = 'test1'
    d1[('TEFGT', )] = 'Testing'
    d2 = StenoDictionary()
    d2[('TEFT', )] = 'test2'
    d2[('TEFT', '-G')] = 'Testing'
    d2.path = 'd2'
    dc.set_dicts([d2, d1])
    assert dc.lookup(('TEFT', )) == 'test2'
    assert dc.raw_lookup(('TEFT', )) == 'test2'
    assert dc.casereverse_lookup('testing') == ['Testing']
    assert dc.reverse_lookup('Testing') == {('TEFT', '-G'), ('TEFGT', )}
    d2.enabled = False
    assert dc.lookup(('TEFT', )) == 'test1'
    assert dc.raw_lookup(('TEFT', )) == 'test1'
    assert dc.casereverse_lookup('testing') == ['Testing']
    assert dc.reverse_lookup('Testing') == {('TEFGT', )}
    d1.enabled = False
    assert dc.lookup(('TEST', )) is None
    assert dc.raw_lookup(('TEFT', )) is None
    assert dc.casereverse_lookup('testing') == []
    assert dc.reverse_lookup('Testing') == set()
コード例 #9
0
ファイル: translation.py プロジェクト: ArenM/plover
 def __init__(self):
     self._undo_length = 0
     self._dictionary = None
     self.set_dictionary(StenoDictionaryCollection())
     self._listeners = set()
     self._state = _State()
     self._to_undo = []
     self._to_do = 0
コード例 #10
0
 def setup_method(self):
     self.d = StenoDictionary()
     self.dc = StenoDictionaryCollection([self.d])
     self.s = _State()
     self.o = self.CaptureOutput()
     self.tlor = Translator()
     self.tlor.set_dictionary(self.dc)
     self.tlor.add_listener(self.o)
     self.tlor.set_state(self.s)
コード例 #11
0
ファイル: translation.py プロジェクト: Quetzal2/plover
 def __init__(self, stroke_limit=KEY_STROKE_LIMIT):
     self._stroke_limit = stroke_limit
     self._undo_length = 0
     self._dictionary = None
     self.set_dictionary(StenoDictionaryCollection())
     self._listeners = set()
     self._state = _State()
     self._to_undo = []
     self._to_do = 0
コード例 #12
0
def test_dictionary_collection_writeable():
    d1 = StenoDictionary()
    d1[('S', )] = 'a'
    d1[('T', )] = 'b'
    d2 = StenoDictionary()
    d2[('S', )] = 'c'
    d2[('W', )] = 'd'
    d2.readonly = True
    dc = StenoDictionaryCollection([d2, d1])
    assert dc.first_writable() == d1
    dc.set(('S', ), 'A')
    assert d1[('S', )] == 'A'
    assert d2[('S', )] == 'c'
コード例 #13
0
 def test_dictionary_collection_writeable(self):
     d1 = StenoDictionary()
     d1[('S', )] = 'a'
     d1[('T', )] = 'b'
     d2 = StenoDictionary()
     d2[('S', )] = 'c'
     d2[('W', )] = 'd'
     d2.readonly = True
     dc = StenoDictionaryCollection([d2, d1])
     self.assertEqual(dc.first_writable(), d1)
     dc.set(('S', ), 'A')
     self.assertEqual(d1[('S', )], 'A')
     self.assertEqual(d2[('S', )], 'c')
コード例 #14
0
 def _set_dictionaries(self, dictionaries):
     def dictionaries_changed(l1, l2):
         if len(l1) != len(l2):
             return True
         for d1, d2 in zip(l1, l2):
             if d1 is not d2:
                 return True
         return False
     if not dictionaries_changed(dictionaries, self._dictionaries.dicts):
         # No change.
         return
     self._dictionaries = StenoDictionaryCollection(dictionaries)
     self._translator.set_dictionary(self._dictionaries)
     self._trigger_hook('dictionaries_loaded', self._dictionaries)
コード例 #15
0
def model_test(monkeypatch, request):
    state = request.function.__doc__
    # Patch configuration directory.
    current_dir = Path('.').resolve()
    monkeypatch.setattr('plover.misc.CONFIG_DIR', str(current_dir))
    monkeypatch.setattr('plover.gui_qt.dictionaries_widget.CONFIG_DIR',
                        str(current_dir))
    # Disable i18n support.
    monkeypatch.setattr('plover.gui_qt.dictionaries_widget._', lambda s: s)
    # Fake config.
    config = mock.PropertyMock()
    config.return_value = {}
    # Dictionaries.
    dictionaries = StenoDictionaryCollection()
    # Fake engine.
    engine = mock.MagicMock(spec='''
                            __enter__ __exit__
                            config signal_connect
                            '''.split())
    engine.__enter__.return_value = engine
    type(engine).config = config
    signals = mock.MagicMock()
    config.return_value = {
        'dictionaries': config_dictionaries_from_state(state) if state else [],
        'classic_dictionaries_display_order': False,
    }
    # Setup model.
    model = DictionariesModel(engine, {name: name
                                       for name in ICON_TO_CHAR},
                              max_undo=5)
    for slot in '''
    dataChanged
    layoutAboutToBeChanged
    layoutChanged
    has_undo_changed
    '''.split():
        getattr(model, slot).connect(getattr(signals, slot))
    connections = dict(call.args for call in engine.signal_connect.mock_calls)
    assert connections.keys() == {'config_changed', 'dictionaries_loaded'}
    config.reset_mock()
    engine.reset_mock()
    # Test helper.
    test = ModelTest(config, dictionaries, engine, model, signals, connections,
                     state)
    if state and any(icon != 'loading'
                     for enabled, icon, path in parse_state(state)):
        test.load_dictionaries(state)
        test.reset_mocks()
    return test
コード例 #16
0
    def test_dictionary_collection(self):
        dc = StenoDictionaryCollection()
        d1 = StenoDictionary()
        d1[('S', )] = 'a'
        d1[('T', )] = 'b'
        d1.path = 'd1'
        d2 = StenoDictionary()
        d2[('S', )] = 'c'
        d2[('W', )] = 'd'
        d2.path = 'd2'
        dc.set_dicts([d2, d1])
        self.assertEqual(dc.lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')
        f = lambda k, v: v == 'c'
        dc.add_filter(f)
        self.assertIsNone(dc.lookup(('S', )))
        self.assertEqual(dc.raw_lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')
        self.assertEqual(dc.reverse_lookup('c'), [('S', )])

        dc.remove_filter(f)
        self.assertEqual(dc.lookup(('S', )), 'c')
        self.assertEqual(dc.lookup(('W', )), 'd')
        self.assertEqual(dc.lookup(('T', )), 'b')

        self.assertEqual(dc.reverse_lookup('c'), [('S', )])

        dc.set(('S', ), 'e')
        self.assertEqual(dc.lookup(('S', )), 'e')
        self.assertEqual(d2[('S', )], 'e')

        dc.set(('S', ), 'f', path='d1')
        self.assertEqual(dc.lookup(('S', )), 'e')
        self.assertEqual(d1[('S', )], 'f')
        self.assertEqual(d2[('S', )], 'e')

        # Iterating on a StenoDictionaryCollection is
        # the same as iterating on its dictionaries' paths.
        self.assertEqual(list(dc), ['d2', 'd1'])

        # Test get and [].
        self.assertEqual(dc.get('d1'), d1)
        self.assertEqual(dc['d1'], d1)
        self.assertEqual(dc.get('invalid'), None)
        with self.assertRaises(KeyError):
            dc['invalid']
コード例 #17
0
def test_dictionary_collection():
    d1 = StenoDictionary()
    d1[('S', )] = 'a'
    d1[('T', )] = 'b'
    d1.path = 'd1'
    d2 = StenoDictionary()
    d2[('S', )] = 'c'
    d2[('W', )] = 'd'
    d2.path = 'd2'
    dc = StenoDictionaryCollection([d2, d1])
    assert dc.lookup(('S', )) == 'c'
    assert dc.lookup(('W', )) == 'd'
    assert dc.lookup(('T', )) == 'b'
    f = lambda k, v: v == 'c'
    dc.add_filter(f)
    assert dc.lookup(('S', )) is None
    assert dc.raw_lookup(('S', )) == 'c'
    assert dc.lookup(('W', )) == 'd'
    assert dc.lookup(('T', )) == 'b'
    assert dc.reverse_lookup('c') == {('S', )}

    dc.remove_filter(f)
    assert dc.lookup(('S', )) == 'c'
    assert dc.lookup(('W', )) == 'd'
    assert dc.lookup(('T', )) == 'b'

    assert dc.reverse_lookup('c') == {('S', )}

    dc.set(('S', ), 'e')
    assert dc.lookup(('S', )) == 'e'
    assert d2[('S', )] == 'e'

    dc.set(('S', ), 'f', path='d1')
    assert dc.lookup(('S', )) == 'e'
    assert d1[('S', )] == 'f'
    assert d2[('S', )] == 'e'

    # Iterating on a StenoDictionaryCollection is
    # the same as iterating on its dictionaries' paths.
    assert list(dc) == ['d2', 'd1']

    # Test get and [].
    assert dc.get('d1') == d1
    assert dc['d1'] == d1
    assert dc.get('invalid') is None
    with pytest.raises(KeyError):
        dc['invalid']
コード例 #18
0
def test_casereverse_lookup():
    dc = StenoDictionaryCollection()

    d1 = StenoDictionary()
    d1[('PWAOUFL', )] = 'beautiful'
    d1[('WAOUFL', )] = 'beAuTIFul'

    d2 = StenoDictionary()
    d2[('PW-FL', )] = 'BEAUTIFUL'

    d3 = StenoDictionary()
    d3[('WAOUFL', )] = 'not beautiful'

    dc.set_dicts([d1, d2, d3])

    assert dc.casereverse_lookup('beautiful') == {
        'beautiful', 'BEAUTIFUL', 'beAuTIFul'
    }
コード例 #19
0
    def test_changing_state(self):
        output = []
        def listener(undo, do, prev):
            prev = list(prev) if prev else None
            output.append((undo, do, prev))

        d = StenoDictionary()
        d[('S', 'P')] = 'hi'
        dc = StenoDictionaryCollection()
        dc.set_dicts([d])
        t = Translator()
        t.set_dictionary(dc)
        t.translate(stroke('T'))
        t.translate(stroke('S'))
        s = copy.deepcopy(t.get_state())
        
        t.add_listener(listener)
        
        expected = [([Translation([stroke('S')], None)], 
                     [Translation([stroke('S'), stroke('P')], 'hi')], 
                     [Translation([stroke('T')], None)])]
        t.translate(stroke('P'))
        self.assertEqual(output, expected)
        
        del output[:]
        t.set_state(s)
        t.translate(stroke('P'))
        self.assertEqual(output, expected)
        
        del output[:]
        t.clear_state()
        t.translate(stroke('P'))
        self.assertEqual(output, [([], [Translation([stroke('P')], None)], None)])
        
        del output[:]
        t.set_state(s)
        t.translate(stroke('P'))
        self.assertEqual(output, 
                         [([], 
                           [Translation([stroke('P')], None)], 
                           [Translation([stroke('S'), stroke('P')], 'hi')])])
コード例 #20
0
def test_search():
    dc = StenoDictionaryCollection()

    # Similarity is based on string equality after removing case and stripping special characters from the ends.
    d1 = StenoDictionary()
    d1[('WAOUFL', )] = 'beautiful'
    d1[('PWAOUFL', )] = 'Beautiful'
    d1[('PWAOUT', '-FL')] = '{^BEAUTIFUL}  '
    d1[('ULG', )] = 'ugly'
    dc.set_dicts([d1])
    assert dc.find_similar('beautiful') == [('Beautiful', {('PWAOUFL', )}),
                                            ('beautiful', {('WAOUFL', )}),
                                            ('{^BEAUTIFUL}  ', {('PWAOUT',
                                                                 '-FL')})]

    assert dc.find_similar('{#BEAUtiful}{^}') == [
        ('Beautiful', {('PWAOUFL', )}), ('beautiful', {('WAOUFL', )}),
        ('{^BEAUTIFUL}  ', {('PWAOUT', '-FL')})
    ]

    # Translations found in multiple dicts should combine non-overlapping keys in the results.
    d2 = StenoDictionary()
    del d1[('PWAOUT', '-FL')]
    d2[('PW-FL', )] = 'beautiful'
    dc.set_dicts([d1, d2])
    assert dc.find_similar('beautiful') == [('Beautiful', {('PWAOUFL', )}),
                                            ('beautiful', {('WAOUFL', ),
                                                           ('PW-FL', )})]

    # If all possible keys for a translation are overridden, that translation should not be returned.
    d3 = StenoDictionary()
    d3[('PW-FL', )] = 'not beautiful'
    d3[('WAOUFL', )] = 'not beautiful'
    dc.set_dicts([d3, d1, d2])
    assert dc.find_similar('beautiful') == [('Beautiful', {('PWAOUFL', )})]

    # For partial word search, similar words will be returned first, but if the count is greater than that,
    # the next words in sorted order which are supersets are returned. Also stops at the end of the dictionary.
    dc.set_dicts([d1])
    d1[('PWAOU', )] = 'beau'
    d1[('PWAOUFL', 'HREU')] = 'beautifully'
    d1[('UG', 'HREU', '-PBS')] = 'ugliness'
    assert dc.find_partial('beau',
                           count=4) == [('beau', {('PWAOU', )}),
                                        ('Beautiful', {('PWAOUFL', )}),
                                        ('beautiful', {('WAOUFL', )}),
                                        ('beautifully', {('PWAOUFL', 'HREU')})]
    assert dc.find_partial('UGLY', count=2) == [('ugly', {('ULG', )})]

    # Even if a word isn't present, the search will return words going forward
    # from the index where it would be found if it was there.
    assert dc.find_partial('beaut',
                           count=3) == [('Beautiful', {('PWAOUFL', )}),
                                        ('beautiful', {('WAOUFL', )}),
                                        ('beautifully', {('PWAOUFL', 'HREU')})]

    # Regex search is straightforward; return up to count entries in order that match the given regular expression.
    # If no regex metacharacters are present, should just be a case-sensitive starts-with search.
    assert dc.find_regex('beau',
                         count=4) == [('beau', {('PWAOU', )}),
                                      ('beautiful', {('WAOUFL', )}),
                                      ('beautifully', {('PWAOUFL', 'HREU')})]
    assert dc.find_regex('beautiful.?.?',
                         count=2) == [('beautiful', {('WAOUFL', )}),
                                      ('beautifully', {('PWAOUFL', 'HREU')})]
    assert dc.find_regex(' beautiful', count=3) == []
    assert dc.find_regex('(b|u).{3}$', count=2) == [('beau', {('PWAOU', )}),
                                                    ('ugly', {('ULG', )})]
    assert dc.find_regex('.*ly',
                         count=5) == [('beautifully', {('PWAOUFL', 'HREU')}),
                                      ('ugly', {('ULG', )})]

    # Regex errors won't raise if the algorithm short circuits a pattern with no possible matches.
    assert dc.find_regex('an open group that doesn\'t raise(', count=5) == []
    with pytest.raises(re.error):
        print(dc.find_regex('beautiful...an open group(', count=1))
コード例 #21
0
ファイル: test_translation.py プロジェクト: benreynwar/plover
 def setUp(self):
     self.d = StenoDictionary()
     self.dc = StenoDictionaryCollection()
     self.dc.set_dicts([self.d])
     self.s = _State()
     self.o = type(self).CaptureOutput()
コード例 #22
0
def test_translator():

    # It's not clear that this test is needed anymore. There are separate
    # tests for _translate_stroke and test_translate_calls_translate_stroke
    # makes sure that translate calls it properly. But since I already wrote
    # this test I'm going to keep it.

    class Output:
        def __init__(self):
            self._output = []

        def write(self, undo, do, prev):
            for t in undo:
                self._output.pop()
            for t in do:
                if t.english:
                    self._output.append(t.english)
                else:
                    self._output.append('/'.join(t.rtfcre))

        def get(self):
            return ' '.join(self._output)

        def clear(self):
            del self._output[:]

    d = StenoDictionary()
    out = Output()
    t = Translator()
    dc = StenoDictionaryCollection([d])
    t.set_dictionary(dc)
    t.add_listener(out.write)

    t.translate(stroke('S'))
    assert out.get() == 'S'
    t.translate(stroke('T'))
    assert out.get() == 'S T'
    t.translate(stroke('*'))
    assert out.get() == 'S'
    t.translate(stroke('*'))
    # Undo buffer ran out
    assert out.get() == 'S ' + BACK_STRING

    t.set_min_undo_length(3)
    out.clear()
    t.translate(stroke('S'))
    assert out.get() == 'S'
    t.translate(stroke('T'))
    assert out.get() == 'S T'
    t.translate(stroke('*'))
    assert out.get() == 'S'
    t.translate(stroke('*'))
    assert out.get() == ''

    out.clear()
    d[('S', )] = 't1'
    d[('T', )] = 't2'
    d[('S', 'T')] = 't3'

    t.translate(stroke('S'))
    assert out.get() == 't1'
    t.translate(stroke('T'))
    assert out.get() == 't3'
    t.translate(stroke('T'))
    assert out.get() == 't3 t2'
    t.translate(stroke('S'))
    assert out.get() == 't3 t2 t1'
    t.translate(stroke('*'))
    assert out.get() == 't3 t2'
    t.translate(stroke('*'))
    assert out.get() == 't3'
    t.translate(stroke('*'))
    assert out.get() == 't1'
    t.translate(stroke('*'))
    assert out.get() == ''

    t.translate(stroke('S'))
    assert out.get() == 't1'
    t.translate(stroke('T'))
    assert out.get() == 't3'
    t.translate(stroke('T'))
    assert out.get() == 't3 t2'

    d[('S', 'T', 'T')] = 't4'
    d[('S', 'T', 'T', 'S')] = 't5'

    t.translate(stroke('S'))
    assert out.get() == 't5'
    t.translate(stroke('*'))
    assert out.get() == 't3 t2'
    t.translate(stroke('*'))
    assert out.get() == 't3'
    t.translate(stroke('T'))
    assert out.get() == 't4'
    t.translate(stroke('S'))
    assert out.get() == 't5'
    t.translate(stroke('S'))
    assert out.get() == 't5 t1'
    t.translate(stroke('*'))
    assert out.get() == 't5'
    t.translate(stroke('*'))
    assert out.get() == 't4'
    t.translate(stroke('*'))
    assert out.get() == 't3'
    t.translate(stroke('*'))
    assert out.get() == 't1'
    t.translate(stroke('*'))
    assert out.get() == ''

    d.clear()

    s = stroke('S')
    t.translate(s)
    t.translate(s)
    t.translate(s)
    t.translate(s)
    s = stroke('*')
    t.translate(s)
    t.translate(s)
    t.translate(s)
    t.translate(s)
    # Not enough undo to clear output.
    assert out.get() == 'S ' + BACK_STRING

    out.clear()
    t.remove_listener(out.write)
    t.translate(stroke('S'))
    assert out.get() == ''