class TestTranslateStroke: class CaptureOutput: output = namedtuple('output', 'undo do prev') def __init__(self): self.output = [] def __call__(self, undo, new, prev): prev = list(prev) if prev else None self.output = type(self).output(undo, new, prev) def t(self, strokes): """A quick way to make a translation.""" strokes = [stroke(x) for x in strokes.split('/')] key = tuple(s.rtfcre for s in strokes) translation = self.dc.lookup(key) return Translation(strokes, translation) def lt(self, translations): """A quick way to make a list of translations.""" return [self.t(x) for x in translations.split()] def define(self, key, value): key = normalize_steno(key) self.d[key] = value def translate(self, steno): self.tlor.translate(stroke(steno)) def _check_translations(self, expected): # Hide from traceback on assertions (reduce output size for failed tests). __tracebackhide__ = operator.methodcaller('errisinstance', AssertionError) msg = ''' translations: results: %s expected: %s ''' % (self.s.translations, expected) assert self.s.translations == expected, msg def _check_output(self, undo, do, prev): # Hide from traceback on assertions (reduce output size for failed tests). __tracebackhide__ = operator.methodcaller('errisinstance', AssertionError) msg = ''' output: results: -%s +%s [%s] expected: -%s +%s [%s] ''' % (self.o.output + (undo, do, prev)) assert self.o.output == (undo, do, prev), msg 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) def test_first_stroke(self): self.translate('-B') self._check_translations(self.lt('-B')) self._check_output([], self.lt('-B'), None) def test_second_stroke(self): self.define('S/P', 'spiders') self.s.translations = self.lt('S') self.translate('-T') self._check_translations(self.lt('S -T')) self._check_output([], self.lt('-T'), self.lt('S')) def test_second_stroke_tail(self): self.s.tail = self.t('T/A/EU/L') self.translate('-E') self._check_translations(self.lt('E')) self._check_output([], self.lt('E'), self.lt('T/A/EU/L')) def test_with_translation_1(self): self.define('S', 'is') self.define('-T', 'that') self.s.translations = self.lt('S') self.tlor.set_min_undo_length(2) self.translate('-T') self._check_translations(self.lt('S -T')) self._check_output([], self.lt('-T'), self.lt('S')) assert self.o.output.do[0].english == 'that' def test_with_translation_2(self): self.define('S', 'is') self.define('-T', 'that') self.s.translations = self.lt('S') self.tlor.set_min_undo_length(1) self.translate('-T') self._check_translations(self.lt('-T')) self._check_output([], self.lt('-T'), self.lt('S')) assert self.o.output.do[0].english == 'that' def test_finish_two_translation(self): self.define('S/T', 'hello') self.s.translations = self.lt('S') self.translate('T') self._check_translations(self.lt('S/T')) self._check_output(self.lt('S'), self.lt('S/T'), None) assert self.o.output.do[0].english == 'hello' assert self.o.output.do[0].replaced == self.lt('S') def test_finish_three_translation(self): self.define('S/T/-B', 'bye') self.s.translations = self.lt('S T') self.translate('-B') self._check_translations(self.lt('S/T/-B')) self._check_output(self.lt('S T'), self.lt('S/T/-B'), None) assert self.o.output.do[0].english == 'bye' assert self.o.output.do[0].replaced == self.lt('S T') def test_replace_translation(self): self.define('S/T/-B', 'longer') self.s.translations = self.lt('S/T') self.translate('-B') self._check_translations(self.lt('S/T/-B')) self._check_output(self.lt('S/T'), self.lt('S/T/-B'), None) assert self.o.output.do[0].english == 'longer' assert self.o.output.do[0].replaced == self.lt('S/T') def test_undo(self): self.s.translations = self.lt('POP') self.translate('*') self._check_translations([]) self._check_output(self.lt('POP'), [], None) def test_empty_undo(self): self.translate('*') self._check_translations([]) self._check_output([], [Translation([Stroke('*')], BACK_STRING)], None) def test_undo_translation(self): self.define('P/P', 'pop') self.translate('P') self.translate('P') self.translate('*') self._check_translations(self.lt('P')) self._check_output(self.lt('P/P'), self.lt('P'), None) def test_undo_longer_translation(self): self.define('P/P/-D', 'popped') self.translate('P') self.translate('P') self.translate('-D') self.translate('*') self._check_translations(self.lt('P P')) self._check_output(self.lt('P/P/-D'), self.lt('P P'), None) def test_undo_tail(self): self.s.tail = self.t('T/A/EU/L') self.translate('*') self._check_translations([]) self._check_output([], [Translation([Stroke('*')], BACK_STRING)], [self.s.tail]) def test_suffix_folding(self): self.define('K-L', 'look') self.define('-G', '{^ing}') lt = self.lt('K-LG') lt[0].english = 'look {^ing}' self.translate('K-LG') self._check_translations(lt) def test_suffix_folding_multi_stroke(self): self.define('E/HR', 'he will') self.define('-S', '{^s}') self.translate('E') self.translate('HR-S') output = ' '.join(t.english for t in self.s.translations) assert output == 'he will {^s}' def test_suffix_folding_doesnt_interfere(self): self.define('E/HR', 'he will') self.define('-S', '{^s}') self.define('E', 'he') self.define('HR-S', 'also') self.translate('E') self.translate('HR-S') output = ' '.join(t.english for t in self.s.translations) assert output == 'he also' def test_suffix_folding_no_suffix(self): self.define('K-L', 'look') lt = self.lt('K-LG') assert lt[0].english is None self.translate('K-LG') self._check_translations(lt) def test_suffix_folding_no_main(self): self.define('-G', '{^ing}') lt = self.lt('K-LG') assert lt[0].english is None self.translate('K-LG') self._check_translations(lt) def test_retrospective_insert_space(self): self.define('T/E/S/T', 'a longer key') self.define('PER', 'perfect') self.define('SWAEUGS', 'situation') self.define('PER/SWAEUGS', 'persuasion') self.define('SP*', '{*?}') self.translate('PER') self.translate('SWAEUGS') self.translate('SP*') lt = self.lt('PER') undo = self.lt('PER/SWAEUGS') undo[0].replaced = lt do = self.lt('SP*') do[0].english = 'perfect situation' do[0].is_retrospective_command = True do[0].replaced = undo self._check_translations(do) self._check_output(undo, do, None) def test_retrospective_insert_space_undefined(self): # Should work when beginning or ending strokes aren't defined self.define('T/E/S/T', 'a longer key') self.define('STWR/STWR', 'test') self.define('SP*', '{*?}') self.translate('STWR') self.translate('STWR') self.translate('SP*') lt = self.lt('STWR') undo = self.lt('STWR/STWR') undo[0].replaced = lt do = self.lt('SP*') do[0].english = 'STWR STWR' do[0].is_retrospective_command = True do[0].replaced = undo self._check_translations(do) self._check_output(undo, do, None) def test_retrospective_delete_space(self): self.define('T/E/S/T', 'a longer key') self.define('K', 'kick') self.define('PW', 'back') self.define('SP*', '{*!}') self.translate('K') self.translate('PW') self.translate('SP*') undo = self.lt('K PW') do = self.lt('SP*') do[0].english = 'kick{^~|^}back' do[0].is_retrospective_command = True do[0].replaced = undo self._check_translations(do) self._check_output(undo, do, None) def test_retrospective_delete_space_with_number(self): self.define('T/E/S/T', 'a longer key') self.define('U', 'user') self.define('SP*', '{*!}') self.translate('U') self.translate('1-') self.translate('SP*') undo = self.lt('U 1-') do = self.lt('SP*') do[0].english = 'user{^~|^}{&1}' do[0].is_retrospective_command = True do[0].replaced = undo self._check_translations(do) self._check_output(undo, do, None) def test_retrospective_delete_space_with_period(self): self.define('T/E/S/T', 'a longer key') self.define('P-P', '{.}') self.define('SH*', 'zshrc') self.define('SP*', '{*!}') self.translate('P-P') self.translate('SH*') self.translate('SP*') undo = self.lt('P-P SH*') do = self.lt('SP*') do[0].english = '{.}{^~|^}zshrc' do[0].is_retrospective_command = True do[0].replaced = undo self._check_translations(do) self._check_output(undo, do, None) def test_retrospective_toggle_asterisk(self): self.define('T/E/S/T', 'a longer key') self.define('S', 'see') self.define('S*', 'sea') self.define('A*', '{*}') self.translate('S') self.translate('A*') self._check_translations(self.lt('S*')) self._check_output(self.lt('S'), self.lt('S*'), None) def test_retrospective_toggle_empty(self): self.define('A*', '{*}') self.translate('A*') self._check_translations(self.lt('')) assert self.o.output == [] def test_retrospective_toggle_asterisk_replaced1(self): self.define('P-P', '{.}') self.define('SKEL', 'cancel') self.define('SKEL/TO-PB', 'skeleton') self.define('SKEL/TO*PB', 'not skeleton!') self.define('A*', '{*}') self.translate('P-P') self.translate('SKEL') self.translate('TO-PB') self.translate('A*') self._check_translations(self.lt('SKEL/TO*PB')) self._check_output(self.lt('SKEL/TO-PB'), self.lt('SKEL/TO*PB'), self.lt('P-P')) def test_retrospective_toggle_asterisk_replaced2(self): self.define('P-P', '{.}') self.define('SKEL', 'cancel') self.define('SKEL/TO-PB', 'skeleton') self.define('TO*PB', '{^ton}') self.define('A*', '{*}') self.translate('P-P') self.translate('SKEL') self.translate('TO-PB') self.translate('A*') self._check_translations(self.lt('SKEL TO*PB')) self._check_output(self.lt('SKEL/TO-PB'), self.lt('SKEL TO*PB'), self.lt('P-P')) def test_repeat_last_stroke1(self): self.define('T/E/S/T', 'a longer key') self.define('TH', 'this') self.define('R*', '{*+}') self.translate('TH') self.translate('R*') undo = [] do = self.lt('TH') state = self.lt('TH TH') self._check_translations(state) self._check_output(undo, do, do) def test_repeat_last_stroke2(self): self.define('T/E/S/T', 'a longer key') self.define('THA', 'that') self.define('R*', '{*+}') self.translate('THA') self.translate('R*') undo = [] do = self.lt('THA') state = self.lt('THA THA') self._check_translations(state) self._check_output(undo, do, do) def test_untranslate_translation(self): self.tlor.set_min_undo_length(2) self.define('TH', 'this') self.define('THA', 'that') self.translate('TH') self.translate('THA') self.tlor.untranslate_translation(self.t('THA')) self.tlor.untranslate_translation(self.t('TH')) self.tlor.flush() self._check_output(self.lt('TH THA'), [], None)