FPRNSURLSessionInstrumentTest.m 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  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. __unused __weak id weakSelf;
  100. self.testServer.responseCompletedBlock =
  101. ^(GCDWebServerRequest *_Nonnull request, GCDWebServerResponse *_Nonnull response) {
  102. id self = weakSelf;
  103. XCTAssert(instrument);
  104. XCTAssert(instrument.delegateInstrument);
  105. };
  106. [instrument deregisterInstrumentors];
  107. }
  108. /** Tests that creating a shared session returns a non-nil object. */
  109. - (void)testSharedSession {
  110. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  111. [instrument registerInstrumentors];
  112. NSURLSession *session = [NSURLSession sharedSession];
  113. XCTAssertNotNil(session);
  114. [instrument deregisterInstrumentors];
  115. }
  116. /** Tests that a method that returns an NSURLSession subclass registers that subclass. */
  117. - (void)testSubclassRegistration {
  118. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  119. [instrument registerInstrumentors];
  120. NSURLSession *session = [NSURLSession sharedSession];
  121. XCTAssertNotNil(session);
  122. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  123. [instrument deregisterInstrumentors];
  124. }
  125. /** Tests that a discovered subclass isn't registered more than once. */
  126. - (void)testSubclassIsNotRegisteredMoreThanOnce {
  127. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  128. [instrument registerInstrumentors];
  129. NSURLSession *session = [NSURLSession sharedSession];
  130. NSURLSession *session2 = [NSURLSession sharedSession];
  131. XCTAssertNotNil(session);
  132. XCTAssertNotNil(session2);
  133. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  134. [instrument deregisterInstrumentors];
  135. }
  136. /** Tests sessionWithConfiguration: with the default configurtion returns a non-nil object. */
  137. - (void)testSessionWithDefaultSessionConfiguration {
  138. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  139. [instrument registerInstrumentors];
  140. NSURLSessionConfiguration *configuration =
  141. [NSURLSessionConfiguration defaultSessionConfiguration];
  142. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  143. XCTAssertNotNil(session);
  144. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  145. [instrument deregisterInstrumentors];
  146. }
  147. /** Tests sessionWithConfiguration: with an ephemeral configuration returns a non-nil object. */
  148. - (void)testSessionWithEphemeralSessionConfiguration {
  149. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  150. [instrument registerInstrumentors];
  151. NSURLSessionConfiguration *configuration =
  152. [NSURLSessionConfiguration ephemeralSessionConfiguration];
  153. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  154. XCTAssertNotNil(session);
  155. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  156. [instrument deregisterInstrumentors];
  157. }
  158. /** Tests sessionWithConfiguration: with a background configuration returns a non-nil object. */
  159. - (void)testSessionWithBackgroundSessionConfiguration {
  160. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  161. [instrument registerInstrumentors];
  162. NSURLSessionConfiguration *configuration =
  163. [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"madeUpID"];
  164. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
  165. XCTAssertNotNil(session);
  166. XCTAssertEqual(instrument.classInstrumentors.count, 2);
  167. [instrument deregisterInstrumentors];
  168. }
  169. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  170. - (void)testProxyWrappedSharedSession {
  171. Method method = class_getClassMethod([NSURLSession class], @selector(sharedSession));
  172. IMP originalImp = method_getImplementation(method);
  173. IMP swizzledImp = imp_implementationWithBlock(^(id session) {
  174. typedef NSURLSession *(*OriginalImp)(id, SEL);
  175. NSURLSession *originalSession = ((OriginalImp)originalImp)(session, @selector(sharedSession));
  176. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  177. });
  178. method_setImplementation(method, swizzledImp);
  179. XCTAssertEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  180. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  181. [instrument registerInstrumentors];
  182. NSURLSession *session;
  183. XCTAssertNoThrow(session = [NSURLSession sharedSession]);
  184. NSURL *url = self.testServer.serverURL;
  185. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  186. NSURLSessionDownloadTask *task =
  187. [session downloadTaskWithURL:url
  188. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  189. NSError *_Nullable error) {
  190. [expectation fulfill];
  191. }];
  192. [task resume];
  193. XCTAssertNotNil(task);
  194. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  195. [instrument deregisterInstrumentors];
  196. method_setImplementation(method, originalImp);
  197. XCTAssertNotEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  198. }
  199. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  200. - (void)testProxyWrappedSessionWithConfiguration {
  201. Method method = class_getClassMethod([NSURLSession class], @selector(sessionWithConfiguration:));
  202. IMP originalImp = method_getImplementation(method);
  203. IMP swizzledImp =
  204. imp_implementationWithBlock(^(id session, NSURLSessionConfiguration *configuration) {
  205. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *);
  206. NSURLSession *originalSession = ((OriginalImp)originalImp)(
  207. session, @selector(sessionWithConfiguration:), configuration);
  208. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  209. });
  210. method_setImplementation(method, swizzledImp);
  211. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  212. XCTAssertEqual([[NSURLSession sessionWithConfiguration:config] class],
  213. [FPRNSURLSessionProxy class]);
  214. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  215. [instrument registerInstrumentors];
  216. NSURLSession *session;
  217. XCTAssertNoThrow(session = [NSURLSession sessionWithConfiguration:config]);
  218. NSURL *url = self.testServer.serverURL;
  219. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  220. NSURLSessionDownloadTask *task =
  221. [session downloadTaskWithURL:url
  222. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  223. NSError *_Nullable error) {
  224. [expectation fulfill];
  225. }];
  226. XCTAssertNotNil(task);
  227. [task resume];
  228. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  229. [instrument deregisterInstrumentors];
  230. method_setImplementation(method, originalImp);
  231. XCTAssertNotEqual([[NSURLSession sharedSession] class], [FPRNSURLSessionProxy class]);
  232. }
  233. /** Tests instrumenting an NSProxy wrapped NSURLSession object works. */
  234. - (void)testProxyWrappedSessionWithConfigurationDelegateDelegateQueue {
  235. SEL selector = @selector(sessionWithConfiguration:delegate:delegateQueue:);
  236. Method method = class_getClassMethod([NSURLSession class], selector);
  237. IMP originalImp = method_getImplementation(method);
  238. IMP swizzledImp = imp_implementationWithBlock(
  239. ^(id session, NSURLSessionConfiguration *configuration, id<NSURLSessionDelegate> *delegate,
  240. NSOperationQueue *delegateQueue) {
  241. typedef NSURLSession *(*OriginalImp)(id, SEL, NSURLSessionConfiguration *,
  242. id<NSURLSessionDelegate> *, NSOperationQueue *);
  243. NSURLSession *originalSession =
  244. ((OriginalImp)originalImp)(session, selector, configuration, delegate, delegateQueue);
  245. return [[FPRNSURLSessionProxy alloc] initWithSession:originalSession];
  246. });
  247. method_setImplementation(method, swizzledImp);
  248. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  249. XCTAssertEqual([[NSURLSession sessionWithConfiguration:config delegate:nil
  250. delegateQueue:nil] class],
  251. [FPRNSURLSessionProxy class]);
  252. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  253. [instrument registerInstrumentors];
  254. NSURLSession *session;
  255. XCTAssertNoThrow(session = [NSURLSession sessionWithConfiguration:config
  256. delegate:nil
  257. delegateQueue:nil]);
  258. NSURL *url = self.testServer.serverURL;
  259. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler"];
  260. NSURLSessionDownloadTask *task =
  261. [session downloadTaskWithURL:url
  262. completionHandler:^(NSURL *_Nullable location, NSURLResponse *_Nullable response,
  263. NSError *_Nullable error) {
  264. [expectation fulfill];
  265. }];
  266. XCTAssertNotNil(task);
  267. [task resume];
  268. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  269. [instrument deregisterInstrumentors];
  270. method_setImplementation(method, originalImp);
  271. }
  272. #pragma mark - Testing delegate method wrapping
  273. /** Tests using a nil delegate still results in tracking responses. */
  274. - (void)testSessionWithConfigurationDelegateDelegateQueueWithNilDelegate {
  275. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  276. [instrument registerInstrumentors];
  277. NSURLSessionConfiguration *configuration =
  278. [NSURLSessionConfiguration defaultSessionConfiguration];
  279. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  280. delegate:nil
  281. delegateQueue:nil];
  282. NSURLRequest *request = [NSURLRequest requestWithURL:self.testServer.serverURL];
  283. NSURLSessionTask *task;
  284. @autoreleasepool {
  285. task = [session dataTaskWithRequest:request];
  286. XCTAssertNotNil(task);
  287. [task resume];
  288. XCTAssertNotNil(session.delegate); // Not sure this is the desired behavior.
  289. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  290. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  291. GCDWebServerResponse *_Nonnull response) {
  292. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:task]);
  293. }];
  294. }
  295. [instrument deregisterInstrumentors];
  296. }
  297. /** Tests that the delegate class isn't instrumented more than once. */
  298. - (void)testDelegateClassOnlyRegisteredOnce {
  299. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  300. [instrument registerInstrumentors];
  301. FPRNSURLSessionCompleteTestDelegate *delegate =
  302. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  303. NSURLSessionConfiguration *configuration =
  304. [NSURLSessionConfiguration defaultSessionConfiguration];
  305. [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
  306. [NSURLSession sessionWithConfiguration:configuration delegate:delegate delegateQueue:nil];
  307. XCTAssertEqual(instrument.delegateInstrument.classInstrumentors.count, 1);
  308. XCTAssertEqual(instrument.delegateInstrument.instrumentedClasses.count, 1);
  309. [instrument deregisterInstrumentors];
  310. }
  311. /** Tests that the called delegate selector is wrapped and calls through. */
  312. - (void)testDelegateURLSessionTaskDidCompleteWithError {
  313. [self.testServer stop];
  314. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  315. [instrument registerInstrumentors];
  316. FPRNSURLSessionCompleteTestDelegate *delegate =
  317. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  318. // This request needs to fail.
  319. NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://nonurl"]];
  320. NSURLSessionConfiguration *configuration =
  321. [NSURLSessionConfiguration defaultSessionConfiguration];
  322. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  323. delegate:delegate
  324. delegateQueue:nil];
  325. NSURLSessionTask *task;
  326. @autoreleasepool {
  327. task = [session dataTaskWithRequest:request];
  328. [task resume];
  329. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  330. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
  331. }
  332. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:task]);
  333. XCTAssertTrue(delegate.URLSessionTaskDidCompleteWithErrorCalled);
  334. [instrument deregisterInstrumentors];
  335. [self.testServer start];
  336. }
  337. /** Tests that the called delegate selector is wrapped and calls through. */
  338. - (void)testDelegateURLSessionTaskDidSendBodyDataTotalBytesSentTotalBytesExpectedToSend {
  339. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  340. [instrument registerInstrumentors];
  341. FPRNSURLSessionCompleteTestDelegate *delegate =
  342. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  343. NSURLSessionConfiguration *configuration =
  344. [NSURLSessionConfiguration defaultSessionConfiguration];
  345. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  346. delegate:delegate
  347. delegateQueue:nil];
  348. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  349. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  350. request.HTTPMethod = @"POST";
  351. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  352. withExtension:@""];
  353. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileURL];
  354. [uploadTask resume];
  355. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  356. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  357. GCDWebServerResponse *_Nonnull response) {
  358. XCTAssertTrue(delegate.URLSessionTaskDidSendBodyDataTotalBytesSentTotalBytesExpectedCalled);
  359. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  360. }];
  361. [instrument deregisterInstrumentors];
  362. }
  363. /** Tests that the called delegate selector is wrapped and calls through. */
  364. - (void)testDelegateURLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandler {
  365. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  366. [instrument registerInstrumentors];
  367. FPRNSURLSessionCompleteTestDelegate *delegate =
  368. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  369. NSURLSessionConfiguration *configuration =
  370. [NSURLSessionConfiguration defaultSessionConfiguration];
  371. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testRedirect"];
  372. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  373. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  374. delegate:delegate
  375. delegateQueue:nil];
  376. NSURLSessionTask *task = [session dataTaskWithRequest:request];
  377. [task resume];
  378. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:task]);
  379. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  380. GCDWebServerResponse *_Nonnull response) {
  381. XCTAssertTrue(
  382. delegate.URLSessionTaskWillPerformHTTPRedirectionNewRequestCompletionHandlerCalled);
  383. }];
  384. [instrument deregisterInstrumentors];
  385. }
  386. /** Tests that the called delegate selector is wrapped and calls through. */
  387. - (void)testDelegateURLSessionDataTaskDidReceiveData {
  388. FPRNSURLSessionInstrument *instrument;
  389. NSURLSessionDataTask *dataTask;
  390. @autoreleasepool {
  391. instrument = [[FPRNSURLSessionInstrument alloc] init];
  392. [instrument registerInstrumentors];
  393. FPRNSURLSessionCompleteTestDelegate *delegate =
  394. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  395. NSURLSessionConfiguration *configuration =
  396. [NSURLSessionConfiguration defaultSessionConfiguration];
  397. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  398. delegate:delegate
  399. delegateQueue:nil];
  400. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  401. dataTask = [session dataTaskWithURL:URL];
  402. [dataTask resume];
  403. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  404. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  405. GCDWebServerResponse *_Nonnull response) {
  406. XCTAssertTrue(delegate.URLSessionDataTaskDidReceiveDataCalled);
  407. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  408. }];
  409. }
  410. [instrument deregisterInstrumentors];
  411. }
  412. /** Tests that the called delegate selector is wrapped and calls through. */
  413. - (void)testDelegateURLSessionDownloadTaskDidFinishDownloadingToURL {
  414. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  415. [instrument registerInstrumentors];
  416. FPRNSURLSessionCompleteTestDelegate *delegate =
  417. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  418. NSURLSessionConfiguration *configuration =
  419. [NSURLSessionConfiguration defaultSessionConfiguration];
  420. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  421. delegate:delegate
  422. delegateQueue:nil];
  423. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  424. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  425. [downloadTask resume];
  426. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  427. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  428. GCDWebServerResponse *_Nonnull response) {
  429. XCTAssertTrue(delegate.URLSessionDownloadTaskDidFinishDownloadingToURLCalled);
  430. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  431. }];
  432. [instrument deregisterInstrumentors];
  433. }
  434. /** Tests that the called delegate selector is wrapped and calls through. */
  435. - (void)testDelegateURLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytes {
  436. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  437. [instrument registerInstrumentors];
  438. FPRNSURLSessionTestDownloadDelegate *delegate =
  439. [[FPRNSURLSessionTestDownloadDelegate alloc] init];
  440. NSURLSessionConfiguration *configuration =
  441. [NSURLSessionConfiguration defaultSessionConfiguration];
  442. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  443. delegate:delegate
  444. delegateQueue:nil];
  445. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  446. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  447. [downloadTask resume];
  448. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
  449. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  450. XCTAssertTrue(delegate.URLSessionDownloadTaskDidResumeAtOffsetExpectedTotalBytesCalled);
  451. [instrument deregisterInstrumentors];
  452. }
  453. /** Tests that the called delegate selector is wrapped and calls through. */
  454. - (void)testDelegateURLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesExpectedToWrite {
  455. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  456. [instrument registerInstrumentors];
  457. FPRNSURLSessionCompleteTestDelegate *delegate =
  458. [[FPRNSURLSessionCompleteTestDelegate alloc] init];
  459. NSURLSessionConfiguration *configuration =
  460. [NSURLSessionConfiguration defaultSessionConfiguration];
  461. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  462. delegate:delegate
  463. delegateQueue:nil];
  464. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testBigDownload"];
  465. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:URL];
  466. [downloadTask resume];
  467. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  468. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  469. GCDWebServerResponse *_Nonnull response) {
  470. XCTAssertTrue(delegate.URLSessionDownloadTaskDidWriteDataTotalBytesWrittenTotalBytesCalled);
  471. [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
  472. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  473. }];
  474. [instrument deregisterInstrumentors];
  475. }
  476. /** Tests that even if a delegate doesn't implement a method, we add it to the delegate class. */
  477. - (void)testDelegateUnimplementedURLSessionTaskDidCompleteWithError {
  478. FPRNSURLSessionTestDelegate *delegate = [[FPRNSURLSessionTestDelegate alloc] init];
  479. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  480. [instrument registerInstrumentors];
  481. NSURLSessionConfiguration *configuration =
  482. [NSURLSessionConfiguration defaultSessionConfiguration];
  483. XCTAssertFalse([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  484. NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
  485. delegate:delegate
  486. delegateQueue:nil];
  487. XCTAssertTrue([delegate respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]);
  488. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:self.testServer.serverURL];
  489. [downloadTask resume];
  490. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  491. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  492. GCDWebServerResponse *_Nonnull response) {
  493. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  494. }];
  495. [instrument deregisterInstrumentors];
  496. }
  497. #pragma mark - Testing instance method wrapping
  498. /** Tests that dataTaskWithRequest: returns a non-nil object. */
  499. - (void)testDataTaskWithRequest {
  500. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  501. [instrument registerInstrumentors];
  502. NSURLSession *session = [NSURLSession sharedSession];
  503. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  504. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  505. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
  506. XCTAssertNotNil(dataTask);
  507. [dataTask resume];
  508. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  509. [instrument deregisterInstrumentors];
  510. }
  511. /** Tests that dataTaskWithRequest:completionHandler: returns a non-nil object. */
  512. - (void)testDataTaskWithRequestAndCompletionHandler {
  513. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  514. [instrument registerInstrumentors];
  515. NSURLSession *session = [NSURLSession sharedSession];
  516. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  517. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  518. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  519. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  520. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  521. [expectation fulfill];
  522. };
  523. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
  524. completionHandler:completionHandler];
  525. XCTAssertNotNil(dataTask);
  526. [dataTask resume];
  527. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  528. [instrument deregisterInstrumentors];
  529. }
  530. /** Tests that dataTaskWithUrl:completionHandler: returns a non-nil object. */
  531. - (void)testDataTaskWithUrlAndCompletionHandler {
  532. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  533. [instrument registerInstrumentors];
  534. NSURLSession *session = [NSURLSession sharedSession];
  535. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  536. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  537. NSURLSessionDataTask *dataTask = nil;
  538. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  539. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  540. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  541. [expectation fulfill];
  542. };
  543. dataTask = [session dataTaskWithURL:URL completionHandler:completionHandler];
  544. XCTAssertNotNil(dataTask);
  545. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  546. [dataTask resume];
  547. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  548. [instrument deregisterInstrumentors];
  549. }
  550. /** Tests that uploadTaskWithRequest:fromFile: returns a non-nil object. */
  551. - (void)testUploadTaskWithRequestFromFile {
  552. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  553. [instrument registerInstrumentors];
  554. NSURLSession *session = [NSURLSession sharedSession];
  555. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  556. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  557. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  558. withExtension:@""];
  559. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:fileURL];
  560. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  561. XCTAssertNotNil(uploadTask);
  562. [uploadTask resume];
  563. [instrument deregisterInstrumentors];
  564. }
  565. /** Tests that uploadTaskWithRequest:fromFile:completionHandler returns a non-nil object. */
  566. - (void)testUploadTaskWithRequestFromFileCompletionHandler {
  567. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  568. [instrument registerInstrumentors];
  569. NSURLSession *session = [NSURLSession sharedSession];
  570. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  571. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  572. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  573. withExtension:@""];
  574. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  575. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  576. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  577. [expectation fulfill];
  578. };
  579. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  580. fromFile:fileURL
  581. completionHandler:completionHandler];
  582. XCTAssertNotNil(uploadTask);
  583. [uploadTask resume];
  584. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  585. [instrument deregisterInstrumentors];
  586. }
  587. /** Tests that uploadTaskWithRequest:fromData: returns a non-nil object. */
  588. - (void)testUploadTaskWithRequestFromData {
  589. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  590. [instrument registerInstrumentors];
  591. NSURLSession *session = [NSURLSession sharedSession];
  592. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  593. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  594. request.HTTPMethod = @"POST";
  595. NSData *data = [[NSData alloc] init];
  596. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data];
  597. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  598. XCTAssertNotNil(uploadTask);
  599. [uploadTask resume];
  600. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  601. GCDWebServerResponse *_Nonnull response) {
  602. XCTAssertEqual(response.statusCode, 200);
  603. [instrument deregisterInstrumentors];
  604. }];
  605. }
  606. /** Tests that uploadTaskWithRequest:fromData:completionHandler: returns a non-nil object. */
  607. - (void)testUploadTaskWithRequestFromDataCompletionHandler {
  608. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  609. [instrument registerInstrumentors];
  610. NSURLSession *session = [NSURLSession sharedSession];
  611. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  612. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  613. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  614. void (^completionHandler)(NSData *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  615. ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  616. [expectation fulfill];
  617. };
  618. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
  619. fromData:[[NSData alloc] init]
  620. completionHandler:completionHandler];
  621. XCTAssertNotNil(uploadTask);
  622. [uploadTask resume];
  623. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  624. [instrument deregisterInstrumentors];
  625. }
  626. /** Tests that uploadTaskWithStreamedRequest: returns a non-nil object. */
  627. - (void)testUploadTaskWithStreamedRequest {
  628. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  629. [instrument registerInstrumentors];
  630. NSURLSession *session = [NSURLSession sharedSession];
  631. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  632. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  633. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithStreamedRequest:request];
  634. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  635. XCTAssertNotNil(uploadTask);
  636. [uploadTask resume];
  637. [instrument deregisterInstrumentors];
  638. }
  639. /** Tests that downloadTaskWithRequest: returns a non-nil object. */
  640. - (void)testDownloadTaskWithRequest {
  641. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  642. [instrument registerInstrumentors];
  643. NSURLSession *session = [NSURLSession sharedSession];
  644. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  645. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  646. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
  647. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  648. XCTAssertNotNil(downloadTask);
  649. [downloadTask resume];
  650. [instrument deregisterInstrumentors];
  651. }
  652. /** Tests that downloadTaskWithRequest:completionHandler: returns a non-nil object. */
  653. - (void)testDownloadTaskWithRequestCompletionHandler {
  654. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  655. [instrument registerInstrumentors];
  656. NSURLSession *session = [NSURLSession sharedSession];
  657. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  658. NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  659. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  660. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  661. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  662. [expectation fulfill];
  663. };
  664. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
  665. completionHandler:completionHandler];
  666. XCTAssertNotNil(downloadTask);
  667. [downloadTask resume];
  668. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  669. [instrument deregisterInstrumentors];
  670. }
  671. /** Tests that downloadTaskWithUrl:completionHandler: returns a non-nil object. */
  672. - (void)testDownloadTaskWithURLCompletionHandler {
  673. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  674. [instrument registerInstrumentors];
  675. NSURLSession *session = [NSURLSession sharedSession];
  676. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"test"];
  677. XCTestExpectation *expectation = [self expectationWithDescription:@"completionHandler called"];
  678. NSURLSessionDownloadTask *downloadTask = nil;
  679. void (^completionHandler)(NSURL *_Nullable, NSURLResponse *_Nullable, NSError *_Nullable) =
  680. ^(NSURL *_Nullable location, NSURLResponse *_Nullable response, NSError *_Nullable error) {
  681. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  682. [expectation fulfill];
  683. };
  684. downloadTask = [session downloadTaskWithURL:URL completionHandler:completionHandler];
  685. XCTAssertNotNil(downloadTask);
  686. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  687. [downloadTask resume];
  688. [self waitForExpectationsWithTimeout:10.0 handler:nil];
  689. [instrument deregisterInstrumentors];
  690. }
  691. /** Validate that it works with NSMutableURLRequest URLs across data, upload, and download. */
  692. - (void)testMutableRequestURLs {
  693. FPRNSURLSessionInstrument *instrument = [[FPRNSURLSessionInstrument alloc] init];
  694. [instrument registerInstrumentors];
  695. NSURL *URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testDownload"];
  696. NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
  697. NSURLSession *session = [NSURLSession
  698. sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
  699. NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:URLRequest];
  700. [dataTask resume];
  701. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  702. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  703. GCDWebServerResponse *_Nonnull response) {
  704. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:dataTask]);
  705. }];
  706. NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:URLRequest];
  707. [downloadTask resume];
  708. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  709. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  710. GCDWebServerResponse *_Nonnull response) {
  711. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:downloadTask]);
  712. }];
  713. URL = [self.testServer.serverURL URLByAppendingPathComponent:@"testUpload"];
  714. URLRequest.URL = URL;
  715. URLRequest.HTTPMethod = @"POST";
  716. NSData *uploadData = [[NSData alloc] init];
  717. NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:URLRequest
  718. fromData:uploadData];
  719. [uploadTask resume];
  720. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  721. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  722. GCDWebServerResponse *_Nonnull response) {
  723. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  724. }];
  725. NSURL *fileURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"smallDownloadFile"
  726. withExtension:@""];
  727. uploadTask = [session uploadTaskWithRequest:URLRequest fromFile:fileURL];
  728. [uploadTask resume];
  729. XCTAssertNotNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  730. [self waitAndRunBlockAfterResponse:^(id self, GCDWebServerRequest *_Nonnull request,
  731. GCDWebServerResponse *_Nonnull response) {
  732. XCTAssertNil([FPRNetworkTrace networkTraceFromObject:uploadTask]);
  733. }];
  734. [instrument deregisterInstrumentors];
  735. }
  736. @end