diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac.xcodeproj/project.pbxproj b/mac/KeymanEngine4Mac/KeymanEngine4Mac.xcodeproj/project.pbxproj index 22ae755b62..2aa2eef6c4 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac.xcodeproj/project.pbxproj +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 984C2B161A79CA4F0023F89D /* KVKFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 984C2B021A79CA4F0023F89D /* KVKFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 984C2B171A79CA4F0023F89D /* KVKFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 984C2B031A79CA4F0023F89D /* KVKFile.m */; }; 984C2B191A79CB020023F89D /* WindowsVKCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 984C2B181A79CB020023F89D /* WindowsVKCodes.h */; }; + E224323F20D37E97006940A1 /* PlatformTest.kmx in Resources */ = {isa = PBXBuildFile; fileRef = E224323E20D37E97006940A1 /* PlatformTest.kmx */; }; E22A5A1120113DC100233965 /* KMEngineTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E22A5A1020113DC100233965 /* KMEngineTests.m */; }; E22A5A142011402E00233965 /* KeymanEngineTestsStaticHelperMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = E22A5A132011402E00233965 /* KeymanEngineTestsStaticHelperMethods.m */; }; E2537D9A201641DA0069FEA6 /* TestMacEngine.kmx in Resources */ = {isa = PBXBuildFile; fileRef = E2537D99201641DA0069FEA6 /* TestMacEngine.kmx */; }; @@ -102,6 +103,7 @@ 984C2B021A79CA4F0023F89D /* KVKFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KVKFile.h; sourceTree = ""; }; 984C2B031A79CA4F0023F89D /* KVKFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KVKFile.m; sourceTree = ""; }; 984C2B181A79CB020023F89D /* WindowsVKCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowsVKCodes.h; sourceTree = ""; }; + E224323E20D37E97006940A1 /* PlatformTest.kmx */ = {isa = PBXFileReference; lastKnownFileType = file; path = PlatformTest.kmx; sourceTree = ""; }; E22A5A1020113DC100233965 /* KMEngineTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMEngineTests.m; sourceTree = ""; }; E22A5A1220113F8100233965 /* KeymanEngineTestsStaticHelperMethods.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeymanEngineTestsStaticHelperMethods.h; sourceTree = ""; }; E22A5A132011402E00233965 /* KeymanEngineTestsStaticHelperMethods.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KeymanEngineTestsStaticHelperMethods.m; sourceTree = ""; }; @@ -197,6 +199,7 @@ 984C2AE21A79C98D0023F89D /* Supporting Files */ = { isa = PBXGroup; children = ( + E224323E20D37E97006940A1 /* PlatformTest.kmx */, E2537D99201641DA0069FEA6 /* TestMacEngine.kmx */, 984C2AE31A79C98D0023F89D /* Info.plist */, ); @@ -361,6 +364,7 @@ buildActionMask = 2147483647; files = ( E2537D9A201641DA0069FEA6 /* TestMacEngine.kmx in Resources */, + E224323F20D37E97006940A1 /* PlatformTest.kmx in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMEngine.m b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMEngine.m index 62e1d7638e..38aba15da1 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMEngine.m +++ b/mac/KeymanEngine4Mac/KeymanEngine4Mac/KME/KMEngine.m @@ -27,12 +27,21 @@ NSString *const Q_RETURN = @"Q_RETURN"; DWORD VKMap[0x80]; @interface KMEngine () ++ (NSRegularExpression *)regexPlatform; @property (strong, nonatomic) NSMutableString *tmpCtxBuf; @property (strong, nonatomic) NSMutableArray *indexStack; @end @implementation KMEngine ++ (NSRegularExpression *)regexPlatform { + static NSRegularExpression *regex = nil; + if (regex == nil) { + regex = [NSRegularExpression regularExpressionWithPattern:@"(\\b(((mac(os)?)x?)|(native)|(hardware)|(desktop)) *)*" options:NSRegularExpressionCaseInsensitive error:nil]; + } + return regex; +} + NSMutableString* _easterEggForCrashlytics = nil; const NSString* kEasterEggText = @"Cr@shlyt!cs crash#KME"; const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx"; @@ -788,14 +797,19 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx"; DWORD x2 = [keyCtx characterAtIndex:i+3]-1; DWORD x3 = [keyCtx characterAtIndex:i+4]-1; KMCompStore *store = [self.kmx.store objectAtIndex:x3]; - if (x1 == TSS_PLATFORM && x2 == EQUAL) { - if ([self checkPlatform:store.string]) + if (x1 == TSS_PLATFORM) { + BOOL platformMatches = [self checkPlatform:store.string]; + if ((platformMatches && (x2 == EQUAL)) || + (!platformMatches && (x2 == NOT_EQUAL))) { checkPlatform = YES; - else + } + else { return 0; + } } - else + else { return 0; // CODE_IFSYSTEMSTORE is not supported except TSS_PLATFORM + } i+=5; break; @@ -867,37 +881,9 @@ const NSString* kEasterEggKmxName = @"EnglishSpanish.kmx"; } - (BOOL)checkPlatform:(NSString *)platform { - NSArray *values = [platform componentsSeparatedByString:@" "]; - for (NSString *value in values) { - if ([value isEqualToString:@"touch"]) - return NO; - else if ([value isEqualToString:@"windows"]) - return NO; - else if ([value isEqualToString:@"android"]) - return NO; - else if ([value isEqualToString:@"ios"]) - return NO; - else if ([value isEqualToString:@"linux"]) - return NO; - else if ([value isEqualToString:@"tablet"]) - return NO; - else if ([value isEqualToString:@"phone"]) - return NO; - else if ([value isEqualToString:@"web"]) - return NO; - else if ([value isEqualToString:@"ie"]) - return NO; - else if ([value isEqualToString:@"chrome"]) - return NO; - else if ([value isEqualToString:@"firefox"]) - return NO; - else if ([value isEqualToString:@"safari"]) - return NO; - else if ([value isEqualToString:@"opera"]) - return NO; - } - - return YES; + NSRange wholeString = NSMakeRange(0, [platform length]); + NSRange firstMatchRange = [[KMEngine regexPlatform] rangeOfFirstMatchInString:platform options:NSMatchingAnchored range:wholeString]; + return (firstMatchRange.length == wholeString.length); } // Creates a VK map to convert Mac VK codes to Windows VK codes diff --git a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KMEngineTests.m b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KMEngineTests.m index 6678cad933..290aaf3741 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KMEngineTests.m +++ b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KMEngineTests.m @@ -269,4 +269,99 @@ NSString * names[nCombinations]; } } } + +// The following checkPlatform tests all use the PlatformTest KMX file based on PlatformTest.kmn +// (not included in the project itself, but available in the source repo for reference. This +// keyboard has been craefully crafted to check for all the "wrong" values first, for which checkPlatform +// should return NO, so while these tests are ostensibly testing for YES for the platform components that +// should match on a Mac, they are also testing for NO for all the other non-matching values. + +- (NSString *)checkPlatform_getOutputForKeystroke: (NSString*) character modifierFlags: (NSEventModifierFlags) flag keyCode:(unsigned short)code { + KMXFile *kmxFile = [KeymanEngineTestsStaticHelperMethods getKmxFileForPlatformTest]; + KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""]; + NSString *lcChar = [character lowercaseString]; + NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:flag timestamp:0 windowNumber:0 context:nil characters:character charactersIgnoringModifiers:lcChar isARepeat:NO keyCode:code]; + NSArray *actions = [engine processEvent:event]; + XCTAssert(actions.count == 1, @"Expected 1 action"); + NSDictionary *action = actions[0]; + NSString *actionType = [[action allKeys] objectAtIndex:0]; + XCTAssert([actionType isEqualToString:Q_STR], @"Expected Q_STR action"); + NSString *output = [action objectForKey:actionType]; + return output; +} + +- (void)testCheckPlatform_native_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"u" modifierFlags:0 keyCode:kVK_ANSI_U]; + XCTAssert([output isEqualToString:@" Native"], @"Expected checkPlatform to return YES for native."); +} + +- (void)testCheckPlatform_NATIVE_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"U" modifierFlags:NSEventModifierFlagShift keyCode:kVK_ANSI_U]; + XCTAssert([output isEqualToString:@" Native"], @"Expected checkPlatform to return YES for NATIVE."); +} + +- (void)testCheckPlatform_hardware_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"p" modifierFlags:0 keyCode:kVK_ANSI_P]; + XCTAssert([output isEqualToString:@" Hardware"], @"Expected checkPlatform to return YES for hardware."); +} + +- (void)testCheckPlatform_HARDWARE_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"P" modifierFlags:NSEventModifierFlagShift keyCode:kVK_ANSI_P]; + XCTAssert([output isEqualToString:@" Hardware"], @"Expected checkPlatform to return YES for HARDWARE."); +} + +- (void)testCheckPlatform_desktop_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"I" modifierFlags:0 keyCode:kVK_ANSI_I]; + XCTAssert([output isEqualToString:@" Desktop"], @"Expected checkPlatform to return YES for desktop."); +} + +- (void)testCheckPlatform_Desktop_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"I" modifierFlags:NSEventModifierFlagShift keyCode:kVK_ANSI_I]; + XCTAssert([output isEqualToString:@" Desktop"], @"Expected checkPlatform to return YES for Desktop."); +} + +- (void)testCheckPlatform_macosx_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"o" modifierFlags:0 keyCode:kVK_ANSI_O]; + XCTAssert([output isEqualToString:@" macOS"], @"Expected checkPlatform to return YES for macosx."); +} + +- (void)testCheckPlatform_MacOS_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"K" modifierFlags:NSEventModifierFlagShift keyCode:kVK_ANSI_K]; + XCTAssert([output isEqualToString:@" macOS"], @"Expected checkPlatform to return YES for MacOS."); +} + +- (void)testCheckPlatform_MAC_ReturnsYes { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"L" modifierFlags:NSEventModifierFlagShift keyCode:kVK_ANSI_L]; + XCTAssert([output isEqualToString:@" mac"], @"Expected checkPlatform to return YES for MAC."); +} + +- (void)testCheckPlatform_Browsers_ReturnsNo { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"y" modifierFlags:0 keyCode:kVK_ANSI_Y]; + XCTAssert([output isEqualToString:@" [Browser Undefined]"], @"Expected checkPlatform to return NO for all browsers (ie, chrome, firefox, safari, opera)."); +} + +- (void)testCheckPlatform_multipleTokens_MatchesCorrectOneForMac { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"m" modifierFlags:0 keyCode:kVK_ANSI_M]; + XCTAssert([output isEqualToString:@" macOS native desktop hardware"], @"Expected checkPlatform to return YES for macOS native desktop hardware."); +} + +- (void)testContextMatch_InvertedPlatformLogic_NotTouch { + NSString *output = [self checkPlatform_getOutputForKeystroke:@"x" modifierFlags:0 keyCode:kVK_ANSI_X]; + XCTAssert([output isEqualToString:@" !Touch"], @"Expected !touch to be true."); +} + +// This allows for easy debugging of the "manual" platformtest keyboard developed for filling in the test spreadsheet. +//- (void)testCheckPlatform_temp_UnknownPlatformX { +// KMXFile *kmxFile = [[KMXFile alloc] initWithFilePath:@"/Users/tom/Documents/keymanapp/keyman/windows/src/test/manual-tests/platform-rules/platformtest.kmx"]; +// +// KMEngine *engine = [[KMEngine alloc] initWithKMX:kmxFile contextBuffer:@""]; +// NSEvent *event = [NSEvent keyEventWithType:NSEventTypeKeyDown location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:kVK_ANSI_A]; +// NSArray *actions = [engine processEvent:event]; +// XCTAssert(actions.count == 1, @"Expected 1 action"); +// NSDictionary *action = actions[0]; +// NSString *actionType = [[action allKeys] objectAtIndex:0]; +// XCTAssert([actionType isEqualToString:Q_STR], @"Expected Q_STR action"); +// NSString *output = [action objectForKey:actionType]; +// XCTAssert([output isEqualToString:@"hardware macosx desktop native undefined !touch undefined macosx"], @"Expected !touch."); +//} @end diff --git a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.h b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.h index 3e4c6fe7f6..a58f1f5953 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.h +++ b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.h @@ -21,6 +21,7 @@ @interface KeymanEngineTestsStaticHelperMethods : NSObject + (KMXFile *)getKmxFileTestMacEngine; ++ (KMXFile *)getKmxFileForPlatformTest; @end #endif /* KeymanEngineTestsStaticHelperMethods_h */ diff --git a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.m b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.m index 6806fb63f5..f2b32bc2b2 100644 --- a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.m +++ b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/KeymanEngineTestsStaticHelperMethods.m @@ -16,4 +16,10 @@ KMXFile *kmxFile = [[KMXFile alloc] initWithFilePath:path]; return kmxFile; } + ++ (KMXFile *)getKmxFileForPlatformTest { + NSString *path = [[NSBundle bundleForClass:[KeymanEngineTestsStaticHelperMethods class]] pathForResource:@"PlatformTest.kmx" ofType:nil]; + KMXFile *kmxFile = [[KMXFile alloc] initWithFilePath:path]; + return kmxFile; +} @end diff --git a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmn b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmn new file mode 100755 index 0000000000..8ffe47fba4 --- /dev/null +++ b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmn @@ -0,0 +1,84 @@ +store(&TARGETS) 'any' +store(&NAME) 'PlatformTest' +begin Unicode > use(main) + +group(main) using keys + +platform('touch') + [K_P] > ' Touch' +platform('hardware') + [K_P] > ' Hardware' ++ [K_P] > ' [UI Undefined]' + +platform('TOUCH') + [SHIFT K_P] > ' Touch' +platform('HARDWARE') + [SHIFT K_P] > ' Hardware' ++ [SHIFT K_P] > ' [UI Undefined]' + +platform('windows') + [K_O] > ' Windows' +platform('android') + [K_O] > ' Android' +platform('iOS') + [K_O] > ' iOS' +platform('linux') + [K_O] > ' Linux' +platform('macosx') + [K_O] > ' macOS' ++ [K_O] > ' [OS Undefined]' + +platform('WINDOWS') + [SHIFT K_O] > ' Windows' +platform('ANDROID') + [SHIFT K_O] > ' Android' +platform('IOS') + [SHIFT K_O] > ' iOS' +platform('LINUX') + [SHIFT K_O] > ' Linux' +platform('MacOSX') + [SHIFT K_O] > ' macOS' ++ [SHIFT K_O] > ' [OS Undefined]' + +platform('mac') + [K_L] > ' mac' ++ [K_L] > ' [Not a mac]' +platform('MAC') + [SHIFT K_L] > ' mac' ++ [SHIFT K_L] > ' [Not a mac]' +platform('macos') + [K_K] > ' macOS' ++ [K_K] > ' [Not macos]' +platform('MacOS') + [SHIFT K_K] > ' macOS' ++ [SHIFT K_K] > ' [Not macos]' + +platform('tablet') + [K_I] > ' Tablet' +platform('phone') + [K_I] > ' Phone' +platform('desktop') + [K_I] > ' Desktop' ++ [K_I] > ' [FF Undefined]' + +platform('TABLET') + [SHIFT K_I] > ' Tablet' +platform('Phone') + [SHIFT K_I] > ' Phone' +platform('DeskTop') + [SHIFT K_I] > ' Desktop' ++ [SHIFT K_I] > ' [FF Undefined]' + +platform('web') + [K_U] > ' Web' +platform('native') + [K_U] > ' Native' ++ [K_U] > ' [Nativeness Undefined]' + +platform('Web') + [SHIFT K_U] > ' Web' +platform('NATIVE') + [SHIFT K_U] > ' Native' ++ [SHIFT K_U] > ' [Nativeness Undefined]' + +platform('ie') + [K_Y] > ' IE' +platform('chrome') + [K_Y] > ' Chrome' +platform('firefox') + [K_Y] > ' Firefox' +platform('safari') + [K_Y] > ' Safari' +platform('opera') + [K_Y] > ' Opera' ++ [K_Y] > ' [Browser Undefined]' + +platform('IE') + [SHIFT K_Y] > ' IE' +platform('Chrome') + [SHIFT K_Y] > ' Chrome' +platform('FireFox') + [SHIFT K_Y] > ' Firefox' +platform('Safari') + [SHIFT K_Y] > ' Safari' +platform('OPERA') + [SHIFT K_Y] > ' Opera' ++ [SHIFT K_Y] > ' [Browser Undefined]' + +platform('tablet') + [K_T] > ' Mobile' +platform('phone') + [K_T] > ' Mobile' ++ [K_T] > ' [Mobile Undefined]' + +if(&platform != 'hardware') + [K_X] > ' !Hardware' +if(&platform != 'touch') + [K_X] > ' !Touch' ++ [K_X] > ' [Inverted rule Failed]' + +platform('tablet ie web') + [K_M] > ' tablet ie web' +platform('touch phone safari web') + [K_M] > ' phone safari web' +platform('Windows desktop native hardware') + [K_M] > ' Windows desktop native hardware' +platform('Linux desktop native') + [K_M] > ' Linux desktop native' +platform('macOS native desktop hardware Safari') + [K_M] > ' macOS native hardware Safari' +platform('macOS native desktop hardware') + [K_M] > ' macOS native desktop hardware' ++ [K_M] > ' [No match]' diff --git a/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmx b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmx new file mode 100644 index 0000000000..909154f325 Binary files /dev/null and b/mac/KeymanEngine4Mac/KeymanEngine4MacTests/PlatformTest.kmx differ diff --git a/mac/history.md b/mac/history.md index 0fd0afba06..5e9e7b9e6a 100644 --- a/mac/history.md +++ b/mac/history.md @@ -1,5 +1,9 @@ # Keyman for macOS Version History +## 2018-06-18 10.0.48 beta +* Made other version(s) of LibreOffice use "legacy" mode for character replacements (#990) +* Corrected the way Keyman responds evaluates tokens in **platform** statement (#971) + ## 2018-06-07 10.0.47 beta * Change to build script to use correct build location (#950) diff --git a/windows/src/test/manual-tests/platform-rules/platform-results.xlsx b/windows/src/test/manual-tests/platform-rules/platform-results.xlsx index f3e0eef1ab..6eb0927ce9 100644 Binary files a/windows/src/test/manual-tests/platform-rules/platform-results.xlsx and b/windows/src/test/manual-tests/platform-rules/platform-results.xlsx differ