/** * 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 #import "TextApiCompliance.h" #import "AppleCompliantTestClient.h" #import "KMLogs.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)testInitComplianceObject_nilClient_returnsInstance { NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:nil applicationId:clientAppId]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is not nil"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testInitComplianceObject_nilApplicationId_returnsInstance { id client = [[AppleCompliantTestClient alloc] init]; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:nil]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testInitComplianceObject_nilInputs_returnsInstance { TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:nil applicationId:nil]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testCheckCompliance_nilClient_returnsNoncompliant { NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:nil applicationId:clientAppId]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); [apiCompliance checkCompliance]; XCTAssertFalse(apiCompliance.isComplianceUncertain, @"ApiCompliance with nil client is uncertain"); XCTAssertFalse(apiCompliance.canReadText, @"ApiCompliance with nil client is compliant"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testCheckComplianceAfterInsert_nilClient_returnsNoncompliant { NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:nil applicationId:clientAppId]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); [apiCompliance checkComplianceAfterInsert:@"a" deleted:@"x"]; XCTAssertFalse(apiCompliance.isComplianceUncertain, @"ApiCompliance with nil client is uncertain"); XCTAssertFalse(apiCompliance.canReadText, @"ApiCompliance with nil client is compliant"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testCheckCompliance_nilApplicationId_returnsUncertain { id client = [[AppleCompliantTestClient alloc] init]; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:nil]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); [apiCompliance checkCompliance]; XCTAssertTrue(apiCompliance.isComplianceUncertain, @"New apiCompliance is not uncertain"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testCheckComplianceAfterInsert_nilApplicationId_returnsNotUncertain { id client = [[AppleCompliantTestClient alloc] init]; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:nil]; XCTAssertNotNil(apiCompliance, @"New apiCompliance is nil"); [apiCompliance checkComplianceAfterInsert:@"a" deleted:@"x"]; XCTAssertFalse(apiCompliance.isComplianceUncertain, @"New apiCompliance is uncertain"); os_log_debug([KMLogs testLog], "apiCompliance = %@", apiCompliance); } - (void)testMatchingClient_sameClient_returnsTrue { id client = [[AppleCompliantTestClient alloc] init]; NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId]; XCTAssertTrue([apiCompliance isMatchingClient:client applicationId:clientAppId], @"Clients do not match"); } - (void)testMatchingClient_differentClients_returnsFalse { id client = [[AppleCompliantTestClient alloc] init]; id noContextClient = [[NoContextTestClient alloc] init]; NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId]; XCTAssertFalse([apiCompliance isMatchingClient:noContextClient applicationId:clientAppId], @"Unique clients match unexpectedly"); } - (void)testMatchingClient_differentClientApplicationIDs_returnsFalse { id client = [[AppleCompliantTestClient alloc] init]; NSString *clientAppId = @"com.compliant.app"; TextApiCompliance *apiCompliance = [[TextApiCompliance alloc]initWithClient:client applicationId:clientAppId]; XCTAssertFalse([apiCompliance isMatchingClient:client applicationId:@"com.imaginary.app"], @"Unique client application IDs match unexpectedly"); } - (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]; os_log_debug([KMLogs testLog], "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