GULReachabilityCheckerTest.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // Copyright 2018 Google
  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 <GoogleUtilities/GULReachabilityChecker.h>
  15. #import <XCTest/XCTest.h>
  16. #import "GoogleUtilities/Reachability/GULReachabilityChecker+Internal.h"
  17. @interface GULReachabilityCheckerTest : XCTestCase <GULReachabilityDelegate> {
  18. @private
  19. GULReachabilityChecker *checker_;
  20. NSMutableArray *statuses_;
  21. BOOL createFail_;
  22. BOOL setCallbackFail_;
  23. BOOL scheduleUnscheduleFail_;
  24. }
  25. - (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator withName:(const char *)hostname;
  26. - (BOOL)reachability:(const void *)reachability
  27. setCallback:(SCNetworkReachabilityCallBack)callback
  28. withContext:(SCNetworkReachabilityContext *)context;
  29. - (BOOL)scheduleReachability:(const void *)reachability
  30. runLoop:(CFRunLoopRef)runLoop
  31. runLoopMode:(CFStringRef)runLoopMode;
  32. - (BOOL)unscheduleReachability:(const void *)reachability
  33. runLoop:(CFRunLoopRef)runLoop
  34. runLoopMode:(CFStringRef)runLoopMode;
  35. - (void)releaseReachability:(const void *)reachability;
  36. @end
  37. static NSString *const kHostname = @"www.google.com";
  38. static const void *kFakeReachabilityObject = (const void *)0x8badf00d;
  39. static GULReachabilityCheckerTest *FakeReachabilityTest = nil;
  40. static struct {
  41. int callsMade;
  42. int createCall;
  43. int setCallbackCall;
  44. int scheduleCall;
  45. int unscheduleCall;
  46. int releaseCall;
  47. void (*callback)(SCNetworkReachabilityRef, SCNetworkReachabilityFlags, void *info);
  48. void *callbackInfo;
  49. } FakeReachability;
  50. static SCNetworkReachabilityRef ReachabilityCreateWithName(CFAllocatorRef allocator,
  51. const char *hostname) {
  52. return (SCNetworkReachabilityRef)[FakeReachabilityTest createReachabilityWithAllocator:allocator
  53. withName:hostname];
  54. }
  55. static Boolean ReachabilitySetCallback(SCNetworkReachabilityRef reachability,
  56. SCNetworkReachabilityCallBack callback,
  57. SCNetworkReachabilityContext *context) {
  58. return [FakeReachabilityTest reachability:reachability setCallback:callback withContext:context];
  59. }
  60. static Boolean ReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef reachability,
  61. CFRunLoopRef runLoop,
  62. CFStringRef runLoopMode) {
  63. return [FakeReachabilityTest scheduleReachability:reachability
  64. runLoop:runLoop
  65. runLoopMode:runLoopMode];
  66. }
  67. static Boolean ReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef reachability,
  68. CFRunLoopRef runLoop,
  69. CFStringRef runLoopMode) {
  70. return [FakeReachabilityTest unscheduleReachability:reachability
  71. runLoop:runLoop
  72. runLoopMode:runLoopMode];
  73. }
  74. static void ReachabilityRelease(CFTypeRef reachability) {
  75. [FakeReachabilityTest releaseReachability:reachability];
  76. }
  77. static const struct GULReachabilityApi kTestReachabilityApi = {
  78. ReachabilityCreateWithName, ReachabilitySetCallback, ReachabilityScheduleWithRunLoop,
  79. ReachabilityUnscheduleFromRunLoop, ReachabilityRelease,
  80. };
  81. @implementation GULReachabilityCheckerTest
  82. - (void)resetFakeReachability {
  83. FakeReachabilityTest = self;
  84. FakeReachability.callsMade = 0;
  85. FakeReachability.createCall = -1;
  86. FakeReachability.setCallbackCall = -1;
  87. FakeReachability.scheduleCall = -1;
  88. FakeReachability.unscheduleCall = -1;
  89. FakeReachability.releaseCall = -1;
  90. FakeReachability.callback = NULL;
  91. FakeReachability.callbackInfo = NULL;
  92. }
  93. - (void)setUp {
  94. [super setUp];
  95. [self resetFakeReachability];
  96. createFail_ = NO;
  97. setCallbackFail_ = NO;
  98. scheduleUnscheduleFail_ = NO;
  99. checker_ = [[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:kHostname];
  100. statuses_ = [[NSMutableArray alloc] init];
  101. }
  102. - (void *)createReachabilityWithAllocator:(CFAllocatorRef)allocator
  103. withName:(const char *)hostname {
  104. XCTAssertTrue(allocator == kCFAllocatorDefault, @"");
  105. XCTAssertEqual(strcmp(hostname, [kHostname UTF8String]), 0, @"");
  106. XCTAssertEqual(FakeReachability.callsMade, 0, @"create call must always come first.");
  107. XCTAssertEqual(FakeReachability.createCall, -1, @"create call must only be called once.");
  108. FakeReachability.createCall = ++FakeReachability.callsMade;
  109. return createFail_ ? NULL : (void *)kFakeReachabilityObject;
  110. }
  111. - (BOOL)reachability:(const void *)reachability
  112. setCallback:(SCNetworkReachabilityCallBack)callback
  113. withContext:(SCNetworkReachabilityContext *)context {
  114. XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object");
  115. XCTAssertEqual((int)context->version, 0, @"");
  116. XCTAssertEqual(context->info, (__bridge void *)checker_, @"");
  117. XCTAssertEqual((void *)context->retain, NULL, @"");
  118. XCTAssertEqual((void *)context->release, NULL, @"");
  119. XCTAssertEqual((void *)context->copyDescription, NULL, @"");
  120. XCTAssertEqual(FakeReachability.setCallbackCall, -1, @"setCallback should only be called once.");
  121. FakeReachability.setCallbackCall = ++FakeReachability.callsMade;
  122. XCTAssertTrue(callback != NULL, @"");
  123. FakeReachability.callback = callback;
  124. XCTAssertTrue(context->info != NULL, @"");
  125. FakeReachability.callbackInfo = context->info;
  126. return setCallbackFail_ ? NO : YES;
  127. }
  128. - (BOOL)scheduleReachability:(const void *)reachability
  129. runLoop:(CFRunLoopRef)runLoop
  130. runLoopMode:(CFStringRef)runLoopMode {
  131. XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object");
  132. XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop");
  133. XCTAssertEqualObjects((__bridge NSString *)runLoopMode,
  134. (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode");
  135. XCTAssertEqual(FakeReachability.scheduleCall, -1,
  136. @"scheduleWithRunLoop should only be called once.");
  137. FakeReachability.scheduleCall = ++FakeReachability.callsMade;
  138. return scheduleUnscheduleFail_ ? NO : YES;
  139. }
  140. - (BOOL)unscheduleReachability:(const void *)reachability
  141. runLoop:(CFRunLoopRef)runLoop
  142. runLoopMode:(CFStringRef)runLoopMode {
  143. XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object");
  144. XCTAssertEqual(runLoop, CFRunLoopGetMain(), @"bad run loop");
  145. XCTAssertEqualObjects((__bridge NSString *)runLoopMode,
  146. (__bridge NSString *)kCFRunLoopCommonModes, @"bad run loop mode");
  147. XCTAssertEqual(FakeReachability.unscheduleCall, -1,
  148. @"unscheduleFromRunLoop should only be called once.");
  149. FakeReachability.unscheduleCall = ++FakeReachability.callsMade;
  150. return scheduleUnscheduleFail_ ? NO : YES;
  151. }
  152. - (void)releaseReachability:(const void *)reachability {
  153. XCTAssertEqual(reachability, kFakeReachabilityObject, @"got bad object");
  154. XCTAssertEqual(FakeReachability.releaseCall, -1, @"release should only be called once.");
  155. FakeReachability.releaseCall = ++FakeReachability.callsMade;
  156. }
  157. - (void)reachability:(GULReachabilityChecker *)reachability
  158. statusChanged:(GULReachabilityStatus)status {
  159. [statuses_ addObject:[NSNumber numberWithInt:(int)status]];
  160. }
  161. #pragma mark - Test
  162. - (void)testApiHappyPath {
  163. [checker_ setReachabilityApi:&kTestReachabilityApi];
  164. XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @"");
  165. XCTAssertFalse(checker_.isActive, @"");
  166. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  167. XCTAssertTrue([checker_ start], @"");
  168. XCTAssertTrue(checker_.isActive, @"");
  169. XCTAssertEqual([statuses_ count], (NSUInteger)0, @"");
  170. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  171. FakeReachability.callback(kFakeReachabilityObject, 0, FakeReachability.callbackInfo);
  172. XCTAssertEqual([statuses_ count], (NSUInteger)1, @"");
  173. XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:0] intValue],
  174. (int)kGULReachabilityNotReachable, @"");
  175. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @"");
  176. FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsReachable,
  177. FakeReachability.callbackInfo);
  178. XCTAssertEqual([statuses_ count], (NSUInteger)2, @"");
  179. XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:1] intValue], (int)kGULReachabilityViaWifi,
  180. @"");
  181. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaWifi, @"");
  182. FakeReachability.callback(
  183. kFakeReachabilityObject,
  184. kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsConnectionRequired,
  185. FakeReachability.callbackInfo);
  186. XCTAssertEqual([statuses_ count], (NSUInteger)3, @"");
  187. XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:2] intValue],
  188. (int)kGULReachabilityNotReachable, @"");
  189. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @"");
  190. #if TARGET_OS_IOS || TARGET_OS_TV
  191. FakeReachability.callback(
  192. kFakeReachabilityObject,
  193. kSCNetworkReachabilityFlagsReachable | kSCNetworkReachabilityFlagsIsWWAN,
  194. FakeReachability.callbackInfo);
  195. XCTAssertEqual([statuses_ count], (NSUInteger)4, @"");
  196. XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:3] intValue],
  197. (int)kGULReachabilityViaCellular, @"");
  198. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityViaCellular, @"");
  199. FakeReachability.callback(kFakeReachabilityObject, kSCNetworkReachabilityFlagsIsWWAN,
  200. FakeReachability.callbackInfo);
  201. XCTAssertEqual([statuses_ count], (NSUInteger)5, @"");
  202. XCTAssertEqual([(NSNumber *)[statuses_ objectAtIndex:4] intValue],
  203. (int)kGULReachabilityNotReachable, @"");
  204. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityNotReachable, @"");
  205. #endif
  206. [checker_ stop];
  207. XCTAssertFalse(checker_.isActive, @"");
  208. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  209. XCTAssertEqual(FakeReachability.callsMade, 5, @"");
  210. XCTAssertEqual(FakeReachability.createCall, 1, @"");
  211. XCTAssertEqual(FakeReachability.setCallbackCall, 2, @"");
  212. XCTAssertEqual(FakeReachability.scheduleCall, 3, @"");
  213. XCTAssertEqual(FakeReachability.unscheduleCall, 4, @"");
  214. XCTAssertEqual(FakeReachability.releaseCall, 5, @"");
  215. }
  216. - (void)testApiCreateFail {
  217. [checker_ setReachabilityApi:&kTestReachabilityApi];
  218. XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @"");
  219. createFail_ = YES;
  220. XCTAssertFalse(checker_.isActive, @"");
  221. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  222. XCTAssertFalse([checker_ start], @"");
  223. XCTAssertFalse(checker_.isActive, @"");
  224. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  225. [checker_ stop];
  226. XCTAssertFalse(checker_.isActive, @"");
  227. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  228. XCTAssertEqual(FakeReachability.callsMade, 1, @"");
  229. XCTAssertEqual(FakeReachability.createCall, 1, @"");
  230. XCTAssertEqual(FakeReachability.setCallbackCall, -1, @"");
  231. XCTAssertEqual(FakeReachability.scheduleCall, -1, @"");
  232. XCTAssertEqual(FakeReachability.unscheduleCall, -1, @"");
  233. XCTAssertEqual(FakeReachability.releaseCall, -1, @"");
  234. XCTAssertEqual([statuses_ count], (NSUInteger)0, @"");
  235. }
  236. - (void)testApiCallbackFail {
  237. [checker_ setReachabilityApi:&kTestReachabilityApi];
  238. XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @"");
  239. setCallbackFail_ = YES;
  240. XCTAssertFalse(checker_.isActive, @"");
  241. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  242. XCTAssertFalse([checker_ start], @"");
  243. XCTAssertFalse(checker_.isActive, @"");
  244. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  245. [checker_ stop];
  246. XCTAssertFalse(checker_.isActive, @"");
  247. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  248. XCTAssertEqual(FakeReachability.callsMade, 3, @"");
  249. XCTAssertEqual(FakeReachability.createCall, 1, @"");
  250. XCTAssertEqual(FakeReachability.setCallbackCall, 2, @"");
  251. XCTAssertEqual(FakeReachability.scheduleCall, -1, @"");
  252. XCTAssertEqual(FakeReachability.unscheduleCall, -1, @"");
  253. XCTAssertEqual(FakeReachability.releaseCall, 3, @"");
  254. XCTAssertEqual([statuses_ count], (NSUInteger)0, @"");
  255. }
  256. - (void)testApiScheduleFail {
  257. [checker_ setReachabilityApi:&kTestReachabilityApi];
  258. XCTAssertEqual([checker_ reachabilityApi], &kTestReachabilityApi, @"");
  259. scheduleUnscheduleFail_ = YES;
  260. XCTAssertFalse(checker_.isActive, @"");
  261. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  262. XCTAssertFalse([checker_ start], @"");
  263. XCTAssertFalse(checker_.isActive, @"");
  264. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  265. [checker_ stop];
  266. XCTAssertFalse(checker_.isActive, @"");
  267. XCTAssertEqual(checker_.reachabilityStatus, kGULReachabilityUnknown, @"");
  268. XCTAssertEqual(FakeReachability.callsMade, 4, @"");
  269. XCTAssertEqual(FakeReachability.createCall, 1, @"");
  270. XCTAssertEqual(FakeReachability.setCallbackCall, 2, @"");
  271. XCTAssertEqual(FakeReachability.scheduleCall, 3, @"");
  272. XCTAssertEqual(FakeReachability.unscheduleCall, -1, @"");
  273. XCTAssertEqual(FakeReachability.releaseCall, 4, @"");
  274. XCTAssertEqual([statuses_ count], (NSUInteger)0, @"");
  275. }
  276. - (void)testBadHost {
  277. XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:nil],
  278. @"Creating a checker with nil hostname must fail.");
  279. XCTAssertNil([[GULReachabilityChecker alloc] initWithReachabilityDelegate:self withHost:@""],
  280. @"Creating a checker with empty hostname must fail.");
  281. }
  282. @end