TwitterNetworkLayerTests/TNLResponseTest.m (530 lines of code) (raw):

// // TNLResponseTest.m // TwitterNetworkLayer // // Created on 11/12/14. // Copyright © 2020 Twitter. All rights reserved. // #import "NSURLResponse+TNLAdditions.h" #import "TNL_Project.h" #import "TNLAttemptMetaData.h" #import "TNLAttemptMetaData_Project.h" #import "TNLAttemptMetrics_Project.h" #import "TNLRequest.h" #import "TNLResponse_Project.h" @import XCTest; // we need a leaway of at least 1 second since the timing is only to the 1 second granularity level // that means rounding/truncation of the time could have the timing be up to a second off #define ACCURACY_LEEWAY (1.15) @interface TNLResponseEncodedRequestTest : NSObject <TNLRequest, NSSecureCoding> /** The name of the source `TNLRequest` class that was encoded */ @property (nonatomic, readonly, copy, nullable) NSString *encodedSourceRequestClassName; /** If the encoded source request has a body */ @property (nonatomic, readonly) BOOL encodedSourceRequestHadBody; /** Unavailable */ - (instancetype)init NS_UNAVAILABLE; /** Unavailable */ + (instancetype)new NS_UNAVAILABLE; @end @implementation TNLResponseEncodedRequestTest { NSURL *_URL; TNLHTTPMethod _HTTPMethodValue; NSDictionary<NSString *, NSString *> *_allHTTPHeaderFields; } + (BOOL)supportsSecureCoding { return YES; } - (instancetype)initWithCoder:(NSCoder *)coder { if (self = [super init]) { _encodedSourceRequestHadBody = [coder decodeBoolForKey:@"hasBody"]; _encodedSourceRequestClassName = [coder decodeObjectOfClass:[NSString class] forKey:@"sourceClass"]; _URL = [coder decodeObjectOfClass:[NSURL class] forKey:@"URL"]; _HTTPMethodValue = [coder decodeIntegerForKey:@"HTTPMethod"]; _allHTTPHeaderFields = [coder decodeObjectOfClass:[NSDictionary class] forKey:@"HTTPHeaders"]; } return self; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeBool:_encodedSourceRequestHadBody forKey:@"hasBody"]; [coder encodeObject:_encodedSourceRequestClassName forKey:@"sourceClass"]; [coder encodeObject:_URL forKey:@"URL"]; [coder encodeInteger:_HTTPMethodValue forKey:@"HTTPMethod"]; [coder encodeObject:_allHTTPHeaderFields forKey:@"HTTPHeaders"]; } - (nullable NSURL *)URL { return _URL; } - (TNLHTTPMethod)HTTPMethodValue { return _HTTPMethodValue; } - (nullable NSDictionary<NSString *, NSString *> *)allHTTPHeaderFields { return _allHTTPHeaderFields; } - (nullable NSData *)HTTPBody { if (_encodedSourceRequestHadBody) { return [@"{\"body\":true}" dataUsingEncoding:NSUTF8StringEncoding]; } return nil; } @end @interface TNLResponseTest : XCTestCase @end @implementation TNLResponseTest - (void)testNSCoding { NSURLRequest *finalRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://m.dummy.com"]]; NSHTTPURLResponse *urlResponse = [[NSHTTPURLResponse alloc] initWithURL:finalRequest.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Header1" : @"Value1" }]; id<TNLTemporaryFile> tempFile = nil; TNLResponseSource source = TNLResponseSourceNetworkRequest; NSData *data = [@"{ success: true }" dataUsingEncoding:NSUTF8StringEncoding]; NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.dummy.co"]]; [mRequest setValue:@"dummy" forHTTPHeaderField:@"X-Dummy"]; mRequest.HTTPMethod = @"POST"; mRequest.HTTPBody = [@"{\"dummy\":true}" dataUsingEncoding:NSUTF8StringEncoding]; NSURLRequest *request = [mRequest copy]; NSDictionary *userInfo = @{ NSUnderlyingErrorKey : [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotConnectToHost userInfo:@{ NSDebugDescriptionErrorKey: @"Error with Host", @"tnl.debug" : @"not debuggable" }], @"level" : @"top" }; NSError *error = TNLErrorCreateWithCodeAndUserInfo(TNLErrorCodeGlobalHostWasBlocked, userInfo); uint64_t enqueueTime, firstAttemptStartTime, currentAttemptStartTime, currentAttemptEndTime, completeTime = 0; NSDate *enqueueDate, *firstAttemptStartDate, *currentAttemptStartDate, *currentAttemptEndDate, *completeDate; TNLAttemptMetaData *metaData = [[TNLAttemptMetaData alloc] init]; metaData.HTTPVersion = @"1.1"; NSData *archive = nil; TNLResponse *decodedResponse = nil; // enqueue enqueueDate = [NSDate date]; enqueueTime = (UInt64)(NSEC_PER_SEC * CFAbsoluteTimeGetCurrent()); // start, 1 sec later firstAttemptStartDate = currentAttemptStartDate = [enqueueDate dateByAddingTimeInterval:1]; firstAttemptStartTime = currentAttemptStartTime = enqueueTime + (NSEC_PER_SEC * 1ULL); // end, 1 second later currentAttemptEndDate = [firstAttemptStartDate dateByAddingTimeInterval:1]; currentAttemptEndTime = currentAttemptStartTime + (NSEC_PER_SEC * 1ULL); // complete, 1 ms later completeDate = [currentAttemptEndDate dateByAddingTimeInterval:1.0 / 1000.0]; completeTime = currentAttemptEndTime + NSEC_PER_MSEC; TNLResponseInfo *info = [[TNLResponseInfo alloc] initWithFinalURLRequest:finalRequest URLResponse:urlResponse source:source data:data temporarySavedFile:tempFile]; TNLResponseMetrics *metrics = [[TNLResponseMetrics alloc] initWithEnqueueDate:enqueueDate enqueueTime:enqueueTime completeDate:completeDate completeTime:completeTime attemptMetrics:nil]; [metrics addInitialStartWithDate:firstAttemptStartDate machTime:firstAttemptStartTime request:finalRequest]; [metrics addMetaData:metaData taskMetrics:nil]; [metrics addEndDate:currentAttemptEndDate machTime:currentAttemptEndTime response:info.URLResponse operationError:error]; TNLResponse *response = [TNLResponse responseWithRequest:request operationError:error info:info metrics:metrics]; XCTAssertEqualWithAccuracy(response.metrics.totalDuration, 2.001, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.queuedDuration, 1.0, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.allAttemptsDuration, 1.0, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.currentAttemptDuration, 1.0, 0.0005); if (tnl_available_ios_11) { error = nil; archive = [NSKeyedArchiver archivedDataWithRootObject:response requiringSecureCoding:YES error:&error]; #if !TARGET_OS_MACCATALYST } else { archive = [NSKeyedArchiver archivedDataWithRootObject:response]; #endif } XCTAssertNotEqual(0UL, archive.length); if (tnl_available_ios_11) { error = nil; decodedResponse = [NSKeyedUnarchiver unarchivedObjectOfClass:[TNLResponse class] fromData:archive error:&error]; #if !TARGET_OS_MACCATALYST } else { decodedResponse = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; #endif } XCTAssertTrue([decodedResponse isKindOfClass:[TNLResponse class]]); if (![decodedResponse isKindOfClass:[TNLResponse class]]) { return; } // metrics XCTAssertEqualWithAccuracy(response.metrics.totalDuration, decodedResponse.metrics.totalDuration, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.queuedDuration, decodedResponse.metrics.queuedDuration, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.allAttemptsDuration, decodedResponse.metrics.allAttemptsDuration, 0.0005); XCTAssertEqualWithAccuracy(response.metrics.currentAttemptDuration, decodedResponse.metrics.currentAttemptDuration, 0.0005); XCTAssertEqualObjects([response.metrics.attemptMetrics.lastObject metaData], [decodedResponse.metrics.attemptMetrics.lastObject metaData]); XCTAssertEqual(response.metrics.attemptCount, decodedResponse.metrics.attemptCount); XCTAssertEqualObjects(response.metrics, decodedResponse.metrics); XCTAssertEqual([response.metrics.attemptMetrics.firstObject attemptType], [decodedResponse.metrics.attemptMetrics.firstObject attemptType]); // info XCTAssertEqualObjects(response.info.finalURLRequest, decodedResponse.info.finalURLRequest); XCTAssertTrue([response.info.URLResponse tnl_isEqualToResponse:decodedResponse.info.URLResponse]); XCTAssertEqualObjects(response.info.data, decodedResponse.info.data); XCTAssertTrue((response.info.temporarySavedFile == nil) == (decodedResponse.info.temporarySavedFile == nil)); XCTAssertEqualObjects(response.info, decodedResponse.info); // base XCTAssertEqualObjects(TNLErrorToSecureCodingError(response.operationError), decodedResponse.operationError); XCTAssertTrue(TNLRequestEqualToRequest(response.originalRequest, decodedResponse.originalRequest, YES /*quickBodyCheck*/), @"%@ must be equal to %@", response.originalRequest, decodedResponse.originalRequest); XCTAssertEqualObjects(response, decodedResponse); /// Try decoding with a mapping TNLResponseEncodedRequest *encodedRequest = [[TNLResponseEncodedRequest alloc] initWithSourceRequest:request]; response = [TNLResponse responseWithRequest:encodedRequest operationError:error info:info metrics:metrics]; [NSKeyedUnarchiver setClass:[TNLResponseEncodedRequestTest class] forClassName:@"TNLResponseEncodedRequest"]; tnl_defer(^{ [NSKeyedUnarchiver setClass:Nil forClassName:@"TNLResponseEncodedRequest"]; }); if (tnl_available_ios_11) { error = nil; archive = [NSKeyedArchiver archivedDataWithRootObject:response requiringSecureCoding:YES error:&error]; #if !TARGET_OS_MACCATALYST } else { archive = [NSKeyedArchiver archivedDataWithRootObject:response]; #endif } XCTAssertNotEqual(0UL, archive.length); if (tnl_available_ios_11) { error = nil; decodedResponse = [NSKeyedUnarchiver unarchivedObjectOfClass:[TNLResponse class] fromData:archive error:&error]; #if !TARGET_OS_MACCATALYST } else { decodedResponse = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; #endif } XCTAssertTrue([decodedResponse isKindOfClass:[TNLResponse class]]); if (![decodedResponse isKindOfClass:[TNLResponse class]]) { return; } XCTAssertTrue(TNLRequestEqualToRequest(response.originalRequest, decodedResponse.originalRequest, YES /*quickBodyCheck*/)); } - (void)testRetryAfterMethods { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; dateFormatter.locale = enUSPOSIXLocale; dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"]; TNLResponseInfo *info; NSString *value; NSDate *date; CFAbsoluteTime start, end; // Invalid start = CFAbsoluteTimeGetCurrent(); value = nil; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertFalse(info.hasRetryAfterHeader); XCTAssertNil(info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"Dummy"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"0.5"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"-1"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @""; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"2147483648"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"15000000000"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNil(info.retryAfterDate); XCTAssertEqual(NSTimeIntervalSince1970, info.retryAfterDelayFromNow); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } // Delay start = CFAbsoluteTimeGetCurrent(); value = @"0"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 0.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"000"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 0.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"1"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 1.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"0000000000000000000001"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 1.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"64000"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 64000.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"2147483647"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 2147483647.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } start = CFAbsoluteTimeGetCurrent(); value = @"0"; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, -1.0, ACCURACY_LEEWAY); end = CFAbsoluteTimeGetCurrent(); if (end - start > 1.2) { NSLog(@"Slow execution! %fs!", end - start); } // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 dateFormatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss zzz"; date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 0.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:1.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:64000.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 64000.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, -1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 1.2) { NSLog(@"Slow execution! %fs!", end - start - 1.0); } // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 dateFormatter.dateFormat = @"EEEE, dd-MMM-yy HH:mm:ss zzz"; date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 0.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:1.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:64000.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 64000.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, -1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 1.2) { NSLog(@"Slow execution! %fs!", end - start - 1.0); } // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format dateFormatter.dateFormat = @"EEE MMM d HH:mm:ss yyyy"; date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 0.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:1.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:64000.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, 64000.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 0.2) { NSLog(@"Slow execution! %fs!", end - start); } date = [NSDate dateWithTimeIntervalSinceNow:0.0]; start = CFAbsoluteTimeGetCurrent(); value = [dateFormatter stringFromDate:date]; info = [self fakeResponseInfoWithRetryAfterHeaderValue:value]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; XCTAssertTrue(info.hasRetryAfterHeader); XCTAssertEqualObjects(value, info.retryAfterRawValue); XCTAssertNotNil(info.retryAfterDate); XCTAssertEqualWithAccuracy(info.retryAfterDelayFromNow, -1.0, ACCURACY_LEEWAY, @"Value Date: %@, Parsed Date: %@", date, info.retryAfterDate); end = CFAbsoluteTimeGetCurrent(); if (end - start > 1.2) { NSLog(@"Slow execution! %fs!", end - start - 1.0); } } - (void)testValueForHeaderField { NSString *const kExpectedValue = @"42"; TNLResponseInfo *info = [self fakeResponseInfoWithRetryAfterHeaderValue:kExpectedValue]; XCTAssertNotNil(info); XCTAssertEqualObjects([info valueForResponseHeaderField:@"Retry-After"], kExpectedValue); XCTAssertEqualObjects([info valueForResponseHeaderField:@"retry-after"], kExpectedValue); XCTAssertEqualObjects([info valueForResponseHeaderField:@"RETRY-AFTER"], kExpectedValue); XCTAssertNil([info valueForResponseHeaderField:@"retryafter"]); XCTAssertNil([info valueForResponseHeaderField:@""]); XCTAssertNil([info valueForResponseHeaderField:(NSString * __nonnull)nil]); } - (TNLResponseInfo *)fakeResponseInfoWithRetryAfterHeaderValue:(NSString *)retryAfterHeaderValue { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.dummy.com"]]; NSDictionary *dictionary = (retryAfterHeaderValue) ? @{ @"Retry-After" : retryAfterHeaderValue } : @{}; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:TNLHTTPStatusCodeServiceUnavailable HTTPVersion:@"HTTP/1.1" headerFields:dictionary]; TNLResponseInfo *info = [[TNLResponseInfo alloc] initWithFinalURLRequest:request URLResponse:response source:TNLResponseSourceNetworkRequest data:nil temporarySavedFile:nil]; return info; } @end