diff -Nru ibus-table-1.9.18/configure.ac ibus-table-1.9.18.new/configure.ac --- ibus-table-1.9.18/configure.ac 2020-07-22 15:02:24.341755774 +0200 +++ ibus-table-1.9.18.new/configure.ac 2020-07-22 15:03:04.097344610 +0200 @@ -54,6 +54,15 @@ AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION(0.16.1) +AC_ARG_ENABLE([installed-tests], + [AS_HELP_STRING([--enable-installed-tests], + [Enable to install tests])], + [enable_installed_tests=$enableval], + [enable_installed_tests=no] +) + +AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], [test x"$enable_installed_tests" = x"yes"]) + # OUTPUT files AC_CONFIG_FILES([po/Makefile.in Makefile diff -Nru ibus-table-1.9.18/tests/.gitignore ibus-table-1.9.18.new/tests/.gitignore --- ibus-table-1.9.18/tests/.gitignore 2020-07-22 15:02:24.349755691 +0200 +++ ibus-table-1.9.18.new/tests/.gitignore 2020-07-22 15:11:24.740159555 +0200 @@ -1,5 +1,5 @@ run_tests -run_tests.log -run_tests.trs -test-suite.log +*.log +*.trs +*.tap __pycache__/ diff -Nru ibus-table-1.9.18/tests/Makefile.am ibus-table-1.9.18.new/tests/Makefile.am --- ibus-table-1.9.18/tests/Makefile.am 2020-07-22 15:02:24.349755691 +0200 +++ ibus-table-1.9.18.new/tests/Makefile.am 2020-07-22 15:04:28.298473415 +0200 @@ -19,20 +19,58 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # -TESTS = run_tests +TESTS = \ + test_it.py \ + test_0_gtk.py \ + $(NULL) + +check_SCRIPTS = run_tests + +LOG_COMPILER = $(builddir)/run_tests + +TESTS_ENVIRONMENT = \ + IBUS_TABLE_LOCATION=../ + +test_meta_in = meta.test.in +test_metas = + +if ENABLE_INSTALLED_TESTS +test_metas += $(patsubst %.py, %.test, $(TESTS)) +test_source_DATA = $(test_metas) +test_sourcedir = $(datadir)/installed-tests/ibus-table +test_exec_SCRIPTS = \ + $(TESTS) \ + gtkcases.py \ + mock_engine.py \ + run_tests \ + $(NULL) +test_execdir = $(libexecdir)/installed-tests/ibus-table + +$(test_metas): $(test_meta_in) + @TEST_EXEC=`echo $@ | sed -e 's&\.test&\.py&'`; \ + sed -e "s&@TEST_EXECDIR@&$(test_execdir)&g" \ + -e "s&@TEST_EXEC@&$$TEST_EXEC&g" $< > $@.tmp; \ + mv $@.tmp $@; \ + $(NULL) +endif run_tests: run_tests.in sed -e 's&@PYTHON_BIN@&$(PYTHON)&g' \ + -e 's&@PKGDATADIR@&$(pkgdatadir)&g' \ -e 's&@SRCDIR@&$(srcdir)&g' $< > $@ chmod +x $@ EXTRA_DIST = \ - run_tests.in \ - test_it.py \ + $(test_meta_in) \ __init__.py \ + gtkcases.py \ + mock_engine.py \ + run_tests.in \ + $(TESTS) \ $(NULL) CLEANFILES = \ + $(test_metas) \ run_tests \ $(NULL) diff -Nru ibus-table-1.9.18/tests/gtkcases.py ibus-table-1.9.18.new/tests/gtkcases.py --- ibus-table-1.9.18/tests/gtkcases.py 1970-01-01 01:00:00.000000000 +0100 +++ ibus-table-1.9.18.new/tests/gtkcases.py 2020-07-22 15:03:04.097344610 +0200 @@ -0,0 +1,38 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# 'init' has one array which is [keysym, keycode, modifier] and to be run +# before the main tests. E.g. +# Ctrl-space to enable Hiragana mode +# +# 'tests' cases are the main test cases. +# 'preedit' case runs to create a preedit text. +# 'lookup' case runs to update a lookup table. +# 'commit' case runs to commit the preedit text. +# 'result' case is the expected output. +# 'preedit', 'lookup', 'commit' can choose the type of either 'string' or 'keys' +# 'string' type is a string sequence which does not need modifiers + +from gi import require_version as gi_require_version +gi_require_version('IBus', '1.0') +from gi.repository import IBus + +TestCases = { + #'init': [IBus.KEY_j, 0, IBus.ModifierType.CONTROL_MASK], + 'tests': [ + {'preedit': {'string': 'a'}, + 'lookup': {'keys': [[IBus.KEY_Down, 0, 0]]}, + 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, + 'result': {'string': '区'} + }, + {'preedit': {'string': 'ijgl'}, + 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, + 'result': {'string': '漫画'} + }, + {'preedit': {'string': 'wgl'}, + 'lookup': {'keys': [[IBus.KEY_Down, 0, 0]]}, + 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, + 'result': {'string': '全国'} + }, + ] +} diff -Nru ibus-table-1.9.18/tests/meta.test.in ibus-table-1.9.18.new/tests/meta.test.in --- ibus-table-1.9.18/tests/meta.test.in 1970-01-01 01:00:00.000000000 +0100 +++ ibus-table-1.9.18.new/tests/meta.test.in 2020-07-22 15:03:04.097344610 +0200 @@ -0,0 +1,4 @@ +[Test] +Type=session +Exec=@TEST_EXECDIR@/run_tests @TEST_EXEC@ +Output=TAP diff -Nru ibus-table-1.9.18/tests/mock_engine.py ibus-table-1.9.18.new/tests/mock_engine.py --- ibus-table-1.9.18/tests/mock_engine.py 1970-01-01 01:00:00.000000000 +0100 +++ ibus-table-1.9.18.new/tests/mock_engine.py 2020-07-22 15:07:00.649895226 +0200 @@ -0,0 +1,241 @@ +# ibus-table - The Tables engine for IBus +# +# Copyright (c) 2018-2020 Mike FABIAN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + +''' +Define some mock classes for the unittests. +''' + +from gi import require_version +require_version('IBus', '1.0') +from gi.repository import IBus + +class MockEngine: + def __init__(self, engine_name = '', connection = None, object_path = ''): + self.mock_auxiliary_text = '' + self.mock_preedit_text = '' + self.mock_preedit_text_cursor_pos = 0 + self.mock_preedit_text_visible = True + self.mock_committed_text = '' + self.mock_committed_text_cursor_pos = 0 + self.client_capabilities = ( + IBus.Capabilite.PREEDIT_TEXT + | IBus.Capabilite.AUXILIARY_TEXT + | IBus.Capabilite.LOOKUP_TABLE + | IBus.Capabilite.FOCUS + | IBus.Capabilite.PROPERTY) + # There are lots of weird problems with surrounding text + # which makes this hard to test. Therefore this mock + # engine does not try to support surrounding text, i.e. + # we omit “| IBus.Capabilite.SURROUNDING_TEXT” here. + + def update_auxiliary_text(self, text, visible): + self.mock_auxiliary_text = text.text + + def hide_auxiliary_text(self): + pass + + def hide_preedit_text(self): + pass + + def commit_text(self, text): + self.mock_committed_text = ( + self.mock_committed_text[ + :self.mock_committed_text_cursor_pos] + + text.text + + self.mock_committed_text[ + self.mock_committed_text_cursor_pos:]) + self.mock_committed_text_cursor_pos += len(text.text) + + def forward_key_event(self, val, code, state): + if (val == IBus.KEY_Left + and self.mock_committed_text_cursor_pos > 0): + self.mock_committed_text_cursor_pos -= 1 + return + unicode = IBus.keyval_to_unicode(val) + if unicode: + self.mock_committed_text = ( + self.mock_committed_text[ + :self.mock_committed_text_cursor_pos] + + unicode + + self.mock_committed_text[ + self.mock_committed_text_cursor_pos:]) + self.mock_committed_text_cursor_pos += len(unicode) + + def update_lookup_table(self, table, visible): + pass + + def update_preedit_text(self, text, cursor_pos, visible): + self.mock_preedit_text = text.get_text() + self.mock_preedit_text_cursor_pos = cursor_pos + self.mock_preedit_text_visible = visible + + def register_properties(self, property_list): + pass + + def update_property(self, property): + pass + + def hide_lookup_table(self): + pass + +class MockLookupTable: + def __init__(self, page_size = 9, cursor_pos = 0, cursor_visible = False, round = True): + self.clear() + self.mock_page_size = page_size + self.mock_cursor_pos = cursor_pos + self.mock_cursor_visible = cursor_visible + self.cursor_visible = cursor_visible + self.mock_round = round + self.mock_candidates = [] + self.mock_labels = [] + self.mock_page_number = 0 + + def clear(self): + self.mock_candidates = [] + self.mock_cursor_pos = 0 + + def set_page_size(self, size): + self.mock_page_size = size + + def get_page_size(self): + return self.mock_page_size + + def set_round(self, round): + self.mock_round = round + + def set_cursor_pos(self, pos): + self.mock_cursor_pos = pos + + def get_cursor_pos(self): + return self.mock_cursor_pos + + def get_cursor_in_page(self): + return (self.mock_cursor_pos + - self.mock_page_size * self.mock_page_number) + + def set_cursor_visible(self, visible): + self.mock_cursor_visible = visible + self.cursor_visible = visible + + def cursor_down(self): + if len(self.mock_candidates): + self.mock_cursor_pos += 1 + self.mock_cursor_pos %= len(self.mock_candidates) + + def cursor_up(self): + if len(self.mock_candidates): + if self.mock_cursor_pos > 0: + self.mock_cursor_pos -= 1 + else: + self.mock_cursor_pos = len(self.mock_candidates) - 1 + + def page_down(self): + if len(self.mock_candidates): + self.mock_page_number += 1 + self.mock_cursor_pos += self.mock_page_size + + def page_up(self): + if len(self.mock_candidates): + if self.mock_page_number > 0: + self.mock_page_number -= 1 + self.mock_cursor_pos -= self.mock_page_size + + def set_orientation(self, orientation): + self.mock_orientation = orientation + + def get_number_of_candidates(self): + return len(self.mock_candidates) + + def append_candidate(self, candidate): + self.mock_candidates.append(candidate.get_text()) + + def get_candidate(self, index): + return self.mock_candidates[index] + + def get_number_of_candidates(self): + return len(self.mock_candidates) + + def append_label(self, label): + self.mock_labels.append(label.get_text()) + +class MockPropList: + def __init__(self, *args, **kwargs): + self._mock_proplist = [] + + def append(self, property): + self._mock_proplist.append(property) + + def get(self, index): + if index >= 0 and index < len(self._mock_proplist): + return self._mock_proplist[index] + else: + return None + + def update_property(self, property): + pass + +class MockProperty: + def __init__(self, + key='', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(''), + symbol=IBus.Text.new_from_string(''), + icon='', + tooltip=IBus.Text.new_from_string(''), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None): + self.mock_property_key = key + self.mock_property_prop_type = prop_type + self.mock_property_label = label.get_text() + self.mock_property_symbol = symbol.get_text() + self.mock_property_icon = icon + self.mock_property_tooltip = tooltip.get_text() + self.mock_property_sensitive = sensitive + self.mock_property_visible = visible + self.mock_property_state = state + self.mock_property_sub_props = sub_props + + def set_label(self, ibus_text): + self.mock_property_label = ibus_text.get_text() + + def set_symbol(self, ibus_text): + self.mock_property_symbol = ibus_text.get_text() + + def set_tooltip(self, ibus_text): + self.mock_property_tooltip = ibus_text.get_text() + + def set_icon(self, icon_path): + self.mock_property_icon = icon_path + + def set_sensitive(self, sensitive): + self.mock_property_sensitive = sensitive + + def set_visible(self, visible): + self.mock_property_visible = visible + + def set_state(self, state): + self.mock_property_state = state + + def set_sub_props(self, proplist): + self.mock_property_sub_props = proplist + + def get_key(self): + return self.mock_property_key diff -Nru ibus-table-1.9.18/tests/run_tests.in ibus-table-1.9.18.new/tests/run_tests.in --- ibus-table-1.9.18/tests/run_tests.in 2020-07-22 15:02:24.349755691 +0200 +++ ibus-table-1.9.18.new/tests/run_tests.in 2020-07-22 15:04:42.505326247 +0200 @@ -15,239 +15,47 @@ # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# +# License along with this library. If not, see -import os import sys +import os import unittest -from gi import require_version -require_version('IBus', '1.0') -from gi.repository import IBus - -# -- Define some mock classes for the tests ---------------------------------- -class MockEngine: - def __init__(self, engine_name = '', connection = None, object_path = ''): - self.mock_auxiliary_text = '' - self.mock_preedit_text = '' - self.mock_preedit_text_cursor_pos = 0 - self.mock_preedit_text_visible = True - self.mock_committed_text = '' - self.mock_committed_text_cursor_pos = 0 - self.client_capabilities = ( - IBus.Capabilite.PREEDIT_TEXT - | IBus.Capabilite.AUXILIARY_TEXT - | IBus.Capabilite.LOOKUP_TABLE - | IBus.Capabilite.FOCUS - | IBus.Capabilite.PROPERTY) - # There are lots of weird problems with surrounding text - # which makes this hard to test. Therefore this mock - # engine does not try to support surrounding text, i.e. - # we omit “| IBus.Capabilite.SURROUNDING_TEXT” here. - - def update_auxiliary_text(self, text, visible): - self.mock_auxiliary_text = text.text - - def hide_auxiliary_text(self): - pass - - def commit_text(self, text): - self.mock_committed_text = ( - self.mock_committed_text[ - :self.mock_committed_text_cursor_pos] - + text.text - + self.mock_committed_text[ - self.mock_committed_text_cursor_pos:]) - self.mock_committed_text_cursor_pos += len(text.text) - - def forward_key_event(self, val, code, state): - if (val == IBus.KEY_Left - and self.mock_committed_text_cursor_pos > 0): - self.mock_committed_text_cursor_pos -= 1 - return - unicode = IBus.keyval_to_unicode(val) - if unicode: - self.mock_committed_text = ( - self.mock_committed_text[ - :self.mock_committed_text_cursor_pos] - + unicode - + self.mock_committed_text[ - self.mock_committed_text_cursor_pos:]) - self.mock_committed_text_cursor_pos += len(unicode) - - def update_lookup_table(self, table, visible): - pass - - def update_preedit_text(self, text, cursor_pos, visible): - self.mock_preedit_text = text.get_text() - self.mock_preedit_text_cursor_pos = cursor_pos - self.mock_preedit_text_visible = visible - - def register_properties(self, property_list): - pass - - def update_property(self, property): - pass - - def hide_lookup_table(self): - pass - -class MockLookupTable: - def __init__(self, page_size = 9, cursor_pos = 0, cursor_visible = False, round = True): - self.clear() - self.mock_page_size = page_size - self.mock_cursor_pos = cursor_pos - self.mock_cursor_visible = cursor_visible - self.cursor_visible = cursor_visible - self.mock_round = round - self.mock_candidates = [] - self.mock_labels = [] - self.mock_page_number = 0 - - def clear(self): - self.mock_candidates = [] - self.mock_cursor_pos = 0 - - def set_page_size(self, size): - self.mock_page_size = size - - def get_page_size(self): - return self.mock_page_size - - def set_round(self, round): - self.mock_round = round - - def set_cursor_pos(self, pos): - self.mock_cursor_pos = pos - - def get_cursor_pos(self): - return self.mock_cursor_pos - - def get_cursor_in_page(self): - return (self.mock_cursor_pos - - self.mock_page_size * self.mock_page_number) - - def set_cursor_visible(self, visible): - self.mock_cursor_visible = visible - self.cursor_visible = visible - - def cursor_down(self): - if len(self.mock_candidates): - self.mock_cursor_pos += 1 - self.mock_cursor_pos %= len(self.mock_candidates) - - def cursor_up(self): - if len(self.mock_candidates): - if self.mock_cursor_pos > 0: - self.mock_cursor_pos -= 1 - else: - self.mock_cursor_pos = len(self.mock_candidates) - 1 - - def page_down(self): - if len(self.mock_candidates): - self.mock_page_number += 1 - self.mock_cursor_pos += self.mock_page_size - - def page_up(self): - if len(self.mock_candidates): - if self.mock_page_number > 0: - self.mock_page_number -= 1 - self.mock_cursor_pos -= self.mock_page_size - - def set_orientation(self, orientation): - self.mock_orientation = orientation - - def get_number_of_candidates(self): - return len(self.mock_candidates) - - def append_candidate(self, candidate): - self.mock_candidates.append(candidate.get_text()) - - def get_candidate(self, index): - return self.mock_candidates[index] - - def get_number_of_candidates(self): - return len(self.mock_candidates) - - def append_label(self, label): - self.mock_labels.append(label.get_text()) - -class MockPropList: - def __init__(self, *args, **kwargs): - self._mock_proplist = [] - - def append(self, property): - self._mock_proplist.append(property) - - def get(self, index): - if index >= 0 and index < len(self._mock_proplist): - return self._mock_proplist[index] - else: - return None - - def update_property(self, property): - pass - -class MockProperty: - def __init__(self, - key='', - prop_type=IBus.PropType.RADIO, - label=IBus.Text.new_from_string(''), - symbol=IBus.Text.new_from_string(''), - icon='', - tooltip=IBus.Text.new_from_string(''), - sensitive=True, - visible=True, - state=IBus.PropState.UNCHECKED, - sub_props=None): - self.mock_property_key = key - self.mock_property_prop_type = prop_type - self.mock_property_label = label.get_text() - self.mock_property_symbol = symbol.get_text() - self.mock_property_icon = icon - self.mock_property_tooltip = tooltip.get_text() - self.mock_property_sensitive = sensitive - self.mock_property_visible = visible - self.mock_property_state = state - self.mock_property_sub_props = sub_props - - def set_label(self, ibus_text): - self.mock_property_label = ibus_text.get_text() - - def set_symbol(self, ibus_text): - self.mock_property_symbol = ibus_text.get_text() - - def set_tooltip(self, ibus_text): - self.mock_property_tooltip = ibus_text.get_text() - - def set_sensitive(self, sensitive): - self.mock_property_sensitive = sensitive - - def set_visible(self, visible): - self.mock_property_visible = visible - - def set_state(self, state): - self.mock_property_state = state - - def set_sub_props(self, proplist): - self.mock_property_sub_props = proplist - - def get_key(self): - return self.mock_property_key - -# -- Monkey patch the environment with the mock classes ---------------------- -sys.modules["gi.repository.IBus"].Engine = MockEngine -sys.modules["gi.repository.IBus"].LookupTable = MockLookupTable -sys.modules["gi.repository.IBus"].Property = MockProperty -sys.modules["gi.repository.IBus"].PropList = MockPropList +# pip3 install tap.py --user +IMPORT_TAP_SUCCESSFUL = False +try: + from tap import TAPTestRunner + IMPORT_TAP_SUCCESSFUL = True +except (ImportError,): + pass + +if 'IBUS_TABLE_LOCATION' in os.environ: + location_path = os.environ['IBUS_TABLE_LOCATION'] + if location_path != None and location_path != '': + engine_path = os.path.join(location_path, 'engine') + sys.path.append(engine_path) +sys.path.append('@PKGDATADIR@/engine') # -- Load and run our unit tests --------------------------------------------- -os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' +pattern = 'test*.py' +start_dir = os.path.dirname(__file__) +if len(sys.argv) > 1: + pattern = sys.argv[-1] + dir = os.path.dirname(pattern) + pattern = os.path.basename(pattern) + if dir != '.': + start_dir = os.path.join(start_dir, dir) loader = unittest.TestLoader() -suite = loader.discover(".") -runner = unittest.TextTestRunner(stream = sys.stderr, verbosity = 255) +suite = loader.discover(start_dir=start_dir, pattern=pattern) + +if IMPORT_TAP_SUCCESSFUL: + runner = TAPTestRunner(stream=sys.stderr, verbosity=255) + runner.set_outdir('.') + runner.set_format('Hi: {method_name} - {short_description}') + runner.set_combined(True) +else: + runner = unittest.TextTestRunner(stream=sys.stderr, verbosity=255) + result = runner.run(suite) if result.failures or result.errors: diff -Nru ibus-table-1.9.18/tests/test_0_gtk.py ibus-table-1.9.18.new/tests/test_0_gtk.py --- ibus-table-1.9.18/tests/test_0_gtk.py 1970-01-01 01:00:00.000000000 +0100 +++ ibus-table-1.9.18.new/tests/test_0_gtk.py 2020-07-22 15:09:02.734630566 +0200 @@ -0,0 +1,441 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# ibus-table - The Tables engine for IBus +# +# Copyright (c) 2020 Takao Fujiwara +# Copyright (c) 2020 Mike FABIAN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +''' +This file implements the test cases using GTK GUI +''' +# “Wrong continued indentation”: pylint: disable=bad-continuation +# pylint: disable=attribute-defined-outside-init +# pylint: disable=missing-function-docstring +# pylint: disable=missing-class-docstring +# pylint: disable=global-statement +# pylint: disable=wrong-import-order +# pylint: disable=wrong-import-position + +import argparse +import os +import signal +import sys +import unittest + +from gi import require_version as gi_require_version +gi_require_version('GLib', '2.0') +gi_require_version('Gdk', '3.0') +gi_require_version('Gio', '2.0') +gi_require_version('Gtk', '3.0') +gi_require_version('IBus', '1.0') +from gi.repository import GLib +from gi.repository import Gdk +from gi.repository import Gio +from gi.repository import Gtk +from gi.repository import IBus + +# Get more verbose output in the test log: +os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' + +sys.path.insert(0, "../engine") +IMPORT_TABLE_SUCCESSFUL = False +try: + import table + IMPORT_TABLE_SUCCESSFUL = True +except (ImportError,): + pass +IMPORT_TABSQLITEDB_SUCCESSFUL = False +try: + import tabsqlitedb + IMPORT_TABSQLITEDB_SUCCESSFUL = True +except (ImportError,): + pass +# FIXME: +#sys.path.pop(0) + +DONE_EXIT = True +ENGINE_NAME = 'wubi-jidian86' + +from gtkcases import TestCases + +# Need to flush the output against Gtk.main() +def printflush(sentence): + try: + print(sentence, flush=True) + except IOError: + pass + +def printerr(sentence): + try: + print(sentence, flush=True, file=sys.stderr) + except IOError: + pass + +@unittest.skipUnless( + os.path.isfile( + os.path.join('/usr/share/ibus-table/tables', ENGINE_NAME + '.db')), + '%s.db is not installed.' % ENGINE_NAME + '.db') +@unittest.skipUnless( + 'XDG_SESSION_TYPE' in os.environ + and os.environ['XDG_SESSION_TYPE'] in ('x11', 'wayland'), + 'XDG_SESSION_TYPE is neither "x11" nor "wayland".') +@unittest.skipIf(Gdk.Display.open('') is None, 'Display cannot be opened.') +class SimpleGtkTestCase(unittest.TestCase): + global DONE_EXIT + global ENGINE_NAME + ENGINE_PATH = '/com/redhat/IBus/engines/table/Test/Engine' + + @classmethod + def setUpClass(cls): + cls._flag = False + IBus.init() + cls._gsettings = Gio.Settings( + schema='org.freedesktop.ibus.engine.table', + path='/org/freedesktop/ibus/engine/table/%s/' % ENGINE_NAME) + cls._orig_chinesemode = cls._gsettings.get_int('chinesemode') + signums = [getattr(signal, s, None) for s in + 'SIGINT SIGTERM SIGHUP'.split()] + for signum in filter(None, signums): + original_handler = signal.getsignal(signum) + GLib.unix_signal_add(GLib.PRIORITY_HIGH, + signum, + cls.signal_handler, + (signum, original_handler)) + @classmethod + def tearDownClass(cls): + cls._gsettings.set_int('chinesemode', cls._orig_chinesemode) + + @classmethod + def signal_handler(cls, user_data): + (signum, original_handler) = user_data + cls.tearDownClass() + Gtk.main_quit() + signal.signal(signum, original_handler) + cls._flag = True + assert False, 'signal received: ' + str(signum) + + def setUp(self): + self.__id = 0 + self.__rerun = False + self.__test_index = 0 + self.__preedit_index = 0 + self.__lookup_index = 0 + self.__inserted_text = '' + self.__commit_done = False + self.__reset_coming = False + self._gsettings.set_int('chinesemode', 4) + + def register_ibus_engine(self): + self.__bus = IBus.Bus() + if not self.__bus.is_connected(): + self.fail('ibus-daemon is not running') + return False + self.__bus.get_connection().signal_subscribe( + 'org.freedesktop.DBus', + 'org.freedesktop.DBus', + 'NameOwnerChanged', + '/org/freedesktop/DBus', + None, + 0, + self.__bus_signal_cb, + self.__bus) + self.__factory = IBus.Factory( + object_path=IBus.PATH_FACTORY, + connection=self.__bus.get_connection()) + self.__factory.connect('create-engine', self.__create_engine_cb) + self.__component = IBus.Component( + name='org.freedesktop.IBus.Table.Test', + description='Test Table Component', + version='1.0', + license='GPL', + author=('Mike FABIAN , ' + + 'Caius "kaio" CHANCE '), + homepage='http://mike-fabian.github.io/ibus-table/', + command_line='', + textdomain='ibus-table') + desc = IBus.EngineDesc( + name=ENGINE_NAME, + longname='Test Table %s' % ENGINE_NAME, + description='Test Table Component', + language='t', + license='GPL', + author=('Mike FABIAN , ' + + 'Caius "kaio" CHANCE '), + icon='', + symbol='T') + self.__component.add_engine(desc) + self.__bus.register_component(self.__component) + self.__bus.request_name('org.freedesktop.IBus.Table.Test', 0) + return True + + def __bus_signal_cb(self, connection, sender_name, object_path, + interface_name, signal_name, parameters, + user_data): + if signal_name == 'NameOwnerChanged': + pass + if signal_name == 'UpdateLookupTable': + table = self.__engine._editor.get_lookup_table() + if table.get_number_of_candidates() == 0: + return + self.__lookup_test() + + def __create_engine_cb(self, factory, engine_name): + if engine_name != ENGINE_NAME: + return None + if (not IMPORT_TABLE_SUCCESSFUL + or not IMPORT_TABSQLITEDB_SUCCESSFUL): + with self.subTest(i='create-engine'): + self.fail('NG: ibus-table not installed?') + Gtk.main_quit() + return None + self.__id += 1 + object_path = '%s/%d' % (self.ENGINE_PATH, self.__id) + db_dir = '/usr/share/ibus-table/tables' + db_file = os.path.join(db_dir, engine_name + '.db') + database = tabsqlitedb.TabSqliteDb(filename=db_file, user_db=':memory:') + self.__engine = table.TabEngine( + self.__bus, + object_path, + database) + self.__engine.connect('focus-in', self.__engine_focus_in) + self.__engine.connect('focus-out', self.__engine_focus_out) + # FIXME: Need to connect 'reset' after TabEngine.clear_all_input_and_preedit() + # is called. + self.__engine.connect_after('reset', self.__engine_reset) + self.__bus.get_connection().signal_subscribe( + None, + IBus.INTERFACE_ENGINE, + 'UpdateLookupTable', + object_path, + None, + 0, + self.__bus_signal_cb, + self.__bus) + return self.__engine + + def __engine_focus_in(self, _engine): + if self.__test_index == len(TestCases['tests']): + if DONE_EXIT: + Gtk.main_quit() + return + # Workaround because focus-out resets the preedit text + # ibus_bus_set_global_engine() calls bus_input_context_set_engine() + # twice and it causes bus_engine_proxy_focus_out() + if self.__rerun: + self.__rerun = False + self.__main_test() + + def __engine_focus_out(self, _engine): + self.__rerun = True + self.__test_index = 0 + self.__entry.set_text('') + + def __engine_reset(self, _engine): + if self.__reset_coming: + self.__reset_coming = False + self.__main_test() + + def __entry_focus_in_event_cb(self, entry, event): + if self.__test_index == len(TestCases['tests']): + if DONE_EXIT: + Gtk.main_quit() + return False + self.__bus.set_global_engine_async(ENGINE_NAME, + -1, None, self.__set_engine_cb) + return False + + def __set_engine_cb(self, _object, res): + with self.subTest(i=self.__test_index): + if not self.__bus.set_global_engine_async_finish(res): + self.fail('set engine failed.') + return + # rerun always happen? + #self.__main_test() + + def __get_test_condition_length(self, tag): + tests = TestCases['tests'][self.__test_index] + try: + cases = tests[tag] + except KeyError: + return -1 + case_type = list(cases.keys())[0] + return len(cases[case_type]) + + def __entry_preedit_changed_cb(self, entry, preedit_str): + if len(preedit_str) == 0: + return + if self.__test_index == len(TestCases['tests']): + if DONE_EXIT: + Gtk.main_quit() + return + self.__preedit_index += 1 + if self.__preedit_index != self.__get_test_condition_length('preedit'): + return + if self.__get_test_condition_length('lookup') > 0: + return + self.__run_cases('commit') + + def __main_test(self): + self.__preedit_index = 0 + self.__lookup_index = 0 + self.__commit_done = False + self.__run_cases('preedit') + + def __lookup_test(self): + lookup_length = self.__get_test_condition_length('lookup') + # Need to return again even if all the lookup is finished + # until the final Engine.update_preedit() is called. + if self.__lookup_index > lookup_length: + return + self.__run_cases('lookup', + self.__lookup_index, + self.__lookup_index + 1) + if self.__lookup_index < lookup_length: + self.__lookup_index += 1 + return + self.__lookup_index += 1 + self.__run_cases('commit') + + def __run_cases(self, tag, start=-1, end=-1): + tests = TestCases['tests'][self.__test_index] + if tests is None: + return + try: + cases = tests[tag] + except KeyError: + return + case_type = list(cases.keys())[0] + i = 0 + if case_type == 'string': + printflush('test step: %s sequences: "%s"' + % (tag, str(cases['string']))) + for character in cases['string']: + if start >= 0 and i < start: + i += 1 + continue + if 0 <= end <= i: + break + self.__typing(ord(character), 0, 0) + i += 1 + if case_type == 'keys': + if start == -1 and end == -1: + printflush('test step: %s sequences: %s' + % (tag, str(cases['keys']))) + for key in cases['keys']: + if start >= 0 and i < start: + i += 1 + continue + if 0 <= end <= i: + break + if start != -1 or end != -1: + printflush('test step: %s sequences: [0x%X, 0x%X, 0x%X]' + % (tag, key[0], key[1], key[2])) + self.__typing(key[0], key[1], key[2]) + i += 1 + + def __typing(self, keyval, keycode, modifiers): + self.__engine.emit('process-key-event', keyval, keycode, modifiers) + modifiers |= IBus.ModifierType.RELEASE_MASK + self.__engine.emit('process-key-event', keyval, keycode, modifiers) + + def __buffer_inserted_text_cb(self, buffer, position, chars, nchars): + tests = TestCases['tests'][self.__test_index] + cases = tests['commit'] + case_type = list(cases.keys())[0] + if case_type == 'keys': + # space key is sent separatedly later + if cases['keys'][0] == [IBus.KEY_space, 0, 0]: + self.__inserted_text += chars + # FIXME: Return key emits 'reset' signal in GTK and it calls + # TableEngine.clear_all_input_and_preedit(). + elif cases['keys'][0] == [IBus.KEY_Return, 0, 0] or \ + cases['keys'][0] == [IBus.KEY_KP_Enter, 0, 0] or \ + cases['keys'][0] == [IBus.KEY_ISO_Enter, 0, 0] or \ + cases['keys'][0] == [IBus.KEY_Escape, 0, 0]: + self.__inserted_text = chars + self.__reset_coming = True + else: + self.__inserted_text = chars + cases = tests['result'] + if cases['string'] == self.__inserted_text: + printflush('OK: %d "%s"' + % (self.__test_index, self.__inserted_text)) + else: + if DONE_EXIT: + Gtk.main_quit() + with self.subTest(i=self.__test_index): + self.fail('NG: %d "%s" "%s"' + % (self.__test_index, str(cases['string']), + self.__inserted_text)) + self.__inserted_text = '' + self.__test_index += 1 + if self.__test_index == len(TestCases['tests']): + if DONE_EXIT: + Gtk.main_quit() + return + self.__commit_done = True + self.__entry.set_text('') + if not self.__reset_coming: + self.__main_test() + + def create_window(self): + window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL) + self.__entry = entry = Gtk.Entry() + window.connect('destroy', Gtk.main_quit) + entry.connect('focus-in-event', self.__entry_focus_in_event_cb) + entry.connect('preedit-changed', self.__entry_preedit_changed_cb) + buffer = entry.get_buffer() + buffer.connect('inserted-text', self.__buffer_inserted_text_cb) + window.add(entry) + window.show_all() + + def main(self): # pylint: disable=no-self-use + # Some ATK relative warnings are called during launching GtkWindow. + flags = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL) + Gtk.main() + GLib.log_set_always_fatal(flags) + + def test_typing(self): + if not self.register_ibus_engine(): + sys.exit(-1) + self.create_window() + self.main() + if self._flag: + self.fail('NG: signal failure') + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-k', '--keep', action='store_true', + help='keep this GtkWindow after test is done') + parser.add_argument('-F', '--unittest-failfast', action='store_true', + help='stop on first fail or error in unittest') + parser.add_argument('-H', '--unittest-help', action='store_true', + help='show unittest help message and exit') + args, unittest_args = parser.parse_known_args() + sys.argv[1:] = unittest_args + if args.keep: + global DONE_EXIT + DONE_EXIT = False + if args.unittest_failfast: + sys.argv.append('-f') + if args.unittest_help: + sys.argv.append('-h') + unittest.main() + + unittest.main() + +if __name__ == '__main__': + main() diff -Nru ibus-table-1.9.18/tests/test_it.py ibus-table-1.9.18.new/tests/test_it.py --- ibus-table-1.9.18/tests/test_it.py 2020-07-22 15:02:24.350755681 +0200 +++ ibus-table-1.9.18.new/tests/test_it.py 2020-07-22 15:05:43.128698254 +0200 @@ -1,6 +1,5 @@ -# -*- coding: utf-8 -*- -# vim:et sts=4 sw=4 -# +#!/usr/bin/python3 + # ibus-table - The Tables engine for IBus # # Copyright (c) 2018 Mike FABIAN @@ -26,19 +25,41 @@ import sys import os -import unicodedata import unittest -import subprocess +import importlib +import mock from gi import require_version require_version('IBus', '1.0') from gi.repository import IBus +# Get more verbose output in the test log: +os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' + +# Monkey patch the environment with the mock classes: +from mock_engine import MockEngine +from mock_engine import MockLookupTable +from mock_engine import MockProperty +from mock_engine import MockPropList + sys.path.insert(0, "../engine") -from table import * +import table import tabsqlitedb -import it_util -#sys.path.pop(0) +import ibus_table_location +sys.path.pop(0) + +ENGINE_PATCHER = mock.patch.object( + IBus, 'Engine', new=MockEngine) +LOOKUP_TABLE_PATCHER = mock.patch.object( + IBus, 'LookupTable', new=MockLookupTable) +PROPERTY_PATCHER = mock.patch.object( + IBus, 'Property', new=MockProperty) +PROP_LIST_PATCHER = mock.patch.object( + IBus, 'PropList', new=MockPropList) +IBUS_ENGINE = IBus.Engine +IBUS_LOOKUP_TABLE = IBus.LookupTable +IBUS_PROPERTY = IBus.Property +IBUS_PROP_LIST = IBus.PropList ENGINE = None TABSQLITEDB = None @@ -56,6 +77,8 @@ ORIG_AUTOWILDCARD_MODE = None ORIG_SINGLE_WILDCARD_CHAR = None ORIG_MULTI_WILDCARD_CHAR = None +ORIG_PINYIN_MODE = None +ORIG_SUGGESTION_MODE = None def backup_original_settings(): global ENGINE @@ -73,6 +96,8 @@ global ORIG_AUTOWILDCARD_MODE global ORIG_SINGLE_WILDCARD_CHAR global ORIG_MULTI_WILDCARD_CHAR + global ORIG_PINYIN_MODE + global ORIG_SUGGESTION_MODE ORIG_INPUT_MODE = ENGINE.get_input_mode() ORIG_CHINESE_MODE = ENGINE.get_chinese_mode() ORIG_LETTER_WIDTH = ENGINE.get_letter_width() @@ -87,6 +112,8 @@ ORIG_AUTOWILDCARD_MODE = ENGINE.get_autowildcard_mode() ORIG_SINGLE_WILDCARD_CHAR = ENGINE.get_single_wildcard_char() ORIG_MULTI_WILDCARD_CHAR = ENGINE.get_multi_wildcard_char() + ORIG_PINYIN_MODE = ENGINE.get_pinyin_mode() + ORIG_SUGGESTION_MODE = ENGINE.get_suggestion_mode() def restore_original_settings(): global ENGINE @@ -104,6 +131,8 @@ global ORIG_AUTOWILDCARD_MODE global ORIG_SINGLE_WILDCARD_CHAR global ORIG_MULTI_WILDCARD_CHAR + global ORIG_PINYIN_MODE + global ORIG_SUGGESTION_MODE ENGINE.set_input_mode(ORIG_INPUT_MODE) ENGINE.set_chinese_mode(ORIG_CHINESE_MODE) ENGINE.set_letter_width(ORIG_LETTER_WIDTH[0], input_mode=0) @@ -120,14 +149,16 @@ ENGINE.set_autowildcard_mode(ORIG_AUTOWILDCARD_MODE) ENGINE.set_single_wildcard_char(ORIG_SINGLE_WILDCARD_CHAR) ENGINE.set_multi_wildcard_char(ORIG_MULTI_WILDCARD_CHAR) + ENGINE.set_pinyin_mode(ORIG_PINYIN_MODE) + ENGINE.set_suggestion_mode(ORIG_SUGGESTION_MODE) def set_default_settings(): global ENGINE global TABSQLITEDB ENGINE.set_input_mode(mode=1) - chinese_mode = 0 + chinese_mode = 4 language_filter = TABSQLITEDB.ime_properties.get('language_filter') - if language_filter in ['cm0', 'cm1', 'cm2', 'cm3', 'cm4']: + if language_filter in ('cm0', 'cm1', 'cm2', 'cm3', 'cm4'): chinese_mode = int(language_filter[-1]) ENGINE.set_chinese_mode(mode=chinese_mode) @@ -136,7 +167,7 @@ 'def_full_width_letter') if def_full_width_letter: letter_width_mode = (def_full_width_letter.lower() == u'true') - ENGINE.set_letter_width(mode=letter_width_mode, input_mode=0) + ENGINE.set_letter_width(mode=False, input_mode=0) ENGINE.set_letter_width(mode=letter_width_mode, input_mode=1) punctuation_width_mode = False @@ -144,7 +175,7 @@ 'def_full_width_punct') if def_full_width_punct: punctuation_width_mode = (def_full_width_punct.lower() == u'true') - ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=0) + ENGINE.set_punctuation_width(mode=False, input_mode=0) ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=1) always_show_lookup_mode = True @@ -161,7 +192,8 @@ select_keys_csv = TABSQLITEDB.ime_properties.get('select_keys') # select_keys_csv is something like: "1,2,3,4,5,6,7,8,9,0" if select_keys_csv: - ENGINE.set_page_size(len(select_keys_csv.split(","))) + page_size = len(select_keys_csv.split(",")) + ENGINE.set_page_size(page_size) onechar = False ENGINE.set_onechar_mode(onechar) @@ -223,28 +255,98 @@ multi_wildcard_char = multi_wildcard_char[0] ENGINE.set_multi_wildcard_char(multi_wildcard_char) + ENGINE.set_pinyin_mode(False) + ENGINE.set_suggestion_mode(False) + def set_up(engine_name): + ''' + Setup an ibus table engine + + :param engine_name: The name of the engine to setup + :type engine_name: String + :return: True if the engine could be setup successfully, False if not. + :rtype: Boolean + ''' + global ENGINE_PATCHER + global LOOKUP_TABLE_PATCHER + global PROPERTY_PATCHER + global PROP_LIST_PATCHER + global IBUS_ENGINE + global IBUS_LOOKUP_TABLE + global IBUS_PROPERTY + global IBUS_PROP_LIST global TABSQLITEDB global ENGINE + ENGINE_PATCHER.start() + LOOKUP_TABLE_PATCHER.start() + PROPERTY_PATCHER.start() + PROP_LIST_PATCHER.start() + assert IBus.Engine is not IBUS_ENGINE + assert IBus.Engine is MockEngine + assert IBus.LookupTable is not IBUS_LOOKUP_TABLE + assert IBus.LookupTable is MockLookupTable + assert IBus.Property is not IBUS_PROPERTY + assert IBus.Property is MockProperty + assert IBus.PropList is not IBUS_PROP_LIST + assert IBus.PropList is MockPropList + # Reload the table module so that the patches + # are applied to TabEngine: + sys.path.insert(0, '../engine') + importlib.reload(table) + sys.path.pop(0) bus = IBus.Bus() db_dir = '/usr/share/ibus-table/tables' db_file = os.path.join(db_dir, engine_name + '.db') - TABSQLITEDB = tabsqlitedb.tabsqlitedb( + if not os.path.isfile(db_file): + TABSQLITEDB = None + ENGINE = None + tear_down() + return False + TABSQLITEDB = tabsqlitedb.TabSqliteDb( filename=db_file, user_db=':memory:') - ENGINE = tabengine( + ENGINE = table.TabEngine( bus, '/com/redhat/IBus/engines/table/%s/engine/0' %engine_name, TABSQLITEDB, - unit_test = True) + unit_test=True) backup_original_settings() set_default_settings() + return True def tear_down(): + global ENGINE_PATCHER + global LOOKUP_TABLE_PATCHER + global PROPERTY_PATCHER + global PROP_LIST_PATCHER + global IBUS_ENGINE + global IBUS_LOOKUP_TABLE + global IBUS_PROPERTY + global IBUS_PROP_LIST + global TABSQLITEDB + global ENGINE + if ENGINE: restore_original_settings() + TABSQLITEDB = None + ENGINE = None + # Remove the patches from the IBus stuff: + ENGINE_PATCHER.stop() + LOOKUP_TABLE_PATCHER.stop() + PROPERTY_PATCHER.stop() + PROP_LIST_PATCHER.stop() + assert IBus.Engine is IBUS_ENGINE + assert IBus.Engine is not MockEngine + assert IBus.LookupTable is IBUS_LOOKUP_TABLE + assert IBus.LookupTable is not MockLookupTable + assert IBus.Property is IBUS_PROPERTY + assert IBus.Property is not MockProperty + assert IBus.PropList is IBUS_PROP_LIST + assert IBus.PropList is not MockPropList -class Wubi_Jidian86TestCase(unittest.TestCase): +class WubiJidian86TestCase(unittest.TestCase): def setUp(self): - set_up('wubi-jidian86') + engine_name = 'wubi-jidian86' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) def tearDown(self): tear_down() @@ -257,7 +359,81 @@ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) self.assertEqual(ENGINE.mock_committed_text, '工') - def test_commit_to_preedit_and_switching_to_pinyin_and_defining_a_phrase(self): + def test_pinyin_mode(self): + # Pinyin mode is False by default: + self.assertEqual(ENGINE.get_pinyin_mode(), False) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '工') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工') + ENGINE.set_pinyin_mode(True) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '爱') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工爱') + ENGINE.set_pinyin_mode(False) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '工') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工爱工') + + def test_suggestion_mode(self): + if not ENGINE._ime_sg: + self.skipTest("This engine does not have a suggestion mode.") + # Suggestion mode is False by default: + self.assertEqual(ENGINE.get_suggestion_mode(), False) + self.assertEqual(ENGINE.get_pinyin_mode(), False) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '工') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工') + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + ENGINE.set_suggestion_mode(True) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '工') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工工') + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['工作人员 673 0', + '工作会议 310 0', + '工作报告 267 0', + '工人阶级 146 0', + '工作重点 78 0', + '工作小组 73 0', + '工业企业 71 0', + '工业大学 69 0', + '工作单位 61 0', + '工业生产 58 0']) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工工作人员') + ENGINE.set_pinyin_mode(True) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '爱') + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱') + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['爱因斯坦 1109 0', + '爱情故事 519 0', + '爱国主义 191 0', + '爱尔兰语 91 0', + '爱好和平 62 0', + '爱情小说 58 0', + '爱不释手 39 0', + '爱国热情 35 0', + '爱莫能助 34 0', + '爱理不理 32 0']) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱因斯坦') + + def test_commit_to_preedit_switching_to_pinyin_defining_a_phrase(self): ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) # commit to preëdit needs a press and release of either # the left or the right shift key: @@ -324,7 +500,8 @@ IBus.KEY_Shift_L, 0, IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) self.assertEqual(ENGINE.mock_preedit_text, '工了你好以在') - # Move right two characters in the preëdit (triggers a commit to preëdit): + # Move right two characters in the preëdit + # (triggers a commit to preëdit): ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) self.assertEqual(ENGINE.mock_auxiliary_text, 'd dhf dhfd\t#: abwd') @@ -353,9 +530,88 @@ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) self.assertEqual(ENGINE.mock_committed_text, '工了你好以在工了你好以在') + def test_chinese_mode(self): + ENGINE.set_chinese_mode(mode=0) # show simplified Chinese only + ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['以 418261033 0', + '能 ex 1820000000 0', + '能 exx 1820000000 0', + '对 fy 1200000000 0', + '又 cc 729000000 0', + '又 ccc 729000000 0', + '通 ep 521000000 0', + '通 epk 521000000 0', + '台 kf 486000000 0', + '难忘 wyn 404000000 0']) + ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + ENGINE.set_chinese_mode(mode=1) # show traditional Chinese only + ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['以 418261033 0', + '能 ex 1820000000 0', + '能 exx 1820000000 0', + '又 cc 729000000 0', + '又 ccc 729000000 0', + '通 ep 521000000 0', + '通 epk 521000000 0', + '台 kf 486000000 0', + '能 e 306980312 0', + '能力 elt 274000000 0']) + ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + ENGINE.set_chinese_mode(mode=2) # show simplified Chinese first + ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['以 418261033 0', + '能 ex 1820000000 0', + '能 exx 1820000000 0', + '对 fy 1200000000 0', + '又 cc 729000000 0', + '又 ccc 729000000 0', + '通 ep 521000000 0', + '通 epk 521000000 0', + '台 kf 486000000 0', + '难忘 wyn 404000000 0']) + ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + ENGINE.set_chinese_mode(mode=3) # show traditional Chinese first + ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['以 418261033 0', + '能 ex 1820000000 0', + '能 exx 1820000000 0', + '又 cc 729000000 0', + '又 ccc 729000000 0', + '通 ep 521000000 0', + '通 epk 521000000 0', + '台 kf 486000000 0', + '能 e 306980312 0', + '能力 elt 274000000 0']) + ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + ENGINE.set_chinese_mode(mode=4) # show all characters + ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['以 418261033 0', + '能 ex 1820000000 0', + '能 exx 1820000000 0', + '对 fy 1200000000 0', + '又 cc 729000000 0', + '又 ccc 729000000 0', + '通 ep 521000000 0', + '通 epk 521000000 0', + '台 kf 486000000 0', + '难忘 wyn 404000000 0']) + ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) + class Stroke5TestCase(unittest.TestCase): def setUp(self): - set_up('stroke5') + engine_name = 'stroke5' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) def tearDown(self): tear_down() @@ -372,9 +628,56 @@ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) self.assertEqual(ENGINE.mock_committed_text, '的') +class TelexTestCase(unittest.TestCase): + def setUp(self): + engine_name = 'telex' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) + + def tearDown(self): + tear_down() + + def test_dummy(self): + self.assertEqual(True, True) + + def test_telex(self): + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'o') + self.assertEqual(ENGINE.mock_committed_text, '') + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'o') + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_f, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'oò') + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'ô') + self.assertEqual(ENGINE.mock_committed_text, 'oò') + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'oòô') + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'ô') + self.assertEqual(ENGINE.mock_committed_text, 'oòô') + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'oòôô') + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'ô') + self.assertEqual(ENGINE.mock_committed_text, 'oòôô') + ENGINE.do_process_key_event(IBus.KEY_j, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'oòôôộ') + class TranslitTestCase(unittest.TestCase): def setUp(self): - set_up('translit') + engine_name ='translit' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) def tearDown(self): tear_down() @@ -401,3 +704,215 @@ self.assertEqual(ENGINE.mock_preedit_text, 'с') ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) self.assertEqual(ENGINE.mock_committed_text, 'шщс ') + + def test_sh_multiple_match_slavic(self): + ENGINE.do_process_key_event(IBus.KEY_scaron, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'ш') + self.assertEqual(ENGINE.mock_committed_text, '') + ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'щ') + ENGINE.do_process_key_event(IBus.KEY_scaron, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, 'ш') + self.assertEqual(ENGINE.mock_committed_text, 'щ') + ENGINE.do_process_key_event(IBus.KEY_ccaron, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '') + self.assertEqual(ENGINE.mock_committed_text, 'щщ') + +class Cangjie5TestCase(unittest.TestCase): + def setUp(self): + engine_name = 'cangjie5' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) + + def tearDown(self): + tear_down() + + def test_dummy(self): + self.assertEqual(True, True) + + def test_single_char_commit_with_space(self): + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, '日') + + def test_type_one_char_and_check_auxiliary(self): + ENGINE.do_process_key_event(IBus.KEY_d, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '木') + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates[8], + '林 木 1000 0') + ENGINE.do_process_key_event(IBus.KEY_v, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_i, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_i, 0, 0) + self.assertEqual(ENGINE.mock_preedit_text, '機') + self.assertEqual(ENGINE.mock_auxiliary_text, '木女戈戈 (1 / 1)') + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['機 1000 0']) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, '機') + +class IpaXSampaTestCase(unittest.TestCase): + def setUp(self): + engine_name = 'ipa-x-sampa' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) + + def tearDown(self): + tear_down() + + def test_dummy(self): + self.assertEqual(True, True) + + def test_single_char_commit_with_space(self): + ENGINE.do_process_key_event(IBus.KEY_at, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'ə ') + + def test_single_char_commit_with_f3(self): + ENGINE.do_process_key_event(IBus.KEY_at, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['ə 0 0', 'ɘ \\ 0 0', 'ɚ ` 0 0']) + ENGINE.do_process_key_event(IBus.KEY_F3, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'ɚ') + +class LatexTestCase(unittest.TestCase): + def setUp(self): + engine_name = 'latex' + if not set_up(engine_name): + self.skipTest('Could not setup “%s”, skipping test.' % engine_name) + + def tearDown(self): + tear_down() + + def test_dummy(self): + self.assertEqual(True, True) + + def test_single_char_commit_with_space(self): + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_l, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_p, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'α') + + def test_single_char_commit_with_space_fraktur(self): + # needs ibus-table-others-1.3.10 which adds + # most of Unicode 9.0 block Mathematical Alphanumeric Symbols + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_m, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_t, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_f, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_r, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_k, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_F, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, '𝔉') + + def test_single_char_commit_with_f3(self): + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) + # Lookup table shows only the first page, subsequent + # pages are added on demand as a speed optimization: + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['¯ ar 0 0', + '⊥ ot 0 0', + 'β eta 0 0', + 'ℶ eth 0 0', + '⋂ igcap 0 0', + '⋃ igcup 0 0', + '⋁ igvee 0 0', + '⋈ owtie 0 0', + '⊡ oxdot 0 0']) + ENGINE.do_process_key_event(IBus.KEY_F3, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'β') + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Page_Down, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['β eta 0 1', # user freq for β increased to 1 + '¯ ar 0 0', + '⊥ ot 0 0', + 'ℶ eth 0 0', + '⋂ igcap 0 0', + '⋃ igcup 0 0', + '⋁ igvee 0 0', + '⋈ owtie 0 0', + '⊡ oxdot 0 0', + '• ullet 0 0', + '∙ ullet 0 0', + '≏ umpeq 0 0', + '∽ acksim 0 0', + '∵ ecause 0 0', + '≬ etween 0 0', + '⊞ oxplus 0 0', + '⊼ arwedge 0 0', + '⋀ igwedge 0 0']) + self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 9) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 15) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates[0:18], + ['β eta 0 1', # user freq for β increased to 1 + '¯ ar 0 0', + '⊥ ot 0 0', + 'ℶ eth 0 0', + '⋂ igcap 0 0', + '⋃ igcup 0 0', + '⋁ igvee 0 0', + '⋈ owtie 0 0', + '⊡ oxdot 0 0', + '• ullet 0 0', + '∙ ullet 0 0', + '≏ umpeq 0 0', + '∽ acksim 0 0', + '∵ ecause 0 0', + '≬ etween 0 0', + '⊞ oxplus 0 0', + '⊼ arwedge 0 0', + '⋀ igwedge 0 0']) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'β⊞') + ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Page_Down, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, + ['β eta 0 1', # user freq for β increased to 1 + '⊞ oxplus 0 1', # user freq for ⊞ increased to 1 + '¯ ar 0 0', + '⊥ ot 0 0', + 'ℶ eth 0 0', + '⋂ igcap 0 0', + '⋃ igcup 0 0', + '⋁ igvee 0 0', + '⋈ owtie 0 0', + '⊡ oxdot 0 0', + '• ullet 0 0', + '∙ ullet 0 0', + '≏ umpeq 0 0', + '∽ acksim 0 0', + '∵ ecause 0 0', + '≬ etween 0 0', + '⊼ arwedge 0 0', + '⋀ igwedge 0 0']) + self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 9) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) + self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 15) + ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) + self.assertEqual(ENGINE.mock_committed_text, 'β⊞≬') + +if __name__ == '__main__': + unittest.main()