From 72afc6d52a0887dc9ccbf1b8e263cf0f71cc87d5 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 16 Jul 2024 05:20:37 +1000 Subject: [PATCH] test(developer): Add tests for automatic versioning of notany() with context() Adds a unit test to verify that automatic versioning of notany() in conjunction with context() is correctly managed. Removes compiler error that could never be generated (replace with safety-check exception). Fixes: #11959 --- developer/src/kmc-kmn/build.sh | 2 +- .../src/compiler/kmn-compiler-messages.ts | 10 +++---- .../src/kmw-compiler/javascript-strings.ts | 5 ++-- .../src/kmw-compiler/kmw-compiler-messages.ts | 4 --- .../test/fixtures/kmw/version_notany.kmn | 12 ++++++++ .../test/fixtures/kmw/version_notany_10.kmn | 13 +++++++++ .../src/kmc-kmn/test/kmw/test-kmw-compiler.ts | 28 +++++++++++++++++++ developer/src/kmcmplib/src/CompMsg.cpp | 10 +++---- 8 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 developer/src/kmc-kmn/test/fixtures/kmw/version_notany.kmn create mode 100644 developer/src/kmc-kmn/test/fixtures/kmw/version_notany_10.kmn diff --git a/developer/src/kmc-kmn/build.sh b/developer/src/kmc-kmn/build.sh index be6a2fcff5..3fabac6f84 100755 --- a/developer/src/kmc-kmn/build.sh +++ b/developer/src/kmc-kmn/build.sh @@ -69,7 +69,7 @@ if builder_start_action test; then copy_deps tsc --build test/ npm run lint - readonly C8_THRESHOLD=74 + readonly C8_THRESHOLD=80 c8 --reporter=lcov --reporter=text --lines $C8_THRESHOLD --statements $C8_THRESHOLD --branches $C8_THRESHOLD --functions $C8_THRESHOLD mocha builder_echo warning "Coverage thresholds are currently $C8_THRESHOLD%, which is lower than ideal." builder_echo warning "Please increase threshold in build.sh as test coverage improves." diff --git a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts index ec7c81a7a8..4c66b61927 100644 --- a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts +++ b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts @@ -484,10 +484,10 @@ export class KmnCompilerMessages { `Invalid key identifier "${def(o.keyId)}"`); static ERROR_90FeatureOnlyLayoutFile = SevError | 0x05C; - static Error_90FeatureOnlyLayoutFile = () => m(this.ERROR_90FeatureOnlyLayoutFile, `Touch layout file reference requires store(version) '9.0'or higher`); + static Error_90FeatureOnlyLayoutFile = () => m(this.ERROR_90FeatureOnlyLayoutFile, `Touch layout file reference requires store(version) '9.0' or higher`); static ERROR_90FeatureOnlyKeyboardVersion = SevError | 0x05D; - static Error_90FeatureOnlyKeyboardVersion = () => m(this.ERROR_90FeatureOnlyKeyboardVersion, `KeyboardVersion system store requires store(version) '9.0'or higher`); + static Error_90FeatureOnlyKeyboardVersion = () => m(this.ERROR_90FeatureOnlyKeyboardVersion, `KeyboardVersion system store requires store(version) '9.0' or higher`); static ERROR_KeyboardVersionFormatInvalid = SevError | 0x05E; static Error_KeyboardVersionFormatInvalid = () => m(this.ERROR_KeyboardVersionFormatInvalid, `KeyboardVersion format is invalid, expecting dot-separated integers`); @@ -496,16 +496,16 @@ export class KmnCompilerMessages { static Error_ContextExHasInvalidOffset = () => m(this.ERROR_ContextExHasInvalidOffset, `context() statement has offset out of range`); static ERROR_90FeatureOnlyEmbedCSS = SevError | 0x060; - static Error_90FeatureOnlyEmbedCSS = () => m(this.ERROR_90FeatureOnlyEmbedCSS, `Embedding CSS requires store(version) '9.0'or higher`); + static Error_90FeatureOnlyEmbedCSS = () => m(this.ERROR_90FeatureOnlyEmbedCSS, `Embedding CSS requires store(version) '9.0' or higher`); static ERROR_90FeatureOnlyTargets = SevError | 0x061; - static Error_90FeatureOnlyTargets = () => m(this.ERROR_90FeatureOnlyTargets, `TARGETS system store requires store(version) '9.0'or higher`); + static Error_90FeatureOnlyTargets = () => m(this.ERROR_90FeatureOnlyTargets, `TARGETS system store requires store(version) '9.0' or higher`); static ERROR_ContextAndIndexInvalidInMatchNomatch = SevError | 0x062; static Error_ContextAndIndexInvalidInMatchNomatch = () => m(this.ERROR_ContextAndIndexInvalidInMatchNomatch, `context and index statements cannot be used in a match or nomatch statement`); static ERROR_140FeatureOnlyContextAndNotAnyWeb = SevError | 0x063; - static Error_140FeatureOnlyContextAndNotAnyWeb = () => m(this.ERROR_140FeatureOnlyContextAndNotAnyWeb, `For web and touch platforms, context() statement referring to notany() requires store(version) '14.0'or higher`); + static Error_140FeatureOnlyContextAndNotAnyWeb = () => m(this.ERROR_140FeatureOnlyContextAndNotAnyWeb, `For web and touch platforms, context() statement referring to notany() requires store(version) '14.0' or higher`); static ERROR_ExpansionMustFollowCharacterOrVKey = SevError | 0x064; diff --git a/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts b/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts index 0d2fb9c125..539e67ea92 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/javascript-strings.ts @@ -677,10 +677,9 @@ export function JavaScript_OutputString(fk: KMX.KEYBOARD, FTabStops: string, fkp break; case KMX.KMXFile.CODE_NOTANY: // #917: Minimum version required is 14.0: the KCXO function was only added for 14.0 - // Note that this is checked in compiler.cpp as well, so this error can probably never occur - // TODO: automatic version upgrade if(!isKeyboardVersion14OrLater()) { - callbacks.reportMessage(KmwCompilerMessages.Error_NotAnyRequiresVersion14({line:fkp.Line})); + // Note that this is checked in compiler.cpp as well, so this error can probably never occur + throw new Error('Unexpected: notany() encountered with invalid version'); } Result += nlt + `k.KCXO(${len},t,${AdjustIndex(fkp.dpContext, xstrlen(fkp.dpContext))},${AdjustIndex(fkp.dpContext, ContextIndex)+1});`; break; diff --git a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts index f2a88fed7f..8ed5893b44 100644 --- a/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts +++ b/developer/src/kmc-kmn/src/kmw-compiler/kmw-compiler-messages.ts @@ -30,10 +30,6 @@ export class KmwCompilerMessages extends KmnCompilerMessages { // Following messages are kmw-compiler only, so use KmwCompiler error namespace - static ERROR_NotAnyRequiresVersion14 = SevError | 0x0001; - static Error_NotAnyRequiresVersion14 = (o:{line: number}) => m(this.ERROR_NotAnyRequiresVersion14, - `Statement notany in context() match requires version 14.0+ of KeymanWeb`, o); - static ERROR_TouchLayoutIdentifierRequires15 = SevError | 0x0002; static Error_TouchLayoutIdentifierRequires15 = (o:{keyId:string, platformName:string, layerId:string}) => m(this.ERROR_TouchLayoutIdentifierRequires15, `Key "${def(o.keyId)}" on "${def(o.platformName)}", layer "${def(o.layerId)}" has a multi-part identifier which requires version 15.0 or newer.`); diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/version_notany.kmn b/developer/src/kmc-kmn/test/fixtures/kmw/version_notany.kmn new file mode 100644 index 0000000000..596cbef94b --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/version_notany.kmn @@ -0,0 +1,12 @@ +store(&NAME) 'version_notany' +store(&TARGETS) 'web' + +begin Unicode > use(main) + +group(main) using keys + +store(abc) 'abc' + +c Use of notany with corresponding context() should make compiler select version 14 + +notany(abc) + 'a' > context(1) \ No newline at end of file diff --git a/developer/src/kmc-kmn/test/fixtures/kmw/version_notany_10.kmn b/developer/src/kmc-kmn/test/fixtures/kmw/version_notany_10.kmn new file mode 100644 index 0000000000..bbec1509dc --- /dev/null +++ b/developer/src/kmc-kmn/test/fixtures/kmw/version_notany_10.kmn @@ -0,0 +1,13 @@ +store(&NAME) 'version_notany' +store(&TARGETS) 'web' +store(&VERSION) '10.0' + +begin Unicode > use(main) + +group(main) using keys + +store(abc) 'abc' + +c Use of notany with corresponding context() should make compiler generate ERROR_NotAnyRequiresVersion14 + +notany(abc) + 'a' > context(1) \ No newline at end of file diff --git a/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts index 5694fc6b51..3cd9cdb519 100644 --- a/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts +++ b/developer/src/kmc-kmn/test/kmw/test-kmw-compiler.ts @@ -135,6 +135,34 @@ describe('KeymanWeb Compiler', function() { assert.match(data, /KMINVER="10.0"/, `Could not find expected 'KMINVER="10.0"'`); }); }); + + it('should determine the minimum version correctly with `notany`', async function() { + // Note that the logic being tested here is in kmx compiler.cpp, not kmw compiler + const filenames = generateTestFilenames('version_notany'); + + let result = await kmnCompiler.run(filenames.source, null); + assert.isNotNull(result); + assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion)); + // The min version message from the .kmn compiler is generic 208A INFO_Info; + // we expect only 1 of the info messages -- for the .kmx target (not 2) + assert.equal(callbacks.messages.filter(item => item.code == KmnCompilerMessages.INFO_Info).length, 1); + + const data = new TextDecoder('utf-8').decode(result.artifacts.js.data); + assert.match(data, /KMINVER="14.0"/, `Could not find expected 'KMINVER="14.0"'`); + }); + + it('should give an error if the minimum version specified in the keyboard does not support `notany`', async function() { + // Note that the logic being tested here is in kmx compiler.cpp, not kmw compiler + const filenames = generateTestFilenames('version_notany_10'); + + let result = await kmnCompiler.run(filenames.source, null); + assert.isNull(result); + // The min version message from the .kmn compiler is generic 208A INFO_Info + assert.isFalse(callbacks.hasMessage(KmnCompilerMessages.INFO_Info)); + assert.isFalse(callbacks.hasMessage(KmwCompilerMessages.INFO_MinimumEngineVersion)); + assert.isTrue(callbacks.hasMessage(KmwCompilerMessages.ERROR_140FeatureOnlyContextAndNotAnyWeb)); + }); + }); async function run_test_keyboard(kmnCompiler: KmnCompiler, id: string): diff --git a/developer/src/kmcmplib/src/CompMsg.cpp b/developer/src/kmcmplib/src/CompMsg.cpp index 9b1bce1e0a..3313da9104 100644 --- a/developer/src/kmcmplib/src/CompMsg.cpp +++ b/developer/src/kmcmplib/src/CompMsg.cpp @@ -82,14 +82,14 @@ std::map CompilerErrorMap = { { CERR_InvalidLanguageLine , "Invalid 'language' command"}, { CERR_LayoutButNoLanguage , "Layout command found but no language command"}, { CERR_CannotCreateTempfile , "Cannot create temp file"}, - { CERR_90FeatureOnlyLayoutFile , "Touch layout file reference requires store(version) '9.0'or higher"}, - { CERR_90FeatureOnlyKeyboardVersion , "KeyboardVersion system store requires store(version) '9.0'or higher"}, + { CERR_90FeatureOnlyLayoutFile , "Touch layout file reference requires store(version) '9.0' or higher"}, + { CERR_90FeatureOnlyKeyboardVersion , "KeyboardVersion system store requires store(version) '9.0' or higher"}, { CERR_KeyboardVersionFormatInvalid , "KeyboardVersion format is invalid, expecting dot-separated integers"}, { CERR_ContextExHasInvalidOffset , "context() statement has offset out of range"}, - { CERR_90FeatureOnlyEmbedCSS , "Embedding CSS requires store(version) '9.0'or higher"}, - { CERR_90FeatureOnlyTargets , "TARGETS system store requires store(version) '9.0'or higher"}, + { CERR_90FeatureOnlyEmbedCSS , "Embedding CSS requires store(version) '9.0' or higher"}, + { CERR_90FeatureOnlyTargets , "TARGETS system store requires store(version) '9.0' or higher"}, { CERR_ContextAndIndexInvalidInMatchNomatch , "context and index statements cannot be used in a match or nomatch statement"}, - { CERR_140FeatureOnlyContextAndNotAnyWeb , "For web and touch platforms, context() statement referring to notany() requires store(version) '14.0'or higher"}, + { CERR_140FeatureOnlyContextAndNotAnyWeb , "For web and touch platforms, context() statement referring to notany() requires store(version) '14.0' or higher"}, { CERR_ExpansionMustFollowCharacterOrVKey , "An expansion must follow a character or a virtual key"}, { CERR_VKeyExpansionMustBeFollowedByVKey , "A virtual key expansion must be terminated by a virtual key"}, { CERR_CharacterExpansionMustBeFollowedByCharacter , "A character expansion must be terminated by a character key"},