Skip to content

Comprehensive Unit Testing and small Core Logic Refactoring#120

Open
pzauner wants to merge 6 commits intopalsoftware:mainfrom
pzauner:feature/add-unit-tests-casing-helper
Open

Comprehensive Unit Testing and small Core Logic Refactoring#120
pzauner wants to merge 6 commits intopalsoftware:mainfrom
pzauner:feature/add-unit-tests-casing-helper

Conversation

@pzauner
Copy link
Collaborator

@pzauner pzauner commented Jan 25, 2026

This PR introduces a robust testing framework and significantly improves the reliability of the keyboard's core logic, focusing on modifier states, input context detection, and the suggestion engine.

Test Infrastructure

  • Robolectric Integration: Added Robolectric to gradle/libs.versions.toml and app/build.gradle.kts. This allows running Unit Tests that depend on Android framework classes (like KeyEvent, EditorInfo, and InputType) directly on the JVM.

New Unit Tests

Implemented a comprehensive suite of tests to prevent regressions in core features:

  • ModifierStateControllerTest: Validates the Shift/Caps-Lock state machine, including double-tap detection, one-shot shift consumption, and state persistence.
  • InputContextStateTest: Ensures correct detection of field types (Password, Email, URI) and respects Android IME flags (CAP_SENTENCES, CAP_WORDS, CAP_CHARACTERS).
  • SuggestionEngineTest and SymSpellTest: Verifies the accuracy of word corrections and the suggestion generation logic.
  • DictionaryRepositoryTest: Tests dictionary loading and lookup performance, supported by a new FakeDictionaryRepository for isolated testing.
  • CurrentWordTrackerTest: Validates how the IME tracks the current word during typing and cursor movements.
  • CasingHelperTest and PunctuationTest: Ensures correct behavior for auto-capitalization and punctuation handling across different languages.

Core Logic Improvements

This is the only place where the actual structure of the codebase was modified. This was performed for two reasons:

a) To enable testing in the first place. Testing private methods via reflection is problematic in Kotlin when the class performs strict null-checks on its constructor dependencies (such as DictionaryRepository). Moving the pure logic into a companion object allows for clean unit tests without requiring complex mocks.

b) The splitApostropheWord method was hard-capped to prefixes of at most 3 characters (e.g., l', un', all'). This caused longer Italian elisions like dell' or nell' to be bypassed by the specialized correction logic."

  • AutoReplaceController: Refactored the auto-replace logic to better handle elisions (e.g., Italian "l'amico") and improved the integration with the dictionary repository.
  • DictionaryRepository: Optimized asynchronous loading and memory management for dictionaries.
  • InputContextState: Enhanced the mapping of EditorInfo to internal state flags, providing more granular control over features like auto-capitalization and suggestions based on the active input field.

Change Stats

  • Files changed: 16
  • Insertions: ~1160 lines (primarily test coverage)
  • Status: All unit tests are passing (./gradlew testDebugUnitTest).

@pzauner pzauner force-pushed the main branch 10 times, most recently from fe7c448 to 35c9b8e Compare March 6, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant