FPRNSURLSessionInstrumentTest.m 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "FirebasePerformance/Tests/Unit/Instruments/FPRNSURLSessionInstrumentTestDelegates.h"
  15. #import <XCTest/XCTest.h>
  16. #import <objc/runtime.h>
  17. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations+Private.h"
  18. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
  19. #import "FirebasePerformance/Sources/Instrumentation/FPRNetworkTrace.h"
  20. #import "FirebasePerformance/Sources/Instrumentation/Network/FPRNSURLSessionInstrument.h"
  21. #import "FirebasePerformance/Sources/Instrumentation/Network/FPRNSURLSessionInstrument_Private.h"
  22. #import "FirebasePerformance/Sources/Public/FIRPerformance.h"
  23. #import "FirebasePerformance/Tests/Unit/FPRTestCase.h"
  24. #import "FirebasePerformance/Tests/Unit/Server/FPRHermeticTestServer.h"
  25. /** This class is used to wrap an NSURLSession object during testing. */
  26. @interface FPRNSURLSessionProxy : NSProxy {
  27. // The wrapped session object.
  28. id _session;
  29. }
  30. /** @return an instance of the session proxy. */
  31. - (instancetype)initWithSession:(id)session;
  32. @end
  33. @implementation FPRNSURLSessionProxy
  34. - (instancetype)initWithSession:(id)session {
  35. if (self) {
  36. _session = session;
  37. }
  38. return self;
  39. }
  40. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  41. return [_session methodSignatureForSelector:selector];
  42. }
  43. - (void)forwardInvocation:(NSInvocation *)invocation {
  44. [invocation invokeWithTarget:_session];
  45. }
  46. @end
  47. @interface FPRNSURLSessionInstrumentTest : FPRTestCase
  48. /** Test server to create connections to. */
  49. @property(nonatomic) FPRHermeticTestServer *testServer;
  50. @end
  51. @implementation FPRNSURLSessionInstrumentTest
  52. - (void)setUp {
  53. [super setUp];
  54. FIRPerformance *performance = [FIRPerformance sharedInstance];
  55. [performance setDataCollectionEnabled:YES];
  56. XCTAssertFalse(self.testServer.isRunning);
  57. self.testServer = [[FPRHermeticTestServer alloc] init];
  58. [self.testServer registerTestPaths];
  59. [self.testServer start];
  60. }
  61. - (void)tearDown {
  62. [super tearDown];
  63. FIRPerformance *performance = [FIRPerformance sharedInstance];
  64. [performance setDataCollectionEnabled:NO];
  65. [self.testServer stop];
  66. [FPRConfigurations reset];
  67. }
  68. /** Waits for the server connection to finish by giving a block to run just before a response is
  69. * sent.
  70. *
  71. * @param block A block to run just after the server response is sent.
  72. */
  73. - (void)waitAndRunBlockAfterResponse:(void (^)(id self,
  74. GCDWebServerRequest *_Nonnull request,
  75. GCDWebServerResponse *_Nonnull response))block {
  76. __block BOOL loopingMainThread = YES;
  77. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  78. dispatch_semaphore_t sema = dispatch_semaphore_create(0);
  79. __weak id weakSelf = self;
  80. self.testServer.responseCompletedBlock =
  81. ^(GCDWebServerRequest *_Nonnull request, GCDWebServerResponse *_Nonnull response) {
  82. block(weakSelf, request, response);
  83. dispatch_semaphore_signal(sema);
  84. };
  85. XCTAssertEqual(
  86. dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)), 0);
  87. loopingMainThread = NO;
  88. });
  89. // This is necessary because the FPRHermeticTestServer callbacks occur on the main thread.
  90. while (loopingMainThread) {
  91. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
  92. }
  93. }
  94. #pragma mark - Testing infrastructure and subclass instrumenting
  95. /** Tests initing of FPRNSURLSessionInstrument also inits NSURLSessionDelegate instrument. */
  96. - (void)testInit {
  97. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  98. [instrument registerInstrumentors];
  99. self.testServer.responseCompletedBlock =
  100. ^(GCDWebServerRequest *_Nonnull request, GCDWebServerResponse *_Nonnull response) {
  101. XCTAssert(instrument);
  102. XCTAssert(instrument.delegateInstrument);
  103. };
  104. [instrument deregisterInstrumentors];
  105. }
  106. /** Tests that creating a shared session returns a non-nil object. */
  107. - (void)testSharedSession {
  108. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  109. [instrument registerInstrumentors];
  110. NSURLSession *session = [NSURLSession sharedSession];
  111. XCTAssertNotNil(session);
  112. [instrument deregisterInstrumentors];
  113. }
  114. /** Tests that a method that returns an NSURLSession subclass registers that subclass. */
  115. - (void)testSubclassRegistration {
  116. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  117. [instrument registerInstrumentors];
  118. NSURLSession *session = [NSURLSession sharedSession];
  119. XCTAssertNotNil(session);
  120. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  121. [instrument deregisterInstrumentors];
  122. }
  123. /** Tests that a discovered subclass isn't registered more than once. */
  124. - (void)testSubclassIsNotRegisteredMoreThanOnce {
  125. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  126. [instrument registerInstrumentors];
  127. NSURLSession *session = [NSURLSession sharedSession];
  128. NSURLSession *session2 = [NSURLSession sharedSession];
  129. XCTAssertNotNil(session);
  130. XCTAssertNotNil(session2);
  131. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  132. [instrument deregisterInstrumentors];
  133. }
  134. /** Tests sessionWithConfiguration: with the default configurtion returns a non-nil object. */
  135. - (void)testSessionWithDefaultSessionConfiguration {
  136. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  137. [instrument registerInstrumentors];
  138. NSURLSessionConfiguration *configuration =
  139. [NSURLSessionConfiguration defaultSessionConfiguration];
  140. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  141. XCTAssertNotNil(session);
  142. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  143. [instrument deregisterInstrumentors];
  144. }
  145. /** Tests sessionWithConfiguration: with an ephemeral configuration returns a non-nil object. */
  146. - (void)testSessionWithEphemeralSessionConfiguration {
  147. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  148. [instrument registerInstrumentors];
  149. NSURLSessionConfiguration *configuration =
  150. [NSURLSessionConfiguration ephemeralSessionConfiguration];
  151. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  152. XCTAssertNotNil(session);
  153. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  154. [instrument deregisterInstrumentors];
  155. }
  156. /** Tests sessionWithConfiguration: with a background configuration returns a non-nil object. */
  157. - (void)testSessionWithBackgroundSessionConfiguration {
  158. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  159. [instrument registerInstrumentors];
  160. NSURLSessionConfiguration *configuration =
  161. [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"madeUpID"];
  162. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  163. XCTAssertNotNil(session);
  164. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  165. [instrument deregisterInstrumentors];
  166. }
  167. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  168. - (void)testProxyWrappedSharedSession {
  169. Method method = class_getClassMethod([NSURLSession class], @selector(sharedSession));
  170. IMP originalImp = method_getImplementation(method);
  171. IMP swizzledImp = imp_implementationWithBlock(^(id session) {
  172. typedef NSURLSession *(*OriginalImp)(id, SEL);
  173. NSURLSession *originalSession = ((OriginalImp)originalImp)(session, @selector(sharedSession));
  174. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  175. });
  176. method_setImplementation(method, swizzledImp);
  177. XCTAssertEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  178. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  179. [instrument registerInstrumentors];
  180. NSURLSession *session;
  181. XCTAssertNoThrow(session = [NSURLSession sharedSession]);
  182. NSURL *url = self.testServer.serverURL;
  183. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  184. NSURLSessionDownloadTask *task =
  185. [session downloadTaskWithURL:url
  186. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  187. NSError *_Nullable error) {
  188. [expectation fulfill];
  189. }];
  190. [task resume];
  191. XCTAssertNotNil(task);
  192. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  193. [instrument deregisterInstrumentors];
  194. method_setImplementation(method, originalImp);
  195. XCTAssertNotEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  196. }
  197. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  198. - (void)testProxyWrappedSessionWithConfiguration {
  199. Method method = class_getClassMethod([NSURLSession class], @selector(sessionWithConfiguration:));
  200. IMP originalImp = method_getImplementation(method);
  201. IMP swizzledImp = imp_implementationWithBlock(^(id session,
  202. NSURLSessionConfiguration *configuration) {
  203. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *);
  204. NSURLSession *originalSession =
  205. ((OriginalImp)originalImp)(session, @selector(sessionWithConfiguration:), configuration);
  206. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  207. });
  208. method_setImplementation(method, swizzledImp);
  209. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  210. XCTAssertEqual([[NSURLSession sessionWithConfiguration:config] class],
  211. [FPRNSURLSessionProxy class]);
  212. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  213. [instrument registerInstrumentors];
  214. NSURLSession *session;
  215. XCTAssertNoThrow(session = [NSURLSession sessionWithConfiguration:config]);
  216. NSURL *url = self.testServer.serverURL;
  217. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  218. NSURLSessionDownloadTask *task =
  219. [session downloadTaskWithURL:url
  220. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  221. NSError *_Nullable error) {
  222. [expectation fulfill];
  223. }];
  224. XCTAssertNotNil(task);
  225. [task resume];
  226. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  227. [instrument deregisterInstrumentors];
  228. method_setImplementation(method, originalImp);
  229. XCTAssertNotEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  230. }
  231. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  232. - (void)testProxyWrappedSessionWithConfigurationDelegateDelegateQueue {
  233. SEL selector = @selector(sessionWithConfiguration:delegate:delegateQueue:);
  234. Method method = class_getClassMethod([NSURLSession class], selector);
  235. IMP originalImp = method_getImplementation(method);
  236. IMP swizzledImp = imp_implementationWithBlock(
  237. ^(id session, NSURLSessionConfiguration *configuration, id<NSURLSessionDelegate> *delegate,
  238. NSOperationQueue *delegateQueue) {
  239. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *,
  240. id<NSURLSessionDelegate> *, NSOperationQueue *);
  241. NSURLSession *originalSession =
  242. ((OriginalImp)originalImp)(session, selector, configuration, delegate, delegateQueue);
  243. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  244. });
  245. method_setImplementation(method, swizzledImp);
  246. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  247. XCTAssertEqual([[NSURLSession sessionWithConfiguration:config delegate:nil
  248. delegateQueue:nil] class],
  249. [FPRNSURLSessionProxy class]);
  250. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  251. [instrument registerInstrumentors];
  252. NSURLSession *session;
  253. XCTAssertNoThrow(session = [NSURLSession sessionWithConfiguration:config
  254. delegate:nil
  255. delegateQueue:nil]);
  256. NSURL *url = self.testServer.serverURL;
  257. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  258. NSURLSessionDownloadTask *task =
  259. [session downloadTaskWithURL:url
  260. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  261. NSError *_Nullable error) {
  262. [expectation fulfill];
  263. }];
  264. XCTAssertNotNil(task);
  265. [task resume];
  266. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  267. [instrument deregisterInstrumentors];
  268. method_setImplementation(method, originalImp);
  269. }
  270. #pragma mark - Testing delegate method wrapping
  271. /** Tests using a nil delegate still results in tracking responses. */
  272. - (void)testSessionWithConfigurationDelegateDelegateQueueWithNilDelegate {
  273. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  274. [instrument registerInstrumentors];
  275. NSURLSessionConfiguration *configuration =
  276. [NSURLSessionConfiguration defaultSessionConfiguration];
  277. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  278. delegate:nil
  279. delegateQueue:nil];
  280. NSURLRequest *request = [NSURLRequest requestWithURL:self.testServer.serverURL];
  281. NSURLSessionTask *task;
  282. @autoreleasepool {
  283. task = [session dataTaskWithRequest:request];
  284. XCTAssertNotNil(task);
  285. [task resume];
  286. XCTAssertNotNil(session.delegate); // Not sure this is the desired behavior.
  287. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  288. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  289. GCDWebServerResponse *_Nonnull response) {
  290. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:task]);
  291. }];
  292. }
  293. [instrument deregisterInstrumentors];
  294. }
  295. /** Tests that the delegate class isn't instrumented more than once. */
  296. - (void)testDelegateClassOnlyRegisteredOnce {
  297. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  298. [instrument registerInstrumentors];
  299. FPRNSURLSessionCompleteTestDelegate *delegate =
  300. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  301. NSURLSessionConfiguration *configuration =
  302. [NSURLSessionConfiguration defaultSessionConfiguration];
  303. [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
  304. [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
  305. XCTAssertEqual(instrument.delegateInstrument.classInstrumentors.count, 1);
  306. XCTAssertEqual(instrument.delegateInstrument.instrumentedClasses.count, 1);
  307. [instrument deregisterInstrumentors];
  308. }
  309. /** Tests that the called delegate selector is wrapped and calls through. */
  310. - (void)testDelegateURLSessionTaskDidCompleteWithError {
  311. [self.testServer stop];
  312. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  313. [instrument registerInstrumentors];
  314. FPRNSURLSessionCompleteTestDelegate *delegate =
  315. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  316. // This request needs to fail.
  317. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://nonurl"]];
  318. NSURLSessionConfiguration *configuration =
  319. [NSURLSessionConfiguration defaultSessionConfiguration];
  320. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  321. delegate:delegate
  322. delegateQueue:nil];
  323. NSURLSessionTask *task;
  324. @autoreleasepool {
  325. task = [session dataTaskWithRequest:request];
  326. [task resume];
  327. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  328. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
  329. }
  330. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:task]);
  331. XCTAssertTrue(delegate.URLSessionTaskDidCompleteWithErrorCalled);
  332. [instrument deregisterInstrumentors];
  333. [self.testServer start];
  334. }
  335. /** Tests that the called delegate selector is wrapped and calls through. */
  336. - (void)testDelegateURLSessionTaskDidSendBodyDataTotalBytesSentTotalBytesExpectedToSend {
  337. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  338. [instrument registerInstrumentors];
  339. FPRNSURLSessionCompleteTestDelegate *delegate =
  340. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  341. NSURLSessionConfiguration *configuration =
  342. [NSURLSessionConfiguration defaultSessionConfiguration];
  343. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  344. delegate:delegate
  345. delegateQueue:nil];
  346. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  347. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  348. request.HTTPMethod = @"POST";
  349. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  350. withExtension:@""];
  351. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileURL];
  352. [uploadTask resume];
  353. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  354. FPRNetworkTrace *networkTrace = [FPRNetworkTrace networkTraceFromObject:uploadTask];
  355. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  356. GCDWebServerResponse *_Nonnull response) {
  357. XCTAssertTrue(delegate.URLSessionTaskDidSendBodyDataTotalBytesSentTotalBytesExpectedCalled);
  358. XCTAssert(networkTrace.requestSize > 0);
  359. XCTAssert(
  360. [networkTrace
  361. timeIntervalBetweenCheckpointState:FPRNetworkTraceCheckpointStateInitiated
  362. andState:FPRNetworkTraceCheckpointStateRequestCompleted] > 0);
  363. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  364. }];
  365. [instrument deregisterInstrumentors];
  366. }
  367. /** Tests that the called delegate selector is wrapped and calls through. */
  368. - (void)testDelegateURLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandler {
  369. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  370. [instrument registerInstrumentors];
  371. FPRNSURLSessionCompleteTestDelegate *delegate =
  372. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  373. NSURLSessionConfiguration *configuration =
  374. [NSURLSessionConfiguration defaultSessionConfiguration];
  375. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testRedirect"];
  376. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  377. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  378. delegate:delegate
  379. delegateQueue:nil];
  380. NSURLSessionTask *task = [session dataTaskWithRequest:request];
  381. [task resume];
  382. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  383. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  384. GCDWebServerResponse *_Nonnull response) {
  385. XCTAssertTrue(
  386. delegate.URLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandlerCalled);
  387. }];
  388. [instrument deregisterInstrumentors];
  389. }
  390. /** Tests that the called delegate selector is wrapped and calls through. */
  391. - (void)testDelegateURLSessionDataTaskDidReceiveData {
  392. FPRNSURLSessionInstrument *instrument;
  393. NSURLSessionDataTask *dataTask;
  394. @autoreleasepool {
  395. instrument = [[FPRNSURLSessionInstrument alloc] init];
  396. [instrument registerInstrumentors];
  397. FPRNSURLSessionCompleteTestDelegate *delegate =
  398. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  399. NSURLSessionConfiguration *configuration =
  400. [NSURLSessionConfiguration defaultSessionConfiguration];
  401. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  402. delegate:delegate
  403. delegateQueue:nil];
  404. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  405. dataTask = [session dataTaskWithURL:URL];
  406. [dataTask resume];
  407. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  408. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  409. GCDWebServerResponse *_Nonnull response) {
  410. XCTAssertTrue(delegate.URLSessionDataTaskDidReceiveDataCalled);
  411. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  412. }];
  413. }
  414. [instrument deregisterInstrumentors];
  415. }
  416. /** Tests that the called delegate selector is wrapped and calls through. */
  417. - (void)testDelegateURLSessionDownloadTaskDidFinishDownloadingToURL {
  418. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  419. [instrument registerInstrumentors];
  420. FPRNSURLSessionCompleteTestDelegate *delegate =
  421. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  422. NSURLSessionConfiguration *configuration =
  423. [NSURLSessionConfiguration defaultSessionConfiguration];
  424. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  425. delegate:delegate
  426. delegateQueue:nil];
  427. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  428. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  429. [downloadTask resume];
  430. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  431. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  432. GCDWebServerResponse *_Nonnull response) {
  433. XCTAssertTrue(delegate.URLSessionDownloadTaskDidFinishDownloadingToURLCalled);
  434. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  435. }];
  436. [instrument deregisterInstrumentors];
  437. }
  438. /** Tests that the called delegate selector is wrapped and calls through. */
  439. - (void)testDelegateURLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytes {
  440. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  441. [instrument registerInstrumentors];
  442. FPRNSURLSessionTestDownloadDelegate *delegate =
  443. [[FPRNSURLSessionTestDownloadDelegate alloc] init];
  444. NSURLSessionConfiguration *configuration =
  445. [NSURLSessionConfiguration defaultSessionConfiguration];
  446. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  447. delegate:delegate
  448. delegateQueue:nil];
  449. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  450. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  451. [downloadTask resume];
  452. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
  453. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  454. XCTAssertTrue(delegate.URLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytesCalled);
  455. [instrument deregisterInstrumentors];
  456. }
  457. /** Tests that the called delegate selector is wrapped and calls through. */
  458. - (void)testDelegateURLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesExpectedToWrite {
  459. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  460. [instrument registerInstrumentors];
  461. FPRNSURLSessionCompleteTestDelegate *delegate =
  462. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  463. NSURLSessionConfiguration *configuration =
  464. [NSURLSessionConfiguration defaultSessionConfiguration];
  465. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  466. delegate:delegate
  467. delegateQueue:nil];
  468. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  469. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  470. [downloadTask resume];
  471. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  472. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  473. GCDWebServerResponse *_Nonnull response) {
  474. XCTAssertTrue(delegate.URLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesCalled);
  475. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
  476. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  477. }];
  478. [instrument deregisterInstrumentors];
  479. }
  480. /** Tests that even if a delegate doesn't implement a method, we add it to the delegate class. */
  481. - (void)testDelegateUnimplementedURLSessionTaskDidCompleteWithError {
  482. FPRNSURLSessionTestDelegate *delegate = [[FPRNSURLSessionTestDelegate alloc] init];
  483. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  484. [instrument registerInstrumentors];
  485. NSURLSessionConfiguration *configuration =
  486. [NSURLSessionConfiguration defaultSessionConfiguration];
  487. XCTAssertFalse([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  488. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  489. delegate:delegate
  490. delegateQueue:nil];
  491. XCTAssertTrue([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  492. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:self.testServer.serverURL];
  493. [downloadTask resume];
  494. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  495. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  496. GCDWebServerResponse *_Nonnull response) {
  497. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  498. }];
  499. [instrument deregisterInstrumentors];
  500. }
  501. #pragma mark - Testing instance method wrapping
  502. /** Tests that dataTaskWithRequest: returns a non-nil object. */
  503. - (void)testDataTaskWithRequest {
  504. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  505. [instrument registerInstrumentors];
  506. NSURLSession *session = [NSURLSession sharedSession];
  507. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  508. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  509. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
  510. XCTAssertNotNil(dataTask);
  511. [dataTask resume];
  512. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  513. [instrument deregisterInstrumentors];
  514. }
  515. /** Tests that dataTaskWithRequest:completionHandler: returns a non-nil object. */
  516. - (void)testDataTaskWithRequestAndCompletionHandler {
  517. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  518. [instrument registerInstrumentors];
  519. NSURLSession *session = [NSURLSession sharedSession];
  520. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  521. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  522. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  523. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  524. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  525. [expectation fulfill];
  526. };
  527. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
  528. completionHandler:completionHandler];
  529. XCTAssertNotNil(dataTask);
  530. [dataTask resume];
  531. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  532. [instrument deregisterInstrumentors];
  533. }
  534. /** Tests that dataTaskWithUrl:completionHandler: returns a non-nil object. */
  535. - (void)testDataTaskWithUrlAndCompletionHandler {
  536. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  537. [instrument registerInstrumentors];
  538. NSURLSession *session = [NSURLSession sharedSession];
  539. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  540. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  541. NSURLSessionDataTask *dataTask = nil;
  542. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  543. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  544. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  545. [expectation fulfill];
  546. };
  547. dataTask = [session dataTaskWithURL:URL completionHandler:completionHandler];
  548. XCTAssertNotNil(dataTask);
  549. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  550. [dataTask resume];
  551. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  552. [instrument deregisterInstrumentors];
  553. }
  554. /** Tests that uploadTaskWithRequest:fromFile: returns a non-nil object. */
  555. - (void)testUploadTaskWithRequestFromFile {
  556. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  557. [instrument registerInstrumentors];
  558. NSURLSession *session = [NSURLSession sharedSession];
  559. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  560. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  561. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  562. withExtension:@""];
  563. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileURL];
  564. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  565. XCTAssertNotNil(uploadTask);
  566. [uploadTask resume];
  567. [instrument deregisterInstrumentors];
  568. }
  569. /** Tests that uploadTaskWithRequest:fromFile:completionHandler returns a non-nil object. */
  570. - (void)testUploadTaskWithRequestFromFileCompletionHandler {
  571. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  572. [instrument registerInstrumentors];
  573. NSURLSession *session = [NSURLSession sharedSession];
  574. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  575. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  576. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  577. withExtension:@""];
  578. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  579. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  580. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  581. [expectation fulfill];
  582. };
  583. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  584. fromFile:fileURL
  585. completionHandler:completionHandler];
  586. XCTAssertNotNil(uploadTask);
  587. [uploadTask resume];
  588. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  589. [instrument deregisterInstrumentors];
  590. }
  591. /** Tests that uploadTaskWithRequest:fromData: returns a non-nil object. */
  592. - (void)testUploadTaskWithRequestFromData {
  593. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  594. [instrument registerInstrumentors];
  595. NSURLSession *session = [NSURLSession sharedSession];
  596. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  597. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  598. request.HTTPMethod = @"POST";
  599. NSData *data = [[NSData alloc] init];
  600. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data];
  601. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  602. XCTAssertNotNil(uploadTask);
  603. [uploadTask resume];
  604. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  605. GCDWebServerResponse *_Nonnull response) {
  606. XCTAssertEqual(response.statusCode, 200);
  607. [instrument deregisterInstrumentors];
  608. }];
  609. }
  610. /** Tests that uploadTaskWithRequest:fromData:completionHandler: returns a non-nil object. */
  611. - (void)testUploadTaskWithRequestFromDataCompletionHandler {
  612. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  613. [instrument registerInstrumentors];
  614. NSURLSession *session = [NSURLSession sharedSession];
  615. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  616. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  617. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  618. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  619. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  620. [expectation fulfill];
  621. };
  622. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  623. fromData:[[NSData alloc] init]
  624. completionHandler:completionHandler];
  625. XCTAssertNotNil(uploadTask);
  626. [uploadTask resume];
  627. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  628. [instrument deregisterInstrumentors];
  629. }
  630. /** Tests that uploadTaskWithStreamedRequest: returns a non-nil object. */
  631. - (void)testUploadTaskWithStreamedRequest {
  632. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  633. [instrument registerInstrumentors];
  634. NSURLSession *session = [NSURLSession sharedSession];
  635. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  636. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  637. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithStreamedRequest:request];
  638. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  639. XCTAssertNotNil(uploadTask);
  640. [uploadTask resume];
  641. [instrument deregisterInstrumentors];
  642. }
  643. /** Tests that downloadTaskWithRequest: returns a non-nil object. */
  644. - (void)testDownloadTaskWithRequest {
  645. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  646. [instrument registerInstrumentors];
  647. NSURLSession *session = [NSURLSession sharedSession];
  648. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  649. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  650. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
  651. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  652. XCTAssertNotNil(downloadTask);
  653. [downloadTask resume];
  654. [instrument deregisterInstrumentors];
  655. }
  656. /** Tests that downloadTaskWithRequest:completionHandler: returns a non-nil object. */
  657. - (void)testDownloadTaskWithRequestCompletionHandler {
  658. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  659. [instrument registerInstrumentors];
  660. NSURLSession *session = [NSURLSession sharedSession];
  661. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  662. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  663. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  664. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  665. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  666. [expectation fulfill];
  667. };
  668. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
  669. completionHandler:completionHandler];
  670. XCTAssertNotNil(downloadTask);
  671. [downloadTask resume];
  672. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  673. [instrument deregisterInstrumentors];
  674. }
  675. /** Tests that downloadTaskWithUrl:completionHandler: returns a non-nil object. */
  676. - (void)testDownloadTaskWithURLCompletionHandler {
  677. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  678. [instrument registerInstrumentors];
  679. NSURLSession *session = [NSURLSession sharedSession];
  680. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  681. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  682. NSURLSessionDownloadTask *downloadTask = nil;
  683. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  684. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  685. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  686. [expectation fulfill];
  687. };
  688. downloadTask = [session downloadTaskWithURL:URL completionHandler:completionHandler];
  689. XCTAssertNotNil(downloadTask);
  690. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  691. [downloadTask resume];
  692. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  693. [instrument deregisterInstrumentors];
  694. }
  695. /** Validate that it works with NSMutableURLRequest URLs across data, upload, and download. */
  696. - (void)testMutableRequestURLs {
  697. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  698. [instrument registerInstrumentors];
  699. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  700. NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
  701. NSURLSession *session = [NSURLSession
  702. sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  703. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:URLRequest];
  704. [dataTask resume];
  705. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  706. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  707. GCDWebServerResponse *_Nonnull response) {
  708. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  709. }];
  710. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:URLRequest];
  711. [downloadTask resume];
  712. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  713. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  714. GCDWebServerResponse *_Nonnull response) {
  715. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  716. }];
  717. URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  718. URLRequest.URL = URL;
  719. URLRequest.HTTPMethod = @"POST";
  720. NSData *uploadData = [[NSData alloc] init];
  721. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:URLRequest
  722. fromData:uploadData];
  723. [uploadTask resume];
  724. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  725. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  726. GCDWebServerResponse *_Nonnull response) {
  727. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  728. }];
  729. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  730. withExtension:@""];
  731. uploadTask = [session uploadTaskWithRequest:URLRequest fromFile:fileURL];
  732. [uploadTask resume];
  733. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  734. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  735. GCDWebServerResponse *_Nonnull response) {
  736. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  737. }];
  738. [instrument deregisterInstrumentors];
  739. }
  740. @end