def test_dictionaries_option(self): section = config.SYSTEM_CONFIG_SECTION % config.DEFAULT_SYSTEM_NAME option = config.SYSTEM_DICTIONARIES_OPTION legacy_section = config.LEGACY_DICTIONARY_CONFIG_SECTION legacy_option = config.LEGACY_DICTIONARY_FILE_OPTION c = config.Config() config_dir = os.path.normcase(os.path.realpath(config.CONFIG_DIR)) # Check the default value. self.assertEqual(c.get_dictionaries(), [DictionaryConfig(path) for path in system.DEFAULT_DICTIONARIES]) # Load from a file encoded the ancient way... filename = normalize_path('/some_file') f = make_config('[%s]\n%s: %s' % (legacy_section, legacy_option, filename)) c.load(f) # ..and make sure the right value is set. self.assertEqual(c.get_dictionaries(), [DictionaryConfig(filename)]) # Load from a file encoded the old way... filenames = [os.path.join(ABS_PATH, f) for f in ('b', 'a', 'd', 'c')] dictionaries = [DictionaryConfig(path) for path in filenames] value = '\n'.join('%s%d: %s' % (legacy_option, d, v) for d, v in enumerate(reversed(filenames), start=1)) f = make_config('[%s]\n%s' % (legacy_section, value)) c.load(f) # ...and make sure the right value is set. self.assertEqual(c.get_dictionaries(), dictionaries) # Check the config is saved back converted to the new way. f = make_config() c.save(f) new_config_contents = '[%s]\n%s = %s\n\n' % ( section, option, json.dumps([ {'path': path, 'enabled': True} for path in filenames ], sort_keys=True)) self.assertEqual(f.getvalue().decode('utf-8'), new_config_contents) # Load from a file encoded the new way... f = make_config(new_config_contents) c.load(f) # ...and make sure the right value is set. self.assertEqual(c.get_dictionaries(), dictionaries) # Set a value... dictionaries.reverse() filenames.reverse() c.set_dictionaries(dictionaries) f = make_config() # ...save it... c.save(f) # ...and make sure it's right. new_config_contents = '[%s]\n%s = %s\n\n' % ( section, option, json.dumps([ {'path': path, 'enabled': True} for path in filenames ], sort_keys=True)) self.assertEqual(f.getvalue().decode('utf-8'), new_config_contents) # The new way must take precedence over the old way. legacy_value = '\n'.join('%s%d = %s' % (legacy_option, d, v) for d, v in enumerate(['/foo', '/bar'], start=1)) f = make_config(new_config_contents + '\n[%s]\n%s\n' % ( legacy_section, legacy_value)) c.load(f) self.assertEqual(c.get_dictionaries(), dictionaries)
def test_config_dict(self): short_path = os.path.normpath('~/foo/bar') full_path = os.path.expanduser(os.path.normpath('~/foo/bar')) # Path should be expanded. self.assertEqual(DictionaryConfig(short_path).path, full_path) self.assertEqual(DictionaryConfig(full_path).path, full_path) # Short path is available through `short_path`. self.assertEqual(DictionaryConfig(full_path).short_path, short_path) self.assertEqual(DictionaryConfig(short_path).short_path, short_path) # Enabled default to True. self.assertEqual(DictionaryConfig('foo').enabled, True) self.assertEqual(DictionaryConfig('foo', False).enabled, False) # When converting to a dict (for dumping to JSON), # a dictionary with the shortened path is used. self.assertEqual( DictionaryConfig(full_path).to_dict(), { 'path': short_path, 'enabled': True }) self.assertEqual( DictionaryConfig(short_path, False).to_dict(), { 'path': short_path, 'enabled': False }) # Test from_dict creation helper. self.assertEqual( config.DictionaryConfig.from_dict({'path': short_path}), DictionaryConfig(short_path)) self.assertEqual( config.DictionaryConfig.from_dict({ 'path': full_path, 'enabled': False }), DictionaryConfig(short_path, False))
def test_as_dict_update(self): opt_list = ''' auto_start classic_dictionaries_display_order dictionaries enable_stroke_logging enable_translation_logging enabled_extensions log_file_name machine_specific_options machine_type show_stroke_display show_suggestions_display space_placement start_attached start_capitalized start_minimized system_keymap system_name translation_frame_opacity undo_levels '''.split() cfg = config.Config() excepted_dict = {opt: getattr(cfg, 'get_' + opt)() for opt in opt_list} self.assertEqual(cfg.as_dict(), excepted_dict) update = { 'auto_start': False, 'dictionaries': [DictionaryConfig('user.json', False)], 'enable_stroke_logging': False, 'space_placement': 'After Output', 'start_minimized': False, } cfg.update(**update) excepted_dict.update(update) self.assertEqual(cfg.as_dict(), excepted_dict)
def _create_new_dictionary(self): new_filename = QFileDialog.getSaveFileName( self, _('New dictionary'), None, _dictionary_filters(include_readonly=False), )[0] if not new_filename: return new_filename = normalize_path(new_filename) try: d = create_dictionary(new_filename, threaded_save=False) d.save() except: log.error('creating dictionary %s failed', new_filename, exc_info=True) return dictionaries = self._config_dictionaries[:] for d in dictionaries: if d.path == new_filename: break else: dictionaries.insert(0, DictionaryConfig(new_filename)) # Note: pass in `loaded_dictionaries` to force update (use case: # the user decided to overwrite an already loaded dictionary). self._update_dictionaries( dictionaries, keep_selection=False, loaded_dictionaries=self._loaded_dictionaries)
def test_dictionary_config(self): short_path = os.path.normcase(os.path.normpath('~/foo/bar')) full_path = os.path.normcase(os.path.expanduser(os.path.normpath('~/foo/bar'))) dc = DictionaryConfig(short_path) # Path should be expanded. self.assertEqual(dc.path, full_path) # Shortened path is available through short_path. self.assertEqual(dc.short_path, short_path) # Enabled default to True. self.assertEqual(dc.enabled, True) # Check conversion to dict: short path should be used. self.assertEqual(dc.to_dict(), {'path': short_path, 'enabled': True}) # Test replace method. dc = dc.replace(enabled=False) self.assertEqual(dc.path, full_path) self.assertEqual(dc.enabled, False) # Test creation from dict. self.assertEqual(DictionaryConfig.from_dict({'path': short_path, 'enabled': False}), dc)
def backup_dictionary_stack(dictionaries, path): if dictionaries: with open(path, 'w') as f: json.dump([DictionaryConfig.to_dict(d) for d in dictionaries], f) else: try: os.remove(path) except OSError: pass #Good, we didn't want it anyway!
def setUp(self): self.spanish = DictionaryConfig('spanish/main.json') self.english = DictionaryConfig('main.json') self.commands = DictionaryConfig('commands.json') self.user = DictionaryConfig('user.json') self.extra = DictionaryConfig('extra.json') self.engine = FakeEngine([ self.user, self.commands, self.english, self.spanish, ]) solo_state[SOLO_ENABLED] = False solo_state[SOLO_DICT_HAS_RUN] = False self.tf = tempfile.NamedTemporaryFile(delete=False) pdc.BACKUP_DICTIONARY_PATH = self.tf.name
def handle(self, words=[]): path = normalize_path(" ".join(words)) if not isfile(path): self.output(f"{path} is not a file") return True with self.engine: self.output(f"Adding {path} as a dictionary") dicts = self.engine.config["dictionaries"].copy() dicts.insert(0, DictionaryConfig(path)) self.engine.config = {"dictionaries": dicts} return True
def test_config_dict(): short_path = os.path.normcase(os.path.normpath('~/foo/bar')) full_path = os.path.normcase(os.path.expanduser(os.path.normpath('~/foo/bar'))) # Path should be expanded. assert DictionaryConfig(short_path).path == full_path assert DictionaryConfig(full_path).path == full_path # Short path is available through `short_path`. assert DictionaryConfig(full_path).short_path == short_path assert DictionaryConfig(short_path).short_path == short_path # Enabled default to True. assert DictionaryConfig('foo').enabled assert not DictionaryConfig('foo', False).enabled # When converting to a dict (for dumping to JSON), # a dictionary with the shortened path is used. assert DictionaryConfig(full_path).to_dict() == \ {'path': short_path, 'enabled': True} assert DictionaryConfig(short_path, False).to_dict() == \ {'path': short_path, 'enabled': False} # Test from_dict creation helper. assert DictionaryConfig.from_dict({'path': short_path}) == \ DictionaryConfig(short_path) assert DictionaryConfig.from_dict({'path': full_path, 'enabled': False}) == \ DictionaryConfig(short_path, False)
def on_add_dictionaries(self): filters = ['*.' + ext for ext in sorted(_dictionary_formats())] new_filenames = QFileDialog.getOpenFileNames( self, _('Add dictionaries'), None, _('Dictionary Files') + ' (%s)' % ' '.join(filters), )[0] dictionaries = self._config_dictionaries[:] for filename in new_filenames: for d in dictionaries: if d.path == filename: break else: dictionaries.insert(0, DictionaryConfig(filename)) self._update_dictionaries(dictionaries, keep_selection=False)
def _add_existing_dictionaries(self): new_filenames = QFileDialog.getOpenFileNames( self, _('Add dictionaries'), None, _dictionary_filters(), )[0] dictionaries = self._config_dictionaries[:] for filename in new_filenames: filename = normalize_path(filename) for d in dictionaries: if d.path == filename: break else: dictionaries.insert(0, DictionaryConfig(filename)) self._update_dictionaries(dictionaries, keep_selection=False)
def _insert(self, dest_row, path_list): old_path_list = [item.path for item in self._from_row] new_path_list = ( [p for p in old_path_list[:dest_row] if p not in path_list] + path_list + [p for p in old_path_list[dest_row:] if p not in path_list]) if new_path_list == old_path_list: return if self._reverse_order: new_path_list = reversed(new_path_list) config = [ self._from_path[path].config if path in self._from_path else DictionaryConfig(path) for path in new_path_list ] self._reset_items(config)
def _add_existing_dictionaries(self): new_filenames = QFileDialog.getOpenFileNames( # i18n: Widget: “DictionariesWidget”, “add” file picker. parent=self, caption=_('Add dictionaries'), directory=self._file_dialogs_directory, filter=_dictionary_filters(), )[0] dictionaries = self._config_dictionaries[:] for filename in new_filenames: filename = normalize_path(filename) self._file_dialogs_directory = os.path.dirname(filename) for d in dictionaries: if d.path == filename: break else: dictionaries.insert(0, DictionaryConfig(filename)) self._update_dictionaries(dictionaries, keep_selection=False)
def load_dictionary_stack_from_backup(path): try: with open(path, 'r') as f: try: dictionaries = json.load(f) except json.JSONDecodeError: dictionaries = None if dictionaries: old_dictionaries = [ DictionaryConfig.from_dict(x) for x in dictionaries ] os.remove(path) #backup recovered, delete file return old_dictionaries else: return None except IOError: # No backup file, no problem return None
def _create_new_dictionary(self): # i18n: Widget: “DictionariesWidget”, “new” file picker. new_filename = self._get_dictionary_save_name(_('New dictionary')) if new_filename is None: return with _new_dictionary(new_filename) as d: pass dictionaries = self._config_dictionaries[:] for d in dictionaries: if d.path == new_filename: break else: dictionaries.insert(0, DictionaryConfig(new_filename)) # Note: pass in `loaded_dictionaries` to force update (use case: # the user decided to overwrite an already loaded dictionary). self._update_dictionaries( dictionaries, keep_selection=False, loaded_dictionaries=self._loaded_dictionaries)
def _drop_event(self, event): if not self.is_accepted_drag_event(event): return dictionaries = self._config_dictionaries[:] dest_item = self.table.itemAt(event.pos()) if dest_item is None: if self._reverse_order: dest_index = 0 else: dest_index = len(self._config_dictionaries) else: dest_index = dest_item.row() if self._reverse_order: dest_index = len(self._config_dictionaries) - dest_index - 1 if event.source() == self.table: sources = [dictionaries[row] for row in self._get_selection()] else: sources = [ DictionaryConfig(url.toLocalFile()) for url in event.mimeData().urls() ] for dictionary in sources: try: source_index = [d.path for d in dictionaries].index(dictionary.path) except ValueError: pass else: if source_index == dest_index: dest_index += 1 continue del dictionaries[source_index] if source_index < dest_index: dest_index -= 1 dictionaries.insert(dest_index, dictionary) dest_index += 1 self._update_dictionaries(dictionaries, keep_selection=False)
def test_loading_dictionaries(engine): def check_loaded_events(actual_events, expected_events): assert len(actual_events) == len(expected_events) for n, event in enumerate(actual_events): event_type, event_args, event_kwargs = event msg = 'event %u: %r' % (n, event) assert event_type == 'dictionaries_loaded', msg assert event_kwargs == {}, msg assert len(event_args) == 1, msg assert isinstance(event_args[0], StenoDictionaryCollection), msg assert [(d.path, d.enabled, isinstance(d, ErroredDictionary)) for d in event_args[0].dicts] == expected_events[n], msg with \ make_dict(b'{}', 'json', 'valid1') as valid_dict_1, \ make_dict(b'{}', 'json', 'valid2') as valid_dict_2, \ make_dict(b'', 'json', 'invalid1') as invalid_dict_1, \ make_dict(b'', 'json', 'invalid2') as invalid_dict_2: engine.start() for new_dictionaries, *expected_events in ( # Load one valid dictionary. [ [ # path, enabled (valid_dict_1, True), ], [ # path, enabled, errored (valid_dict_1, True, False), ] ], # Load another invalid dictionary. [[ (valid_dict_1, True), (invalid_dict_1, True), ], [ (valid_dict_1, True, False), (invalid_dict_1, True, True), ]], # Disable first dictionary. [[ (valid_dict_1, False), (invalid_dict_1, True), ], [ (valid_dict_1, False, False), (invalid_dict_1, True, True), ]], # Replace invalid dictonary with another invalid one. [[ (valid_dict_1, False), (invalid_dict_2, True), ], [ (valid_dict_1, False, False), ], [ (valid_dict_1, False, False), (invalid_dict_2, True, True), ]]): engine.events.clear() config_update = { 'dictionaries': [DictionaryConfig(*d) for d in new_dictionaries] } engine.config = dict(config_update) assert engine.events[0] == ('config_changed', (config_update, ), {}) check_loaded_events(engine.events[1:], expected_events) # Simulate an outdated dictionary. engine.events.clear() engine.dictionaries[valid_dict_1].timestamp -= 1 engine.config = {} check_loaded_events(engine.events, [[ (invalid_dict_2, True, True), ], [ (valid_dict_1, False, False), (invalid_dict_2, True, True), ]])
def config_dictionaries_from_state(state_str): return [ DictionaryConfig(path, enabled) for enabled, icon, path in parse_state(state_str) ]
def test_loading_dictionaries(self): def check_loaded_events(actual_events, expected_events): self.assertEqual(len(actual_events), len(expected_events), msg='events: %r' % self.events) for n, event in enumerate(actual_events): event_type, event_args, event_kwargs = event msg = 'event %u: %r' % (n, event) self.assertEqual(event_type, 'dictionaries_loaded', msg=msg) self.assertEqual(event_kwargs, {}, msg=msg) self.assertEqual(len(event_args), 1, msg=msg) self.assertIsInstance(event_args[0], StenoDictionaryCollection, msg=msg) self.assertEqual( [(d.path, d.enabled, isinstance(d, ErroredDictionary)) for d in event_args[0].dicts], expected_events[n], msg=msg) with \ make_dict(b'{}', 'json', 'valid1') as valid_dict_1, \ make_dict(b'{}', 'json', 'valid2') as valid_dict_2, \ make_dict(b'', 'json', 'invalid1') as invalid_dict_1, \ make_dict(b'', 'json', 'invalid2') as invalid_dict_2, \ self._setup(): self.engine.start() for test in ( # Load one valid dictionary. [ [ # path, enabled (valid_dict_1, True), ], [ # path, enabled, errored (valid_dict_1, True, False), ] ], # Load another invalid dictionary. [[ (valid_dict_1, True), (invalid_dict_1, True), ], [ (valid_dict_1, True, False), (invalid_dict_1, True, True), ]], # Disable first dictionary. [[ (valid_dict_1, False), (invalid_dict_1, True), ], [ (valid_dict_1, False, False), (invalid_dict_1, True, True), ]], # Replace invalid dictonary with another invalid one. [[ (valid_dict_1, False), (invalid_dict_2, True), ], [ (valid_dict_1, False, False), ], [ (valid_dict_1, False, False), (invalid_dict_2, True, True), ]]): config_dictionaries = [ DictionaryConfig(path, enabled) for path, enabled in test[0] ] self.events = [] config_update = { 'dictionaries': list(config_dictionaries), } self.engine.config = dict(config_update) self.assertEqual(self.events[0], ('config_changed', (config_update, ), {})) check_loaded_events(self.events[1:], test[1:]) # Simulate an outdated dictionary. self.events = [] self.engine.dictionaries[valid_dict_1].timestamp -= 1 self.engine.config = {} check_loaded_events(self.events, [[ (invalid_dict_2, True, True), ], [ (valid_dict_1, False, False), (invalid_dict_2, True, True), ]])
def test_config_dict(): short_path = os.path.normcase(os.path.normpath('~/foo/bar')) full_path = os.path.normcase( os.path.expanduser(os.path.normpath('~/foo/bar'))) # Path should be expanded. assert DictionaryConfig(short_path).path == full_path assert DictionaryConfig(full_path).path == full_path # Short path is available through `short_path`. assert DictionaryConfig(full_path).short_path == short_path assert DictionaryConfig(short_path).short_path == short_path # Enabled default to True. assert DictionaryConfig('foo').enabled assert not DictionaryConfig('foo', False).enabled # When converting to a dict (for dumping to JSON), # a dictionary with the shortened path is used. assert DictionaryConfig(full_path).to_dict() == \ {'path': short_path, 'enabled': True} assert DictionaryConfig(short_path, False).to_dict() == \ {'path': short_path, 'enabled': False} # Test from_dict creation helper. assert DictionaryConfig.from_dict({'path': short_path}) == \ DictionaryConfig(short_path) assert DictionaryConfig.from_dict({'path': full_path, 'enabled': False}) == \ DictionaryConfig(short_path, False)
False, 'enabled_extensions': set(), 'auto_start': False, 'machine_type': 'Keyboard', 'machine_specific_options': { 'arpeggiate': False }, 'system_name': config.DEFAULT_SYSTEM_NAME, 'system_keymap': DEFAULT_KEYMAP, 'dictionaries': [DictionaryConfig(p) for p in english_stenotype.DEFAULT_DICTIONARIES] } CONFIG_TESTS = ( ( 'defaults', ''' ''', DEFAULTS, {}, {}, ''' ''', ), ( 'simple_options',
def config(self): return DictionaryConfig(self.path, self.enabled)
class DictCommandsTest(unittest.TestCase): def setUp(self): self.spanish = DictionaryConfig('spanish/main.json') self.english = DictionaryConfig('main.json') self.commands = DictionaryConfig('commands.json') self.user = DictionaryConfig('user.json') self.extra = DictionaryConfig('extra.json') self.engine = FakeEngine([ self.user, self.commands, self.english, self.spanish, ]) solo_state[SOLO_ENABLED] = False solo_state[SOLO_DICT_HAS_RUN] = False self.tf = tempfile.NamedTemporaryFile(delete=False) pdc.BACKUP_DICTIONARY_PATH = self.tf.name def tearDown(self): try: os.unlink(self.tf.name) except OSError: # This file gets deleted by the module being tested right now, # leave this here in case that changes to delete temp file # if necessary. pass def test_priority_dict_shortest_path_is_default(self): priority_dict(self.engine, 'main.json') self.assertEqual(self.engine.config['dictionaries'], [ self.english, self.user, self.commands, self.spanish, ]) priority_dict(self.engine, 'spanish/main.json') self.assertEqual(self.engine.config['dictionaries'], [ self.spanish, self.english, self.user, self.commands, ]) def test_priority_dict_multiple(self): priority_dict(self.engine, 'user.json, spanish/main.json, commands.json') self.assertEqual(self.engine.config['dictionaries'], [ self.user, self.spanish, self.commands, self.english, ]) def test_priority_dict_invalid(self): with self.assertRaises(ValueError): priority_dict(self.engine, 'foobar.json') def test_toggle_dict_shortest_path_is_default(self): toggle_dict(self.engine, '+main.json, -spanish/main.json') self.assertEqual(self.engine.config['dictionaries'], [ self.user, self.commands, self.english.replace(enabled=True), self.spanish.replace(enabled=False), ]) def test_toggle_dict_multiple(self): toggle_dict(self.engine, '+spanish/main.json, !commands.json, -user.json') self.assertEqual(self.engine.config['dictionaries'], [ self.user.replace(enabled=False), self.commands.replace(enabled=False), self.english, self.spanish, ]) def test_toggle_dict_invalid_toggle(self): with self.assertRaises(ValueError): toggle_dict(self.engine, '=user.json') def test_toggle_dict_invalid_dictionary(self): with self.assertRaises(ValueError): toggle_dict(self.engine, '+foobar.json') def test_solo_dict(self): solo_dict(self.engine, '+spanish/main.json') self.assertEqual(self.engine.config['dictionaries'], [ self.user.replace(enabled=False), self.commands.replace(enabled=False), self.english.replace(enabled=False), self.spanish, ]) def test_end_solo_dict_doesnt_delete_new_dictionaries(self): solo_dict(self.engine, '+spanish/main.json') # ...then load a new dictionary while in the temporary mode dictionaries = self.engine.config['dictionaries'] dictionaries.append(self.extra) self.engine.config = {'dictionaries': dictionaries} end_solo_dict(self.engine, '') self.assertEqual(self.engine.config['dictionaries'], [ self.user, self.commands, self.english, self.spanish, self.extra, ]) pass def test_backup_dictionaries_to_json_and_reload(self): original_dictionaries = self.engine.config['dictionaries'] #import pdb; pdb.set_trace() backup_dictionary_stack(original_dictionaries, pdc.BACKUP_DICTIONARY_PATH) toggle_dict(self.engine, '-main.json') self.assertEqual( self.engine.config['dictionaries'], [ self.user, self.commands, self.english.replace(enabled=False), # turned off self.spanish, ]) restored_dictionaries = load_dictionary_stack_from_backup( pdc.BACKUP_DICTIONARY_PATH) self.engine.config = {'dictionaries': restored_dictionaries} self.assertEqual( self.engine.config['dictionaries'], [ self.user, self.commands, self.english, # turned back on again after restore self.spanish, ]) backup_dictionary_stack([], pdc.BACKUP_DICTIONARY_PATH) #clear the file for the next test def test_backed_up_dictionaries_restored_after_solo_if_backup_exists(self): toggle_dict(self.engine, '-main.json') #turned off before backup... original_dictionaries = self.engine.config['dictionaries'] backup_dictionary_stack(original_dictionaries, pdc.BACKUP_DICTIONARY_PATH) toggle_dict(self.engine, '+main.json') #but normal before solo_dict #Now that there's a backup file, do the first solo_dict since we've run... solo_dict(self.engine, '+spanish/main.json') end_solo_dict(self.engine, '') self.assertEqual( self.engine.config['dictionaries'], [ self.user, self.commands, self.english.replace( enabled=False), # turned back off again after restore self.spanish, ]) def test_end_solo_dict_restores_previous_state(self): toggle_dict(self.engine, '-main.json') solo_dict(self.engine, '+spanish/main.json') end_solo_dict(self.engine, '') self.assertEqual(self.engine.config['dictionaries'], [ self.user, self.commands, self.english.replace(enabled=False), self.spanish, ]) def test_end_solo_dict_without_first_doing_solo_1(self): backup_dictionary_stack([ self.spanish.replace(enabled=False), self.user.replace(enabled=False), ], pdc.BACKUP_DICTIONARY_PATH) end_solo_dict(self.engine, '') self.assertEqual(self.engine.config['dictionaries'], [ self.user.replace(enabled=False), self.commands, self.english, self.spanish.replace(enabled=False), ]) def test_end_solo_dict_without_first_doing_solo_2(self): backup_dictionary_stack([ self.extra, self.english.replace(enabled=False), ], pdc.BACKUP_DICTIONARY_PATH) end_solo_dict(self.engine, '') self.assertEqual(self.engine.config['dictionaries'], [ self.user, self.commands, self.english.replace(enabled=False), self.spanish, ])