TwitterNetworkLayerTests/NSURLCache+TNLAdditionsTest.m (157 lines of code) (raw):

// // NSURLCache+TNLAdditionsTest.m // TwitterNetworkLayer // // Created on 10/28/14. // Copyright © 2020 Twitter. All rights reserved. // #import "NSURLCache+TNLAdditions.h" #import "TNL_Project.h" #import "TNLHTTPRequest.h" #import "TNLNetwork.h" #import "TNLPseudoURLProtocol.h" #import "TNLRequestDelegate.h" #import "TNLRequestOperation.h" #import "TNLRequestOperationQueue.h" #import "TNLResponse_Project.h" @import XCTest; @interface NSURLCache_TNLAdditionsTest : XCTestCase @property (atomic) NSUInteger spinUps; @end @implementation NSURLCache_TNLAdditionsTest - (void)testNSURLImpotentCache { NSURLCache *cache = [NSURLCache tnl_impotentURLCache]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.dummy.com/path?something=else"]]; NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Header1" : @"Value1" }]; NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:[@"Random Data" dataUsingEncoding:NSUTF8StringEncoding] userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; XCTAssertEqual(0UL, cache.currentMemoryUsage); XCTAssertEqual(0UL, cache.currentDiskUsage); XCTAssertNil([cache cachedResponseForRequest:request]); [cache storeCachedResponse:cachedResponse forRequest:request]; XCTAssertEqual(0UL, cache.currentMemoryUsage); XCTAssertEqual(0UL, cache.currentDiskUsage); XCTAssertNil([cache cachedResponseForRequest:request]); } + (TNLResponse *)GETResponseWithURL:(NSURL *)URL config:(TNLRequestConfiguration *)config { __block TNLResponse *response = nil; TNLHTTPRequest *request = [TNLHTTPRequest GETRequestWithURL:URL HTTPHeaderFields:nil]; TNLRequestOperation *op = [TNLRequestOperation operationWithRequest:request configuration:config completion:^(TNLRequestOperation *operation, TNLResponse *opResponse) { response = opResponse; }]; [[TNLRequestOperationQueue defaultOperationQueue] enqueueRequestOperation:op]; [op waitUntilFinishedWithoutBlockingRunLoop]; return response; } - (void)testCacheHitDetection { NSURL *URL = [NSURL URLWithString:@"http://cache.dummy.com/cacheable"]; NSData *body = [URL.absoluteString dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *headers = @{ @"content-length" : [@(body.length) description], @"cache-control" : @"max-age=10000", @"date" : TNLHTTPDateToString([NSDate date], TNLHTTPDateFormatAuto), }; NSHTTPURLResponse *URLResponse = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers]; [TNLPseudoURLProtocol registerURLResponse:URLResponse body:body withEndpoint:URL]; tnl_defer(^{ [TNLPseudoURLProtocol unregisterEndpoint:URL]; }); NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:NSStringFromSelector(_cmd)]; [[NSFileManager defaultManager] removeItemAtPath:path error:NULL]; NSURLCache *cache; if (tnl_available_ios_13) { cache = [[NSURLCache alloc] initWithMemoryCapacity:1024*1024*10 diskCapacity:1024*1024*10 directoryURL:[NSURL URLWithString:path]]; } #if !TARGET_OS_MACCATALYST else { cache = [[NSURLCache alloc] initWithMemoryCapacity:1024*1024*10 diskCapacity:1024*1024*10 diskPath:path]; } #endif [cache removeAllCachedResponses]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; // give cache time to purge tnl_defer(^{ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; // give cache time to store cache entries [cache removeAllCachedResponses]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; // give cache time to purge }); TNLResponse *response = nil; TNLMutableRequestConfiguration *config = [TNLMutableRequestConfiguration defaultConfiguration]; config.URLCache = cache; config.protocolOptions = TNLRequestProtocolOptionPseudo; config.cachePolicy = NSURLRequestReturnCacheDataElseLoad; response = [[self class] GETResponseWithURL:URL config:config]; XCTAssertEqualObjects(response.info.data, body); XCTAssertEqual(response.info.source, TNLResponseSourceNetworkRequest); response = [[self class] GETResponseWithURL:URL config:config]; XCTAssertEqualObjects(response.info.data, body); XCTAssertEqual(response.info.source, TNLResponseSourceLocalCache); config.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; response = [[self class] GETResponseWithURL:URL config:config]; XCTAssertEqualObjects(response.info.data, body); XCTAssertEqual(response.info.source, TNLResponseSourceNetworkRequest); [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; // give cache time to store cache entries [cache removeAllCachedResponses]; [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; // give cache time to purge config.cachePolicy = NSURLRequestReturnCacheDataDontLoad; response = [[self class] GETResponseWithURL:URL config:config]; XCTAssertNil(response.info.data); XCTAssertNotNil(response.operationError); XCTAssertEqual(response.info.source, TNLResponseSourceNetworkRequest); } - (void)testSessionCoalescing { NSURL *URL = [NSURL URLWithString:@"http://cache.dummy.com/cacheable"]; NSData *body = [URL.absoluteString dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *headers = @{ @"content-length" : [@(body.length) description], @"cache-control" : @"max-age=10000", @"date" : TNLHTTPDateToString([NSDate date], TNLHTTPDateFormatAuto), }; NSHTTPURLResponse *URLResponse = [[NSHTTPURLResponse alloc] initWithURL:URL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headers]; [TNLPseudoURLProtocol registerURLResponse:URLResponse body:body withEndpoint:URL]; tnl_defer(^{ [TNLPseudoURLProtocol unregisterEndpoint:URL]; }); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didSpinUpSession:) name:TNLNetworkDidSpinUpSessionNotification object:nil]; NSURLCache *cache = nil; TNLResponse *response = nil; TNLMutableRequestConfiguration *config = [TNLMutableRequestConfiguration defaultConfiguration]; config.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; config.protocolOptions = TNLRequestProtocolOptionPseudo; // warm the NSURLSessions response = [[self class] GETResponseWithURL:URL config:config]; const NSUInteger originalSpinUpCount = self.spinUps; // 1) shared cache cache = [NSURLCache sharedURLCache]; config.URLCache = cache; tnl_defer(^{ [cache removeAllCachedResponses]; }); response = [[self class] GETResponseWithURL:URL config:config]; // 2) proxy shared cache cache = [NSURLCache tnl_sharedURLCacheProxy]; config.URLCache = cache; tnl_defer(^{ [cache removeAllCachedResponses]; }); response = [[self class] GETResponseWithURL:URL config:config]; // 3) impotent cache cache = [NSURLCache tnl_impotentURLCache]; config.URLCache = cache; tnl_defer(^{ [cache removeAllCachedResponses]; }); response = [[self class] GETResponseWithURL:URL config:config]; // 4) nil cache cache = nil; config.URLCache = cache; tnl_defer(^{ [cache removeAllCachedResponses]; }); response = [[self class] GETResponseWithURL:URL config:config]; // TNL will use demuxing to avoid spinning up multiple sessions XCTAssertEqual(originalSpinUpCount, self.spinUps); } - (void)_didSpinUpSession:(NSNotification *)note { self.spinUps++; } @end