mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-21 13:27:53 +00:00
Merge pull request #11190 from keymanapp/fix/mac/11155-compliance-detection-on-replace
fix(mac): handle text replacement cases during compliance detection
This commit is contained in:
commit
fb3ab22ccd
6 changed files with 281 additions and 98 deletions
|
|
@ -27,6 +27,7 @@
|
|||
299B5E2129126CA20070841D /* keyman-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 299B5E1F29126CA20070841D /* keyman-icon.png */; };
|
||||
29B42A572728339900EDD5D3 /* KMInfoWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 29B42A592728339900EDD5D3 /* KMInfoWindowController.xib */; };
|
||||
29B42A602728343B00EDD5D3 /* KMKeyboardHelpWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 29B42A622728343B00EDD5D3 /* KMKeyboardHelpWindowController.xib */; };
|
||||
29B6FB732BC39DD60074BF7F /* TextApiComplianceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B6FB722BC39DD60074BF7F /* TextApiComplianceTests.m */; };
|
||||
37A245C12565DFA6000BBF92 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37A245C02565DFA6000BBF92 /* Assets.xcassets */; };
|
||||
37AE5C9D239A7B770086CC7C /* qrcode.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 37AE5C9C239A7B770086CC7C /* qrcode.min.js */; };
|
||||
37C2B0CB25FF2C350092E16A /* Help in Resources */ = {isa = PBXBuildFile; fileRef = 37C2B0CA25FF2C340092E16A /* Help */; };
|
||||
|
|
@ -227,6 +228,7 @@
|
|||
29B6BB5E292239B4008F04BD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/KMKeyboardHelpWindowController.strings"; sourceTree = "<group>"; };
|
||||
29B6BB5F292239B4008F04BD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = "<group>"; };
|
||||
29B6BB60292239DC008F04BD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
29B6FB722BC39DD60074BF7F /* TextApiComplianceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TextApiComplianceTests.m; sourceTree = "<group>"; };
|
||||
29B70A23283CB6680086B580 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/KMAboutWindowController.strings"; sourceTree = "<group>"; };
|
||||
29B70A24283CB6690086B580 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/preferences.strings"; sourceTree = "<group>"; };
|
||||
29B70A25283CB66A0086B580 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/KMInfoWindowController.strings"; sourceTree = "<group>"; };
|
||||
|
|
@ -682,6 +684,7 @@
|
|||
E211769E20E18C0B00F8065D /* AppleCompliantTestClient.h */,
|
||||
E211769F20E18C5200F8065D /* AppleCompliantTestClient.m */,
|
||||
2999C69829D3E4CE006366F5 /* InputMethodTests.m */,
|
||||
29B6FB722BC39DD60074BF7F /* TextApiComplianceTests.m */,
|
||||
);
|
||||
path = KeymanTests;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1002,6 +1005,7 @@
|
|||
2999C69929D3E4CE006366F5 /* InputMethodTests.m in Sources */,
|
||||
E211769D20E182DD00F8065D /* NoContextTestClient.m in Sources */,
|
||||
E2D5529D20DD8E0200D77CFB /* LegacyTestClient.m in Sources */,
|
||||
29B6FB732BC39DD60074BF7F /* TextApiComplianceTests.m in Sources */,
|
||||
E21176A020E18C5200F8065D /* AppleCompliantTestClient.m in Sources */,
|
||||
E2585A8120DD7CF100CBB994 /* TestAppDelegate.m in Sources */,
|
||||
E28914D62024E4010048E71E /* KMInputMethodEventHandler.m in Sources */,
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ NSString* const kEasterEggKmxName = @"EnglishSpanish.kmx";
|
|||
[self.appDelegate logDebugMessage:@"KMInputMethodHandler initWithClient checkTextApiCompliance: %@", _apiCompliance];
|
||||
} else if (self.apiCompliance.isComplianceUncertain) {
|
||||
// We have a valid TextApiCompliance object, but compliance is uncertain, so test it
|
||||
[self.apiCompliance testCompliance:client];
|
||||
[self.apiCompliance checkCompliance:client];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +454,7 @@ NSString* const kEasterEggKmxName = @"EnglishSpanish.kmx";
|
|||
|
||||
[client insertText:text replacementRange:replacementRange];
|
||||
if (self.apiCompliance.isComplianceUncertain) {
|
||||
[self.apiCompliance testComplianceAfterInsert:client];
|
||||
[self.apiCompliance checkComplianceAfterInsert:client delete:textToDelete insert:text];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (readonly) NSString *clientApplicationId;
|
||||
|
||||
-(instancetype)initWithClient:(id) client applicationId:(NSString *)appId;
|
||||
-(void)testCompliance:(id) client;
|
||||
-(void)testComplianceAfterInsert:(id) client;
|
||||
-(void)checkCompliance:(id) client;
|
||||
-(void) checkComplianceAfterInsert:(id) client delete:(NSString *)textToDelete insert:(NSString *)textToInsert;
|
||||
-(BOOL)isComplianceUncertain;
|
||||
-(BOOL)canReadText;
|
||||
-(BOOL)mustBackspaceUsingEvents;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ NSString *const kKMLegacyApps = @"KMLegacyApps";
|
|||
|
||||
// if we do not have hard-coded noncompliance, then test the app
|
||||
if (![self applyNoncompliantAppLists:appId]) {
|
||||
[self testCompliance:client];
|
||||
[self checkCompliance:client];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
|
@ -76,79 +76,118 @@ return [NSString stringWithFormat:@"complianceUncertain: %d, hasCompliantSelecti
|
|||
}
|
||||
|
||||
/** test to see if the API selectedRange functions properly for the text input client */
|
||||
-(void) testCompliance:(id) client {
|
||||
BOOL selectionApiVerified = NO;
|
||||
|
||||
-(void) checkCompliance:(id) client {
|
||||
// confirm that the API actually exists (this always seems to return true)
|
||||
selectionApiVerified = [client respondsToSelector:@selector(selectedRange)];
|
||||
self.hasCompliantSelectionApi = [client respondsToSelector:@selector(selectedRange)];
|
||||
|
||||
if (!selectionApiVerified) {
|
||||
if (!self.hasCompliantSelectionApi) {
|
||||
self.complianceUncertain = NO;
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
}
|
||||
else {
|
||||
// if API exists, call it and see if it works as expected
|
||||
NSRange selectionRange = [client selectedRange];
|
||||
self.initialSelection = selectionRange;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testCompliance, location = %lu, length = %lu", selectionRange.location, selectionRange.length];
|
||||
|
||||
if (selectionRange.location == NSNotFound) {
|
||||
// NSNotFound may just mean that we don't have the focus yet; say NO for
|
||||
// now, but this may toggle back to YES after the first insertText
|
||||
selectionApiVerified = NO;
|
||||
self.complianceUncertain = YES;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testCompliance not compliant but uncertain, range is NSNotFound"];
|
||||
} else if (selectionRange.location >= 0) {
|
||||
// location greater than or equal to zero may just mean that the client
|
||||
// returns an inaccurate value; say YES for now, but set back to NO if the
|
||||
// first insertText does not cause a change in the selection
|
||||
selectionApiVerified = YES;
|
||||
self.complianceUncertain = YES;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testCompliance compliant but uncertain, location >= 0"];
|
||||
}
|
||||
self.initialSelection = [client selectedRange];
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkCompliance, location = %lu, length = %lu", self.initialSelection.location, self.initialSelection.length];
|
||||
[self checkComplianceUsingInitialSelection];
|
||||
}
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testCompliance workingSelectionApi for app %@: set to %@", self.clientApplicationId, selectionApiVerified?@"yes":@"no"];
|
||||
|
||||
self.hasCompliantSelectionApi = selectionApiVerified;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkCompliance workingSelectionApi for app %@: set to %@", self.clientApplicationId, self.complianceUncertain?@"YES":@"NO"];
|
||||
}
|
||||
|
||||
/** if complianceUncertain is true, checking the selection after an insert can make it clear */
|
||||
-(void) testComplianceAfterInsert:(id) client {
|
||||
if(self.complianceUncertain) {
|
||||
NSRange selectionRange = [client selectedRange];
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testSelectionApiAfterInsert, location = %lu, length = %lu", selectionRange.location, selectionRange.length];
|
||||
|
||||
if (selectionRange.location == NSNotFound) {
|
||||
// NO for certain, insertText means we have focus, NSNotFound means that the selection API does not work
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert certain, non-compliant, range is NSNotFound"];
|
||||
} else if (selectionRange.location == 0) {
|
||||
// NO for certain, after an insertText we cannot be at location 0
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert certain, non-compliant, location = 0"];
|
||||
} else if (selectionRange.location > 0) {
|
||||
if (NSEqualRanges(selectionRange, self.initialSelection)) {
|
||||
// NO for certain, when the selection is unchanged after an insert
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert certain, non-compliant, selection the same: initial location = %@, new location = %@", NSStringFromRange(self.initialSelection), NSStringFromRange(selectionRange)];
|
||||
} else {
|
||||
// YES for certain, when the selection is changed after an insert
|
||||
self.hasCompliantSelectionApi = YES;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert certain, compliant, selection changed from initial location = %@ to new location = %@", NSStringFromRange(self.initialSelection), NSStringFromRange(selectionRange)];
|
||||
}
|
||||
}
|
||||
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert, self.hasWorkingSelectionApi = %@ for app %@", self.hasCompliantSelectionApi?@"yes":@"no", self.clientApplicationId];
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testComplianceAfterInsert TextApiCompliance: %@", self];
|
||||
} else {
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance testSelectionApiAfterInsert, compliance is already known"];
|
||||
-(void) checkComplianceUsingInitialSelection {
|
||||
if (self.initialSelection.location == NSNotFound) {
|
||||
/**
|
||||
* NSNotFound may just mean that we don't have the focus yet; say NO for now
|
||||
* now, but this may toggle back to YES after the first insertText
|
||||
*/
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = YES;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkComplianceUsingInitialSelection not compliant but uncertain, range is NSNotFound"];
|
||||
} else if (self.initialSelection.location >= 0) {
|
||||
/**
|
||||
* location greater than or equal to zero may just mean that the client
|
||||
* returns an inaccurate value; say YES for now, but set back to NO if the
|
||||
* if the selection is not consistent with the first insert
|
||||
*/
|
||||
self.hasCompliantSelectionApi = YES;
|
||||
self.complianceUncertain = YES;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkComplianceUsingInitialSelection compliant but uncertain, location >= 0"];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If complianceUncertain is true, checking the location after an insert can confirm whether the app is compliant.
|
||||
* Delete and insert are what core instructed us to do.
|
||||
* Text that was selected in the client when the key was processed is irrelevant as it does not affect the location.
|
||||
*/
|
||||
-(void) checkComplianceAfterInsert:(id) client delete:(NSString *)textToDelete insert:(NSString *)textToInsert {
|
||||
|
||||
// return if compliance is already certain
|
||||
if(!self.complianceUncertain) {
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkComplianceAfterInsert, compliance is already known"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSRange selectionRange = [client selectedRange];
|
||||
if ([self validateNewLocation:selectionRange.location delete:textToDelete]) {
|
||||
BOOL changeExpected = [self isLocationChangeExpectedOnInsert:textToDelete insert:textToInsert];
|
||||
BOOL locationChanged = [self hasLocationChanged:selectionRange];
|
||||
[self validateLocationChange:changeExpected hasLocationChanged:locationChanged];
|
||||
}
|
||||
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance checkComplianceAfterInsert, self.hasWorkingSelectionApi = %@ for app %@", self.hasCompliantSelectionApi?@"YES":@"NO", self.clientApplicationId];
|
||||
}
|
||||
|
||||
- (BOOL)validateNewLocation:(NSUInteger)location delete:(NSString *)textToDelete {
|
||||
BOOL validLocation = NO;
|
||||
|
||||
if (location == NSNotFound) {
|
||||
// invalid location: insertText means we have focus, NSNotFound means selection API not functioning
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance validateNewLocation = NO, location is NSNotFound"];
|
||||
} else if ((location == 0) && (textToDelete.length > 0)) {
|
||||
// invalid location: cannot have text to delete at location zero
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance validateNewLocation = NO, location is zero with textToDelete.length > 0"];
|
||||
} else {
|
||||
// location is valid, but do not know if it is compliant yet
|
||||
validLocation = YES;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance validateNewLocation = YES, newLocation = %d, oldLocation = %d", location, self.initialSelection.location];
|
||||
}
|
||||
return validLocation;
|
||||
}
|
||||
|
||||
- (void) validateLocationChange:(BOOL) changeExpected hasLocationChanged:(BOOL) locationChanged {
|
||||
|
||||
if (changeExpected == locationChanged) {
|
||||
// YES for certain, the location is where we expect it
|
||||
self.hasCompliantSelectionApi = YES;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance validateLocationChange compliant, locationChanged = %@, changeExpected = %@", locationChanged?@"YES":@"NO", changeExpected?@"YES":@"NO"];
|
||||
} else if (changeExpected != locationChanged) {
|
||||
// NO for certain, when the selection is unchanged after an insert
|
||||
self.hasCompliantSelectionApi = NO;
|
||||
self.complianceUncertain = NO;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance validateLocationChange non-compliant, locationChanged = %@, changeExpected = %@", locationChanged?@"YES":@"NO", changeExpected?@"YES":@"NO"];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isLocationChangeExpectedOnInsert:(NSString *)textToDelete insert:(NSString *)textToInsert {
|
||||
BOOL changeExpected = textToInsert.length != textToDelete.length;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance isLocationChangeExpected, changeExpected = %@", changeExpected?@"YES":@"NO"];
|
||||
|
||||
return changeExpected;
|
||||
}
|
||||
|
||||
- (BOOL)hasLocationChanged:(NSRange)newSelection {
|
||||
NSUInteger newLocation = newSelection.location;
|
||||
NSUInteger oldLocation = self.initialSelection.location;
|
||||
BOOL locationChanged = newLocation != oldLocation;
|
||||
[self.appDelegate logDebugMessage:@"TextApiCompliance hasLocationChanged, currentSelection = %lu, length = %lu", newSelection.location, newSelection.length];
|
||||
|
||||
return locationChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the hard-coded non-compliant app list and the user-managed (via user defaults) non-compliant app list.
|
||||
* If true, mark the app as non-compliant and certain (not complianceUncertain).
|
||||
|
|
@ -159,7 +198,7 @@ return [NSString stringWithFormat:@"complianceUncertain: %d, hasCompliantSelecti
|
|||
isAppNonCompliant = [self containedInUserManagedNoncompliantAppList:clientAppId];
|
||||
}
|
||||
|
||||
[self.appDelegate logDebugMessage:@"containedInNoncompliantAppLists: for app %@: %@", clientAppId, isAppNonCompliant?@"yes":@"no"];
|
||||
[self.appDelegate logDebugMessage:@"applyNoncompliantAppLists: for app %@: %@", clientAppId, isAppNonCompliant?@"YES":@"NO"];
|
||||
|
||||
if (isAppNonCompliant) {
|
||||
self.complianceUncertain = NO;
|
||||
|
|
|
|||
|
|
@ -13,20 +13,12 @@
|
|||
#import "KMInputMethodEventHandler.h"
|
||||
#import "AppleCompliantTestClient.h"
|
||||
#import "TextApiCompliance.h"
|
||||
#import "AppleCompliantTestClient.h"
|
||||
|
||||
KMInputMethodEventHandler *testEventHandler = nil;
|
||||
|
||||
@interface InputMethodTests : XCTestCase
|
||||
@end
|
||||
|
||||
// included following interface that we can see and test private methods of TextApiCompliance
|
||||
@interface TextApiCompliance (Testing)
|
||||
|
||||
- (BOOL)arrayContainsApplicationId:(NSString *)clientAppId fromArray:(NSArray *)legacyApps;
|
||||
|
||||
@end
|
||||
|
||||
@interface KMInputMethodEventHandler (Testing)
|
||||
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender;
|
||||
|
|
@ -46,29 +38,6 @@ KMInputMethodEventHandler *testEventHandler = nil;
|
|||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)testIsClientAppLegacy_unlistedClientAppId_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.microsoft.VSCode",@"com.adobe.Photoshop",nil];
|
||||
|
||||
BOOL isLegacy = [apiCompliance arrayContainsApplicationId:clientAppId fromArray:legacyAppsArray];
|
||||
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
|
||||
XCTAssertFalse(isLegacy, @"App not expected to be in legacy list");
|
||||
}
|
||||
|
||||
- (void)testIsClientAppLegacy_listedClientAppId_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.microsoft.VSCode";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.adobe.Photoshop",@"com.microsoft.VSCode",nil];
|
||||
|
||||
BOOL isLegacy = [apiCompliance arrayContainsApplicationId:clientAppId fromArray:legacyAppsArray];
|
||||
XCTAssertTrue(isLegacy, @"App expected to be in legacy list");
|
||||
}
|
||||
|
||||
- (void)testCalculateInsertRange_noDelete_returnsCurrentLocation {
|
||||
NSRange selectionRange = NSMakeRange(1, 0);
|
||||
NSRange insertRange = [testEventHandler calculateInsertRangeForDeletedText:@"" selectionRange:selectionRange];
|
||||
|
|
|
|||
171
mac/Keyman4MacIM/KeymanTests/TextApiComplianceTests.m
Normal file
171
mac/Keyman4MacIM/KeymanTests/TextApiComplianceTests.m
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/**
|
||||
* Keyman is copyright (C) SIL International. MIT License.
|
||||
*
|
||||
* TextApiComplianceTests.m
|
||||
* KeymanTests
|
||||
*
|
||||
* Created by Shawn Schantz on 2024-04-08.
|
||||
*
|
||||
* Unit tests of TextApiCompliance class
|
||||
*/
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "TextApiCompliance.h"
|
||||
#import "AppleCompliantTestClient.h"
|
||||
|
||||
@interface TextApiComplianceTests : XCTestCase
|
||||
@end
|
||||
|
||||
// included following interface that we can see and test private methods of TextApiCompliance
|
||||
@interface TextApiCompliance (Testing)
|
||||
@property NSRange initialSelection;
|
||||
- (BOOL)validateNewLocation:(NSUInteger)location delete:(NSString *)textToDelete;
|
||||
- (void) validateLocationChange:(BOOL) changeExpected hasLocationChanged:(BOOL) locationChanged;
|
||||
- (BOOL)isLocationChangeExpectedOnInsert:(NSString *)textToDelete insert:(NSString *)textToInsert;
|
||||
- (BOOL)hasLocationChanged:(NSRange)newSelection;
|
||||
- (BOOL)arrayContainsApplicationId:(NSString *)clientAppId fromArray:(NSArray *)legacyApps;
|
||||
@end
|
||||
|
||||
@implementation TextApiComplianceTests
|
||||
|
||||
- (void)setUp {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)testValidateNewLocation_locationNotFound_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isNewLocationValid = [apiCompliance validateNewLocation:NSNotFound delete:@"a"];
|
||||
XCTAssertFalse(isNewLocationValid, @"New location is not valid if equal to NSNotFound");
|
||||
}
|
||||
|
||||
- (void)testValidateNewLocation_deleteAtLocationZero_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isNewLocationValid = [apiCompliance validateNewLocation:0 delete:@"a"];
|
||||
XCTAssertFalse(isNewLocationValid, @"New location is not valid if attempting to delete at location of zero");
|
||||
}
|
||||
|
||||
- (void)testValidateNewLocation_deleteAtLocationOne_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isNewLocationValid = [apiCompliance validateNewLocation:1 delete:@"a"];
|
||||
XCTAssertTrue(isNewLocationValid, @"New location is valid if attempting to delete at location of one");
|
||||
}
|
||||
|
||||
- (void)testIsLocationChangeExpectedOnInsert_nilDeleteString_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isExpected = [apiCompliance isLocationChangeExpectedOnInsert:nil insert:@"a"];
|
||||
XCTAssertTrue(isExpected, @"Location change is expected for nil delete string and non-empty insert string");
|
||||
}
|
||||
|
||||
- (void)testIsLocationChangeExpectedOnInsert_emptyDeleteString_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isExpected = [apiCompliance isLocationChangeExpectedOnInsert:@"" insert:@"a"];
|
||||
XCTAssertTrue(isExpected, @"Location change is expected for empty delete string and non-empty insert string");
|
||||
}
|
||||
|
||||
- (void)testIsLocationChangeExpectedOnInsert_deleteAndInsertOneCharacter_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isExpected = [apiCompliance isLocationChangeExpectedOnInsert:@"a" insert:@"X"];
|
||||
XCTAssertFalse(isExpected, @"Location change not expected for delete string and insert string of same length");
|
||||
}
|
||||
|
||||
- (void)testIsLocationChangeExpectedOnInsert_deleteAndInsertSurrogatePair_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
BOOL isExpected = [apiCompliance isLocationChangeExpectedOnInsert:@"𑀓" insert:@"𑀢"];
|
||||
XCTAssertFalse(isExpected, @"Location change not expected for delete string and insert string of same length");
|
||||
}
|
||||
|
||||
- (void)testHasLocationChanged_zeroForBoth_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
NSRange zeroSelection = NSMakeRange(0, 0);
|
||||
apiCompliance.initialSelection = zeroSelection;
|
||||
|
||||
BOOL hasChanged = [apiCompliance hasLocationChanged:zeroSelection];
|
||||
XCTAssertFalse(hasChanged, @"Location change unexpected for matching selections");
|
||||
}
|
||||
|
||||
/**
|
||||
should not encounter the unintuitive case
|
||||
of moving from a discrete location to NSNotFound because we will
|
||||
catch this first with the method validateNewLocation
|
||||
*/
|
||||
- (void)testHasLocationChanged_newLocationNotFound_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
NSRange notFoundSelection = NSMakeRange(NSNotFound, NSNotFound);
|
||||
apiCompliance.initialSelection = notFoundSelection;
|
||||
|
||||
BOOL hasChanged = [apiCompliance hasLocationChanged:notFoundSelection];
|
||||
XCTAssertFalse(hasChanged, @"Location change unexpected for selection of NSNotFound");
|
||||
}
|
||||
|
||||
/**
|
||||
* unlikely scenario, because if the initial location is notFound, then the app is
|
||||
* probably non-complianat because we are still unable to get the current selection
|
||||
*/
|
||||
- (void)testHasLocationChanged_initialLocationNotFound_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
NSRange notFoundSelection = NSMakeRange(NSNotFound, NSNotFound);
|
||||
apiCompliance.initialSelection = notFoundSelection;
|
||||
|
||||
BOOL hasChanged = [apiCompliance hasLocationChanged:NSMakeRange(0, 0)];
|
||||
XCTAssertTrue(hasChanged, @"Location change unexpected for selection of NSNotFound");
|
||||
}
|
||||
|
||||
- (void)testHasLocationChanged_fromZeroToOne_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
apiCompliance.initialSelection = NSMakeRange(0, 0);
|
||||
|
||||
BOOL hasChanged = [apiCompliance hasLocationChanged:NSMakeRange(1, 0)];
|
||||
XCTAssertTrue(hasChanged, @"Location change expected for inserting one char");
|
||||
}
|
||||
|
||||
|
||||
- (void)testIsClientAppLegacy_unlistedClientAppId_returnsNo {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.compliant.app";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.microsoft.VSCode",@"com.adobe.Photoshop",nil];
|
||||
|
||||
BOOL isLegacy = [apiCompliance arrayContainsApplicationId:clientAppId fromArray:legacyAppsArray];
|
||||
NSLog(@"isLegacy = %@", isLegacy?@"yes":@"no");
|
||||
XCTAssertFalse(isLegacy, @"App not expected to be in legacy list");
|
||||
}
|
||||
|
||||
- (void)testIsClientAppLegacy_listedClientAppId_returnsYes {
|
||||
id client = [[AppleCompliantTestClient alloc] init];
|
||||
NSString *clientAppId = @"com.microsoft.VSCode";
|
||||
TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId];
|
||||
|
||||
NSArray *legacyAppsArray = [NSArray arrayWithObjects:@"com.adobe.Photoshop",@"com.microsoft.VSCode",nil];
|
||||
|
||||
BOOL isLegacy = [apiCompliance arrayContainsApplicationId:clientAppId fromArray:legacyAppsArray];
|
||||
XCTAssertTrue(isLegacy, @"App expected to be in legacy list");
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Add table
Reference in a new issue