From 8f04f0b6e8e1d7fcecb734d8df8b0046489697e9 Mon Sep 17 00:00:00 2001 From: smalsam-newr <84096330+smalsam-newr@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:42:02 -0800 Subject: [PATCH] remove while loop from tests (#186) * Replace timer based polling with file watching * Added log to determine where attributes are getting lost * Founded missed NSLog call --- Agent/Analytics/PersistentEventStore.m | 2 +- .../Analytics-Tests/PersistentStoreTests.m | 181 +++++++++++------- 2 files changed, 114 insertions(+), 69 deletions(-) diff --git a/Agent/Analytics/PersistentEventStore.m b/Agent/Analytics/PersistentEventStore.m index c4fa2da4..4e0baac1 100644 --- a/Agent/Analytics/PersistentEventStore.m +++ b/Agent/Analytics/PersistentEventStore.m @@ -45,7 +45,7 @@ - (void)setObject:(nonnull id)object forKey:(nonnull id)key { @synchronized (store) { store[key] = object; _dirty = YES; - NSLog(@"Marked dirty for adding"); + NRLOG_VERBOSE(@"Marked dirty for adding"); } @synchronized (self) { diff --git a/Tests/Unit-Tests/NewRelicAgentTests/Analytics-Tests/PersistentStoreTests.m b/Tests/Unit-Tests/NewRelicAgentTests/Analytics-Tests/PersistentStoreTests.m index 7e8d0c3a..dad3088f 100644 --- a/Tests/Unit-Tests/NewRelicAgentTests/Analytics-Tests/PersistentStoreTests.m +++ b/Tests/Unit-Tests/NewRelicAgentTests/Analytics-Tests/PersistentStoreTests.m @@ -67,7 +67,7 @@ @implementation PersistentStoreTests { } static NSString *testFilename = @"fbstest_tempStore"; -static NSTimeInterval shortTimeInterval = 1; +static NSTimeInterval shortTimeInterval = 10; - (void)setUp { @@ -125,6 +125,41 @@ - (void)testStoresObject { - (void)testWritesObjectToFile { // Given XCTestExpectation *writeExpectation = [self expectationWithDescription:@"Waiting for write delay to write file"]; + + int docsDirDescriptor = open(".", O_EVTONLY); + dispatch_source_t fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, docsDirDescriptor, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + dispatch_source_set_event_handler(fileSource, ^{ + unsigned long data = dispatch_source_get_data(fileSource); + if(data & DISPATCH_VNODE_DELETE) { + NSLog(@"Watched File Deleted!"); + dispatch_source_cancel(fileSource); + return; + } + + NSLog(@"File found and has data"); + NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; + NSError *error = nil; + NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData + error:&error]; + if(retrievedDictionary.count == 1) { + NSLog(@"Initial file found and full"); + NSDictionary *attributes = ((NRMAMobileEvent *)retrievedDictionary[@"aKey"]).attributes; + if(attributes.count == 3) { + NSLog(@"file has right number of attributes"); + } else { + NSLog(@"file has %d number of attributes", attributes.count); + } + dispatch_cancel(fileSource); + close(docsDirDescriptor); + [writeExpectation fulfill]; + } else if (retrievedDictionary == nil) { + NSLog(@"File doesn't exist yet"); + } else if(retrievedDictionary.count != 1){ + NSLog(@"File found, but has a count of %lu", (unsigned long)retrievedDictionary.count); + } + }); + dispatch_resume(fileSource); + PersistentEventStore *sut = [[PersistentEventStore alloc] initWithFilename:testFilename andMinimumDelay:1]; @@ -140,35 +175,24 @@ - (void)testWritesObjectToFile { [sut setObject:testEvent forKey:@"aKey"]; // Then - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, shortTimeInterval*NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - NSFileManager *fileManager = [NSFileManager defaultManager]; - - while(1) { - if([fileManager fileExistsAtPath:testFilename]) { - NSLog(@"File Found"); - break; - } - sleep(1); - } - NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; - NSError *error = nil; - NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData - error:&error]; - XCTAssertNil(error, "Error testing file written: %@", [error localizedDescription]); - XCTAssertEqual([retrievedDictionary count], 1); - - PersistentEventStore *anotherOne = [[PersistentEventStore alloc] initWithFilename:testFilename - andMinimumDelay:1]; - [anotherOne load:&error]; - XCTAssertNil(error, "Error loading previous events: %@", [error localizedDescription]); - TestEvent *anotherEvent = [anotherOne objectForKey:@"aKey"]; - XCTAssertNotNil(anotherEvent); - XCTAssertEqual(anotherEvent.timestamp, testEvent.timestamp); - XCTAssertEqual(anotherEvent.sessionElapsedTimeSeconds, testEvent.sessionElapsedTimeSeconds); - XCTAssertEqual([anotherEvent.attributes count], [testEvent.attributes count]); - [writeExpectation fulfill]; - }); [self waitForExpectationsWithTimeout:shortTimeInterval*3 handler:nil]; + + NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; + NSError *error = nil; + NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData + error:&error]; + XCTAssertNil(error, "Error testing file written: %@", [error localizedDescription]); + XCTAssertEqual([retrievedDictionary count], 1); + + PersistentEventStore *anotherOne = [[PersistentEventStore alloc] initWithFilename:testFilename + andMinimumDelay:1]; + [anotherOne load:&error]; + XCTAssertNil(error, "Error loading previous events: %@", [error localizedDescription]); + TestEvent *anotherEvent = [anotherOne objectForKey:@"aKey"]; + XCTAssertNotNil(anotherEvent); + XCTAssertEqual(anotherEvent.timestamp, testEvent.timestamp); + XCTAssertEqual(anotherEvent.sessionElapsedTimeSeconds, testEvent.sessionElapsedTimeSeconds); + XCTAssertEqual([anotherEvent.attributes count], [testEvent.attributes count]); } - (void)testStoreReturnsNoIfFileDoesNotExist { @@ -181,6 +205,34 @@ - (void)testStoreReturnsNoIfFileDoesNotExist { - (void)testStoreHandlesDifferentTypesOfEvents { // Given XCTestExpectation *writeExpectation = [self expectationWithDescription:@"Waiting for write delay to write file"]; + int docsDirDescriptor = open(".", O_EVTONLY); + dispatch_source_t fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, docsDirDescriptor, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + dispatch_source_set_event_handler(fileSource, ^{ + unsigned long data = dispatch_source_get_data(fileSource); + if(data & DISPATCH_VNODE_DELETE) { + NSLog(@"Watched File Deleted!"); + dispatch_source_cancel(fileSource); + return; + } + + NSLog(@"File found and has data"); + NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; + NSError *error = nil; + NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData + error:&error]; + if(retrievedDictionary.count == 3) { + NSLog(@"Initial file found and full"); + dispatch_cancel(fileSource); + close(docsDirDescriptor); + [writeExpectation fulfill]; + } else if (retrievedDictionary == nil) { + NSLog(@"File doesn't exist yet"); + } else if(retrievedDictionary.count != 3){ + NSLog(@"File found, but has a count of %lu", (unsigned long)retrievedDictionary.count); + } + }); + dispatch_resume(fileSource); + PersistentEventStore *originalStore = [[PersistentEventStore alloc] initWithFilename:testFilename andMinimumDelay:1]; NSError *error = nil; @@ -195,27 +247,6 @@ - (void)testStoreHandlesDifferentTypesOfEvents { [originalStore setObject:interactionEvent forKey:@"Interaction Event"]; // Then - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, shortTimeInterval*NSEC_PER_SEC), dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ - - while(1) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - - if(![fileManager fileExistsAtPath:testFilename]) { - continue; - } - NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; - NSError *error = nil; - NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData - error:&error]; - if(retrievedDictionary.count == 3) { - NSLog(@"Found full file"); - break; - } else { - NSLog(@"Found file with %d entries: ", retrievedDictionary.count); - } - } - [writeExpectation fulfill]; - }); [self waitForExpectationsWithTimeout:shortTimeInterval*5 handler:nil]; PersistentEventStore *anotherOne = [[PersistentEventStore alloc] initWithFilename:testFilename @@ -246,6 +277,9 @@ - (void)testStoreHandlesDifferentTypesOfEvents { - (void)testEventRemoval { // Given + int docsDirDescriptor = open(".", O_EVTONLY); + dispatch_source_t fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, docsDirDescriptor, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + XCTestExpectation *waitForInitialWriteExpectation = [self expectationWithDescription:@"Waiting for the first time the file is written"]; PersistentEventStore *sut = [[PersistentEventStore alloc] initWithFilename:testFilename andMinimumDelay:1]; @@ -256,28 +290,38 @@ - (void)testEventRemoval { NRMARequestEvent *requestEvent = [self createRequestEvent]; NRMAInteractionEvent *interactionEvent = [self createInteractionEvent]; + + dispatch_source_set_event_handler(fileSource, ^{ + unsigned long data = dispatch_source_get_data(fileSource); + if(data & DISPATCH_VNODE_DELETE) { + NSLog(@"Watched File Deleted!"); + dispatch_source_cancel(fileSource); + return; + } + + NSLog(@"File found and has data"); + NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; + NSError *error = nil; + NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData + error:&error]; + if(retrievedDictionary.count == 3) { + NSLog(@"Initial file found and full"); + dispatch_cancel(fileSource); + close(docsDirDescriptor); + [waitForInitialWriteExpectation fulfill]; + } else if (retrievedDictionary == nil) { + NSLog(@"File doesn't exist yet"); + } else if(retrievedDictionary.count != 3){ + NSLog(@"File found, but has a count of %lu", (unsigned long)retrievedDictionary.count); + } + }); + dispatch_resume(fileSource); + [sut setObject:customEvent forKey:@"Custom Event"]; [sut setObject:requestEvent forKey:@"Request Event"]; [sut setObject:interactionEvent forKey:@"Interaction Event"]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, shortTimeInterval*NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - - while(1) { - NSData *retrievedData = [NSData dataWithContentsOfFile:testFilename]; - NSError *error = nil; - NSMutableDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveTopLevelObjectWithData:retrievedData - error:&error]; - if(retrievedDictionary.count == 3) { - NSLog(@"Initial file found and full"); - break; - } else { - NSLog(@"File not found or not full"); - } - sleep(1); - } - [waitForInitialWriteExpectation fulfill]; - }); - [self waitForExpectationsWithTimeout:shortTimeInterval*3 handler:nil]; + [self waitForExpectationsWithTimeout:shortTimeInterval*5 handler:nil]; // When [sut removeObjectForKey:@"Request Event"]; @@ -285,6 +329,7 @@ - (void)testEventRemoval { // Then XCTestExpectation *writeExpectation = [self expectationWithDescription:@"Waiting for write delay to write file"]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, shortTimeInterval*NSEC_PER_SEC), dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{ NSFileManager *fileManager = [NSFileManager defaultManager];