FPRNSURLSessionInstrumentTest.m 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  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 =
  202. imp_implementationWithBlock(^(id session, NSURLSessionConfiguration *configuration) {
  203. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *);
  204. NSURLSession *originalSession = ((OriginalImp)originalImp)(
  205. 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. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  355. GCDWebServerResponse *_Nonnull response) {
  356. XCTAssertTrue(delegate.URLSessionTaskDidSendBodyDataTotalBytesSentTotalBytesExpectedCalled);
  357. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  358. }];
  359. [instrument deregisterInstrumentors];
  360. }
  361. /** Tests that the called delegate selector is wrapped and calls through. */
  362. - (void)testDelegateURLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandler {
  363. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  364. [instrument registerInstrumentors];
  365. FPRNSURLSessionCompleteTestDelegate *delegate =
  366. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  367. NSURLSessionConfiguration *configuration =
  368. [NSURLSessionConfiguration defaultSessionConfiguration];
  369. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testRedirect"];
  370. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  371. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  372. delegate:delegate
  373. delegateQueue:nil];
  374. NSURLSessionTask *task = [session dataTaskWithRequest:request];
  375. [task resume];
  376. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  377. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  378. GCDWebServerResponse *_Nonnull response) {
  379. XCTAssertTrue(
  380. delegate.URLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandlerCalled);
  381. }];
  382. [instrument deregisterInstrumentors];
  383. }
  384. /** Tests that the called delegate selector is wrapped and calls through. */
  385. - (void)testDelegateURLSessionDataTaskDidReceiveData {
  386. FPRNSURLSessionInstrument *instrument;
  387. NSURLSessionDataTask *dataTask;
  388. @autoreleasepool {
  389. instrument = [[FPRNSURLSessionInstrument alloc] init];
  390. [instrument registerInstrumentors];
  391. FPRNSURLSessionCompleteTestDelegate *delegate =
  392. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  393. NSURLSessionConfiguration *configuration =
  394. [NSURLSessionConfiguration defaultSessionConfiguration];
  395. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  396. delegate:delegate
  397. delegateQueue:nil];
  398. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  399. dataTask = [session dataTaskWithURL:URL];
  400. [dataTask resume];
  401. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  402. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  403. GCDWebServerResponse *_Nonnull response) {
  404. XCTAssertTrue(delegate.URLSessionDataTaskDidReceiveDataCalled);
  405. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  406. }];
  407. }
  408. [instrument deregisterInstrumentors];
  409. }
  410. /** Tests that the called delegate selector is wrapped and calls through. */
  411. - (void)testDelegateURLSessionDownloadTaskDidFinishDownloadingToURL {
  412. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  413. [instrument registerInstrumentors];
  414. FPRNSURLSessionCompleteTestDelegate *delegate =
  415. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  416. NSURLSessionConfiguration *configuration =
  417. [NSURLSessionConfiguration defaultSessionConfiguration];
  418. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  419. delegate:delegate
  420. delegateQueue:nil];
  421. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  422. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  423. [downloadTask resume];
  424. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  425. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  426. GCDWebServerResponse *_Nonnull response) {
  427. XCTAssertTrue(delegate.URLSessionDownloadTaskDidFinishDownloadingToURLCalled);
  428. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  429. }];
  430. [instrument deregisterInstrumentors];
  431. }
  432. /** Tests that the called delegate selector is wrapped and calls through. */
  433. - (void)testDelegateURLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytes {
  434. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  435. [instrument registerInstrumentors];
  436. FPRNSURLSessionTestDownloadDelegate *delegate =
  437. [[FPRNSURLSessionTestDownloadDelegate alloc] init];
  438. NSURLSessionConfiguration *configuration =
  439. [NSURLSessionConfiguration defaultSessionConfiguration];
  440. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  441. delegate:delegate
  442. delegateQueue:nil];
  443. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  444. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  445. [downloadTask resume];
  446. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
  447. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  448. XCTAssertTrue(delegate.URLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytesCalled);
  449. [instrument deregisterInstrumentors];
  450. }
  451. /** Tests that the called delegate selector is wrapped and calls through. */
  452. - (void)testDelegateURLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesExpectedToWrite {
  453. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  454. [instrument registerInstrumentors];
  455. FPRNSURLSessionCompleteTestDelegate *delegate =
  456. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  457. NSURLSessionConfiguration *configuration =
  458. [NSURLSessionConfiguration defaultSessionConfiguration];
  459. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  460. delegate:delegate
  461. delegateQueue:nil];
  462. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  463. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  464. [downloadTask resume];
  465. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  466. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  467. GCDWebServerResponse *_Nonnull response) {
  468. XCTAssertTrue(delegate.URLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesCalled);
  469. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
  470. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  471. }];
  472. [instrument deregisterInstrumentors];
  473. }
  474. /** Tests that even if a delegate doesn't implement a method, we add it to the delegate class. */
  475. - (void)testDelegateUnimplementedURLSessionTaskDidCompleteWithError {
  476. FPRNSURLSessionTestDelegate *delegate = [[FPRNSURLSessionTestDelegate alloc] init];
  477. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  478. [instrument registerInstrumentors];
  479. NSURLSessionConfiguration *configuration =
  480. [NSURLSessionConfiguration defaultSessionConfiguration];
  481. XCTAssertFalse([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  482. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  483. delegate:delegate
  484. delegateQueue:nil];
  485. XCTAssertTrue([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  486. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:self.testServer.serverURL];
  487. [downloadTask resume];
  488. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  489. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  490. GCDWebServerResponse *_Nonnull response) {
  491. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  492. }];
  493. [instrument deregisterInstrumentors];
  494. }
  495. #pragma mark - Testing instance method wrapping
  496. /** Tests that dataTaskWithRequest: returns a non-nil object. */
  497. - (void)testDataTaskWithRequest {
  498. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  499. [instrument registerInstrumentors];
  500. NSURLSession *session = [NSURLSession sharedSession];
  501. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  502. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  503. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
  504. XCTAssertNotNil(dataTask);
  505. [dataTask resume];
  506. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  507. [instrument deregisterInstrumentors];
  508. }
  509. /** Tests that dataTaskWithRequest:completionHandler: returns a non-nil object. */
  510. - (void)testDataTaskWithRequestAndCompletionHandler {
  511. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  512. [instrument registerInstrumentors];
  513. NSURLSession *session = [NSURLSession sharedSession];
  514. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  515. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  516. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  517. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  518. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  519. [expectation fulfill];
  520. };
  521. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
  522. completionHandler:completionHandler];
  523. XCTAssertNotNil(dataTask);
  524. [dataTask resume];
  525. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  526. [instrument deregisterInstrumentors];
  527. }
  528. /** Tests that dataTaskWithUrl:completionHandler: returns a non-nil object. */
  529. - (void)testDataTaskWithUrlAndCompletionHandler {
  530. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  531. [instrument registerInstrumentors];
  532. NSURLSession *session = [NSURLSession sharedSession];
  533. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  534. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  535. NSURLSessionDataTask *dataTask = nil;
  536. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  537. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  538. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  539. [expectation fulfill];
  540. };
  541. dataTask = [session dataTaskWithURL:URL completionHandler:completionHandler];
  542. XCTAssertNotNil(dataTask);
  543. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  544. [dataTask resume];
  545. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  546. [instrument deregisterInstrumentors];
  547. }
  548. /** Tests that uploadTaskWithRequest:fromFile: returns a non-nil object. */
  549. - (void)testUploadTaskWithRequestFromFile {
  550. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  551. [instrument registerInstrumentors];
  552. NSURLSession *session = [NSURLSession sharedSession];
  553. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  554. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  555. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  556. withExtension:@""];
  557. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileURL];
  558. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  559. XCTAssertNotNil(uploadTask);
  560. [uploadTask resume];
  561. [instrument deregisterInstrumentors];
  562. }
  563. /** Tests that uploadTaskWithRequest:fromFile:completionHandler returns a non-nil object. */
  564. - (void)testUploadTaskWithRequestFromFileCompletionHandler {
  565. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  566. [instrument registerInstrumentors];
  567. NSURLSession *session = [NSURLSession sharedSession];
  568. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  569. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  570. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  571. withExtension:@""];
  572. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  573. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  574. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  575. [expectation fulfill];
  576. };
  577. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  578. fromFile:fileURL
  579. completionHandler:completionHandler];
  580. XCTAssertNotNil(uploadTask);
  581. [uploadTask resume];
  582. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  583. [instrument deregisterInstrumentors];
  584. }
  585. /** Tests that uploadTaskWithRequest:fromData: returns a non-nil object. */
  586. - (void)testUploadTaskWithRequestFromData {
  587. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  588. [instrument registerInstrumentors];
  589. NSURLSession *session = [NSURLSession sharedSession];
  590. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  591. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  592. request.HTTPMethod = @"POST";
  593. NSData *data = [[NSData alloc] init];
  594. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data];
  595. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  596. XCTAssertNotNil(uploadTask);
  597. [uploadTask resume];
  598. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  599. GCDWebServerResponse *_Nonnull response) {
  600. XCTAssertEqual(response.statusCode, 200);
  601. [instrument deregisterInstrumentors];
  602. }];
  603. }
  604. /** Tests that uploadTaskWithRequest:fromData:completionHandler: returns a non-nil object. */
  605. - (void)testUploadTaskWithRequestFromDataCompletionHandler {
  606. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  607. [instrument registerInstrumentors];
  608. NSURLSession *session = [NSURLSession sharedSession];
  609. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  610. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  611. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  612. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  613. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  614. [expectation fulfill];
  615. };
  616. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  617. fromData:[[NSData alloc] init]
  618. completionHandler:completionHandler];
  619. XCTAssertNotNil(uploadTask);
  620. [uploadTask resume];
  621. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  622. [instrument deregisterInstrumentors];
  623. }
  624. /** Tests that uploadTaskWithStreamedRequest: returns a non-nil object. */
  625. - (void)testUploadTaskWithStreamedRequest {
  626. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  627. [instrument registerInstrumentors];
  628. NSURLSession *session = [NSURLSession sharedSession];
  629. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  630. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  631. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithStreamedRequest:request];
  632. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  633. XCTAssertNotNil(uploadTask);
  634. [uploadTask resume];
  635. [instrument deregisterInstrumentors];
  636. }
  637. /** Tests that downloadTaskWithRequest: returns a non-nil object. */
  638. - (void)testDownloadTaskWithRequest {
  639. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  640. [instrument registerInstrumentors];
  641. NSURLSession *session = [NSURLSession sharedSession];
  642. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  643. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  644. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
  645. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  646. XCTAssertNotNil(downloadTask);
  647. [downloadTask resume];
  648. [instrument deregisterInstrumentors];
  649. }
  650. /** Tests that downloadTaskWithRequest:completionHandler: returns a non-nil object. */
  651. - (void)testDownloadTaskWithRequestCompletionHandler {
  652. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  653. [instrument registerInstrumentors];
  654. NSURLSession *session = [NSURLSession sharedSession];
  655. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  656. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  657. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  658. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  659. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  660. [expectation fulfill];
  661. };
  662. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
  663. completionHandler:completionHandler];
  664. XCTAssertNotNil(downloadTask);
  665. [downloadTask resume];
  666. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  667. [instrument deregisterInstrumentors];
  668. }
  669. /** Tests that downloadTaskWithUrl:completionHandler: returns a non-nil object. */
  670. - (void)testDownloadTaskWithURLCompletionHandler {
  671. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  672. [instrument registerInstrumentors];
  673. NSURLSession *session = [NSURLSession sharedSession];
  674. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  675. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  676. NSURLSessionDownloadTask *downloadTask = nil;
  677. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  678. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  679. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  680. [expectation fulfill];
  681. };
  682. downloadTask = [session downloadTaskWithURL:URL completionHandler:completionHandler];
  683. XCTAssertNotNil(downloadTask);
  684. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  685. [downloadTask resume];
  686. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  687. [instrument deregisterInstrumentors];
  688. }
  689. /** Validate that it works with NSMutableURLRequest URLs across data, upload, and download. */
  690. - (void)testMutableRequestURLs {
  691. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  692. [instrument registerInstrumentors];
  693. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  694. NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
  695. NSURLSession *session = [NSURLSession
  696. sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  697. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:URLRequest];
  698. [dataTask resume];
  699. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  700. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  701. GCDWebServerResponse *_Nonnull response) {
  702. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  703. }];
  704. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:URLRequest];
  705. [downloadTask resume];
  706. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  707. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  708. GCDWebServerResponse *_Nonnull response) {
  709. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  710. }];
  711. URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  712. URLRequest.URL = URL;
  713. URLRequest.HTTPMethod = @"POST";
  714. NSData *uploadData = [[NSData alloc] init];
  715. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:URLRequest
  716. fromData:uploadData];
  717. [uploadTask resume];
  718. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  719. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  720. GCDWebServerResponse *_Nonnull response) {
  721. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  722. }];
  723. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  724. withExtension:@""];
  725. uploadTask = [session uploadTaskWithRequest:URLRequest fromFile:fileURL];
  726. [uploadTask resume];
  727. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  728. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  729. GCDWebServerResponse *_Nonnull response) {
  730. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  731. }];
  732. [instrument deregisterInstrumentors];
  733. }
  734. @end