GULUserDefaultsTests.m 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  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 <OCMock/OCMock.h>
  15. #import <XCTest/XCTest.h>
  16. #import "GoogleUtilities/Network/Private/GULMutableDictionary.h"
  17. #import "GoogleUtilities/UserDefaults/Private/GULUserDefaults.h"
  18. static const double sEpsilon = 0.001;
  19. /// The maximum time to wait for an expectation before failing.
  20. static const NSTimeInterval kGULTestCaseTimeoutInterval = 10;
  21. @interface GULUserDefaultsThreadArgs : NSObject
  22. /// The new user defaults to be tested on threads.
  23. @property(atomic) GULUserDefaults *userDefaults;
  24. /// The old user defaults to be tested on threads.
  25. @property(atomic) NSUserDefaults *oldUserDefaults;
  26. /// The thread index.
  27. @property(atomic) int index;
  28. /// The number of items to be removed/added into the dictionary per thread.
  29. @property(atomic) int itemsPerThread;
  30. /// The dictionary that store all the objects that the user defaults stores.
  31. @property(atomic) GULMutableDictionary *dictionary;
  32. @end
  33. @implementation GULUserDefaultsThreadArgs
  34. @end
  35. @interface GULUserDefaultsTests : XCTestCase
  36. @end
  37. @implementation GULUserDefaultsTests
  38. - (void)testNewUserDefaultsWithStandardUserDefaults {
  39. NSString *suiteName = @"test_suite_defaults";
  40. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  41. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  42. NSString *key1 = @"testing";
  43. NSString *value1 = @"blabla";
  44. [newUserDefaults setObject:value1 forKey:key1];
  45. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"blabla");
  46. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"blabla");
  47. XCTAssertEqualObjects([newUserDefaults stringForKey:key1], @"blabla");
  48. NSString *key2 = @"OtherKey";
  49. NSNumber *number = @(123.45);
  50. [newUserDefaults setDouble:123.45 forKey:key2];
  51. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], number);
  52. XCTAssertEqualWithAccuracy([newUserDefaults doubleForKey:key2], 123.45, sEpsilon);
  53. XCTAssertEqualObjects([userDefaults objectForKey:key2], number);
  54. NSString *key3 = @"ArrayKey";
  55. NSArray *array = @[ @1, @"Hi" ];
  56. [newUserDefaults setObject:array forKey:key3];
  57. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array);
  58. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array);
  59. XCTAssertEqualObjects([userDefaults objectForKey:key3], array);
  60. NSString *key4 = @"DictionaryKey";
  61. NSDictionary *dictionary = @{@"testing" : @"Hi there!"};
  62. [newUserDefaults setObject:dictionary forKey:key4];
  63. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary);
  64. XCTAssertEqualObjects([newUserDefaults dictionaryForKey:key4], dictionary);
  65. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary);
  66. NSString *key5 = @"BoolKey";
  67. NSNumber *boolObject = @(YES);
  68. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  69. XCTAssertFalse([userDefaults boolForKey:key5]);
  70. [newUserDefaults setObject:boolObject forKey:key5];
  71. XCTAssertEqualObjects([newUserDefaults objectForKey:key5], boolObject);
  72. XCTAssertEqualObjects([userDefaults objectForKey:key5], boolObject);
  73. XCTAssertTrue([newUserDefaults boolForKey:key5]);
  74. XCTAssertTrue([userDefaults boolForKey:key5]);
  75. [newUserDefaults setBool:NO forKey:key5];
  76. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  77. XCTAssertFalse([userDefaults boolForKey:key5]);
  78. NSString *key6 = @"DataKey";
  79. NSData *testData = [@"google" dataUsingEncoding:NSUTF8StringEncoding];
  80. [newUserDefaults setObject:testData forKey:key6];
  81. XCTAssertEqualObjects([newUserDefaults objectForKey:key6], testData);
  82. XCTAssertEqualObjects([userDefaults objectForKey:key6], testData);
  83. NSString *key7 = @"DateKey";
  84. NSDate *testDate = [NSDate date];
  85. [newUserDefaults setObject:testDate forKey:key7];
  86. XCTAssertNotNil([newUserDefaults objectForKey:key7]);
  87. XCTAssertNotNil([userDefaults objectForKey:key7]);
  88. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[newUserDefaults objectForKey:key7]],
  89. 0.0, sEpsilon);
  90. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[userDefaults objectForKey:key7]], 0.0,
  91. sEpsilon);
  92. NSString *key8 = @"FloatKey";
  93. [newUserDefaults setFloat:0.99 forKey:key8];
  94. XCTAssertEqualWithAccuracy([newUserDefaults floatForKey:key8], 0.99, sEpsilon);
  95. XCTAssertEqualWithAccuracy([userDefaults floatForKey:key8], 0.99, sEpsilon);
  96. // Remove all of the objects from the normal NSUserDefaults. The values from the new user
  97. // defaults must also be cleared!
  98. [userDefaults removePersistentDomainForName:suiteName];
  99. XCTAssertNil([userDefaults objectForKey:key1]);
  100. XCTAssertNil([newUserDefaults objectForKey:key1]);
  101. XCTAssertNil([userDefaults objectForKey:key2]);
  102. XCTAssertNil([newUserDefaults objectForKey:key2]);
  103. [newUserDefaults setObject:@"anothervalue" forKey:key1];
  104. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"anothervalue");
  105. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"anothervalue");
  106. [newUserDefaults setInteger:111 forKey:key2];
  107. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], @111);
  108. XCTAssertEqual([newUserDefaults integerForKey:key2], 111);
  109. XCTAssertEqualObjects([userDefaults objectForKey:key2], @111);
  110. NSArray *array2 = @[ @2, @"Hello" ];
  111. [newUserDefaults setObject:array2 forKey:key3];
  112. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array2);
  113. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array2);
  114. XCTAssertEqualObjects([userDefaults objectForKey:key3], array2);
  115. NSDictionary *dictionary2 = @{@"testing 2" : @3};
  116. [newUserDefaults setObject:dictionary2 forKey:key4];
  117. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary2);
  118. XCTAssertEqualObjects([newUserDefaults dictionaryForKey:key4], dictionary2);
  119. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary2);
  120. [self removePreferenceFileWithSuiteName:suiteName];
  121. }
  122. - (void)testNSUserDefaultsWithNewUserDefaults {
  123. NSString *suiteName = @"test_suite_defaults_2";
  124. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  125. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  126. NSString *key1 = @"testing";
  127. NSString *value1 = @"blabla";
  128. [userDefaults setObject:value1 forKey:key1];
  129. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"blabla");
  130. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"blabla");
  131. XCTAssertEqualObjects([newUserDefaults stringForKey:key1], @"blabla");
  132. NSString *key2 = @"OtherKey";
  133. NSNumber *number = @(123.45);
  134. [userDefaults setDouble:123.45 forKey:key2];
  135. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], number);
  136. XCTAssertEqualWithAccuracy([newUserDefaults doubleForKey:key2], 123.45, sEpsilon);
  137. XCTAssertEqualObjects([userDefaults objectForKey:key2], number);
  138. NSString *key3 = @"ArrayKey";
  139. NSArray *array = @[ @1, @"Hi" ];
  140. [userDefaults setObject:array forKey:key3];
  141. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array);
  142. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array);
  143. XCTAssertEqualObjects([userDefaults objectForKey:key3], array);
  144. NSString *key4 = @"DictionaryKey";
  145. NSDictionary *dictionary = @{@"testing" : @"Hi there!"};
  146. [userDefaults setObject:dictionary forKey:key4];
  147. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary);
  148. XCTAssertEqualObjects([newUserDefaults dictionaryForKey:key4], dictionary);
  149. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary);
  150. NSString *key5 = @"BoolKey";
  151. NSNumber *boolObject = @(YES);
  152. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  153. XCTAssertFalse([userDefaults boolForKey:key5]);
  154. [userDefaults setObject:boolObject forKey:key5];
  155. XCTAssertEqualObjects([newUserDefaults objectForKey:key5], boolObject);
  156. XCTAssertEqualObjects([userDefaults objectForKey:key5], boolObject);
  157. XCTAssertTrue([newUserDefaults boolForKey:key5]);
  158. XCTAssertTrue([userDefaults boolForKey:key5]);
  159. [userDefaults setObject:@(NO) forKey:key5];
  160. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  161. XCTAssertFalse([userDefaults boolForKey:key5]);
  162. NSString *key6 = @"DataKey";
  163. NSData *testData = [@"google" dataUsingEncoding:NSUTF8StringEncoding];
  164. [userDefaults setObject:testData forKey:key6];
  165. XCTAssertEqualObjects([newUserDefaults objectForKey:key6], testData);
  166. XCTAssertEqualObjects([userDefaults objectForKey:key6], testData);
  167. NSString *key7 = @"DateKey";
  168. NSDate *testDate = [NSDate date];
  169. [userDefaults setObject:testDate forKey:key7];
  170. XCTAssertNotNil([newUserDefaults objectForKey:key7]);
  171. XCTAssertNotNil([userDefaults objectForKey:key7]);
  172. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[newUserDefaults objectForKey:key7]],
  173. 0.0, sEpsilon);
  174. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[userDefaults objectForKey:key7]], 0.0,
  175. sEpsilon);
  176. NSString *key8 = @"FloatKey";
  177. [userDefaults setFloat:0.99 forKey:key8];
  178. XCTAssertEqualWithAccuracy([newUserDefaults floatForKey:key8], 0.99, sEpsilon);
  179. XCTAssertEqualWithAccuracy([userDefaults floatForKey:key8], 0.99, sEpsilon);
  180. // Remove all of the objects from the normal NSUserDefaults. The values from the new user
  181. // defaults must also be cleared!
  182. [userDefaults removePersistentDomainForName:suiteName];
  183. XCTAssertNil([userDefaults objectForKey:key1]);
  184. XCTAssertNil([newUserDefaults objectForKey:key1]);
  185. XCTAssertNil([userDefaults objectForKey:key2]);
  186. XCTAssertNil([newUserDefaults objectForKey:key2]);
  187. [userDefaults setObject:@"anothervalue" forKey:key1];
  188. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"anothervalue");
  189. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"anothervalue");
  190. [userDefaults setObject:@111 forKey:key2];
  191. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], @111);
  192. XCTAssertEqual([newUserDefaults integerForKey:key2], 111);
  193. XCTAssertEqualObjects([userDefaults objectForKey:key2], @111);
  194. NSArray *array2 = @[ @2, @"Hello" ];
  195. [userDefaults setObject:array2 forKey:key3];
  196. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array2);
  197. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array2);
  198. XCTAssertEqualObjects([userDefaults objectForKey:key3], array2);
  199. NSDictionary *dictionary2 = @{@"testing 2" : @3};
  200. [userDefaults setObject:dictionary2 forKey:key4];
  201. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary2);
  202. XCTAssertEqualObjects([newUserDefaults dictionaryForKey:key4], dictionary2);
  203. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary2);
  204. // Remove all of the objects from the new user defaults. The values from the NSUserDefaults must
  205. // also be cleared.
  206. [userDefaults removePersistentDomainForName:suiteName];
  207. XCTAssertNil([userDefaults objectForKey:key1]);
  208. XCTAssertNil([newUserDefaults objectForKey:key1]);
  209. XCTAssertNil([userDefaults objectForKey:key2]);
  210. XCTAssertNil([newUserDefaults objectForKey:key2]);
  211. XCTAssertNil([userDefaults objectForKey:key3]);
  212. XCTAssertNil([newUserDefaults objectForKey:key3]);
  213. XCTAssertNil([userDefaults objectForKey:key4]);
  214. XCTAssertNil([newUserDefaults objectForKey:key4]);
  215. XCTAssertNil([userDefaults objectForKey:key5]);
  216. XCTAssertNil([newUserDefaults objectForKey:key5]);
  217. XCTAssertNil([userDefaults objectForKey:key6]);
  218. XCTAssertNil([newUserDefaults objectForKey:key6]);
  219. XCTAssertNil([userDefaults objectForKey:key7]);
  220. XCTAssertNil([newUserDefaults objectForKey:key7]);
  221. XCTAssertNil([userDefaults objectForKey:key8]);
  222. XCTAssertNil([newUserDefaults objectForKey:key8]);
  223. [self removePreferenceFileWithSuiteName:suiteName];
  224. }
  225. - (void)testNewSharedUserDefaultsWithStandardUserDefaults {
  226. NSString *appDomain = [NSBundle mainBundle].bundleIdentifier;
  227. NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  228. GULUserDefaults *newUserDefaults = [GULUserDefaults standardUserDefaults];
  229. NSString *key1 = @"testing";
  230. NSString *value1 = @"blabla";
  231. [newUserDefaults setObject:value1 forKey:key1];
  232. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"blabla");
  233. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"blabla");
  234. XCTAssertEqualObjects([newUserDefaults stringForKey:key1], @"blabla");
  235. NSString *key2 = @"OtherKey";
  236. NSNumber *number = @(123.45);
  237. [newUserDefaults setObject:number forKey:key2];
  238. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], number);
  239. XCTAssertEqualWithAccuracy([newUserDefaults doubleForKey:key2], 123.45, sEpsilon);
  240. XCTAssertEqualWithAccuracy([newUserDefaults floatForKey:key2], 123.45, sEpsilon);
  241. XCTAssertEqualObjects([userDefaults objectForKey:key2], number);
  242. NSString *key3 = @"ArrayKey";
  243. NSArray *array = @[ @1, @"Hi" ];
  244. [userDefaults setObject:array forKey:key3];
  245. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array);
  246. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array);
  247. XCTAssertEqualObjects([userDefaults objectForKey:key3], array);
  248. NSString *key4 = @"DictionaryKey";
  249. NSDictionary *dictionary = @{@"testing" : @"Hi there!"};
  250. [userDefaults setObject:dictionary forKey:key4];
  251. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary);
  252. XCTAssertEqualObjects([newUserDefaults dictionaryForKey:key4], dictionary);
  253. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary);
  254. NSString *key5 = @"BoolKey";
  255. NSNumber *boolObject = @(1);
  256. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  257. XCTAssertFalse([userDefaults boolForKey:key5]);
  258. [userDefaults setObject:boolObject forKey:key5];
  259. XCTAssertEqualObjects([newUserDefaults objectForKey:key5], boolObject);
  260. XCTAssertEqualObjects([userDefaults objectForKey:key5], boolObject);
  261. XCTAssertTrue([newUserDefaults boolForKey:key5]);
  262. XCTAssertTrue([userDefaults boolForKey:key5]);
  263. [userDefaults setObject:@(0) forKey:key5];
  264. XCTAssertFalse([newUserDefaults boolForKey:key5]);
  265. XCTAssertFalse([userDefaults boolForKey:key5]);
  266. NSString *key6 = @"DataKey";
  267. NSData *testData = [@"google" dataUsingEncoding:NSUTF8StringEncoding];
  268. [newUserDefaults setObject:testData forKey:key6];
  269. XCTAssertEqualObjects([newUserDefaults objectForKey:key6], testData);
  270. XCTAssertEqualObjects([userDefaults objectForKey:key6], testData);
  271. NSString *key7 = @"DateKey";
  272. NSDate *testDate = [NSDate date];
  273. [newUserDefaults setObject:testDate forKey:key7];
  274. XCTAssertNotNil([newUserDefaults objectForKey:key7]);
  275. XCTAssertNotNil([userDefaults objectForKey:key7]);
  276. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[newUserDefaults objectForKey:key7]],
  277. 0.0, sEpsilon);
  278. XCTAssertEqualWithAccuracy([testDate timeIntervalSinceDate:[userDefaults objectForKey:key7]], 0.0,
  279. sEpsilon);
  280. // Remove all of the objects from the normal NSUserDefaults. The values from the new user
  281. // defaults must also be cleared!
  282. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
  283. XCTAssertNil([userDefaults objectForKey:key1]);
  284. XCTAssertNil([newUserDefaults objectForKey:key1]);
  285. XCTAssertNil([userDefaults objectForKey:key2]);
  286. XCTAssertNil([newUserDefaults objectForKey:key2]);
  287. [userDefaults setObject:@"anothervalue" forKey:key1];
  288. XCTAssertEqualObjects([newUserDefaults objectForKey:key1], @"anothervalue");
  289. XCTAssertEqualObjects([userDefaults objectForKey:key1], @"anothervalue");
  290. [userDefaults setObject:@111 forKey:key2];
  291. XCTAssertEqualObjects([newUserDefaults objectForKey:key2], @111);
  292. XCTAssertEqual([newUserDefaults integerForKey:key2], 111);
  293. XCTAssertEqualObjects([userDefaults objectForKey:key2], @111);
  294. NSArray *array2 = @[ @2, @"Hello" ];
  295. [userDefaults setObject:array2 forKey:key3];
  296. XCTAssertEqualObjects([newUserDefaults objectForKey:key3], array2);
  297. XCTAssertEqualObjects([newUserDefaults arrayForKey:key3], array2);
  298. XCTAssertEqualObjects([userDefaults objectForKey:key3], array2);
  299. NSDictionary *dictionary2 = @{@"testing 2" : @3};
  300. [userDefaults setObject:dictionary2 forKey:key4];
  301. XCTAssertEqualObjects([newUserDefaults objectForKey:key4], dictionary2);
  302. XCTAssertEqualObjects([userDefaults objectForKey:key4], dictionary2);
  303. [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
  304. }
  305. - (void)testUserDefaultNotifications {
  306. // Test to ensure no notifications are sent with our implementation.
  307. void (^callBlock)(NSNotification *) = ^(NSNotification *_Nonnull notification) {
  308. XCTFail(@"A notification must not be sent for GULUserDefaults!");
  309. };
  310. id observer =
  311. [[NSNotificationCenter defaultCenter] addObserverForName:NSUserDefaultsDidChangeNotification
  312. object:nil
  313. queue:nil
  314. usingBlock:callBlock];
  315. NSString *suiteName = @"test_suite_notification";
  316. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  317. [newUserDefaults setObject:@"134" forKey:@"test-another"];
  318. XCTAssertEqualObjects([newUserDefaults objectForKey:@"test-another"], @"134");
  319. [newUserDefaults setObject:nil forKey:@"test-another"];
  320. XCTAssertNil([newUserDefaults objectForKey:@"test-another"]);
  321. [newUserDefaults synchronize];
  322. [[NSNotificationCenter defaultCenter] removeObserver:observer];
  323. // Remove the underlying reference file.
  324. [self removePreferenceFileWithSuiteName:suiteName];
  325. }
  326. - (void)testSynchronizeToDisk {
  327. #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
  328. // `NSFileManager` has trouble reading the files in `~/Library` even though the
  329. // `removeItemAtPath:` call works. Watching Finder while stepping through this test shows that the
  330. // file does get created and removed properly. When using LLDB to call `fileExistsAtPath:` the
  331. // correct return value of `YES` is returned, but in this test it returns `NO`. Best guess is the
  332. // test app is sandboxed and `NSFileManager` is refusing to read the directory.
  333. // TODO: Investigate the failure and re-enable this test.
  334. return;
  335. #endif // TARGET_OS_OSX
  336. NSString *suiteName = [NSString stringWithFormat:@"another_test_suite"];
  337. NSString *filePath = [self filePathForPreferencesName:suiteName];
  338. NSFileManager *fileManager = [NSFileManager defaultManager];
  339. // Test the new User Defaults.
  340. [fileManager removeItemAtPath:filePath error:NULL];
  341. XCTAssertFalse([fileManager fileExistsAtPath:filePath]);
  342. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  343. [newUserDefaults setObject:@"134" forKey:@"test-another"];
  344. [newUserDefaults synchronize];
  345. XCTAssertTrue([fileManager fileExistsAtPath:filePath],
  346. @"The user defaults file was not synchronized to disk.");
  347. // Now get the file directly from disk.
  348. XCTAssertTrue([fileManager fileExistsAtPath:filePath]);
  349. [newUserDefaults synchronize];
  350. [self removePreferenceFileWithSuiteName:suiteName];
  351. }
  352. - (void)testInvalidKeys {
  353. NSString *suiteName = @"test_suite_invalid_key";
  354. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  355. #pragma clang diagnostic push
  356. #pragma clang diagnostic ignored "-Wnonnull"
  357. // These mostly to make sure that we don't crash.
  358. [newUserDefaults setObject:@"test" forKey:nil];
  359. [newUserDefaults setObject:@"test" forKey:(NSString *)@123];
  360. [newUserDefaults setObject:@"test" forKey:@""];
  361. [newUserDefaults objectForKey:@""];
  362. [newUserDefaults objectForKey:(NSString *)@123];
  363. [newUserDefaults objectForKey:nil];
  364. #pragma clang diagnostic pop
  365. [self removePreferenceFileWithSuiteName:suiteName];
  366. }
  367. - (void)testInvalidObjects {
  368. NSString *suiteName = @"test_suite_invalid_obj";
  369. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  370. GULMutableDictionary *invalidObject = [[GULMutableDictionary alloc] init];
  371. [newUserDefaults setObject:invalidObject forKey:@"Key"];
  372. XCTAssertNil([newUserDefaults objectForKey:@"Key"]);
  373. [self removePreferenceFileWithSuiteName:suiteName];
  374. }
  375. - (void)testSetNilObject {
  376. NSString *suiteName = @"test_suite_set_nil";
  377. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  378. [newUserDefaults setObject:@"blabla" forKey:@"fine"];
  379. XCTAssertEqualObjects([newUserDefaults objectForKey:@"fine"], @"blabla");
  380. [newUserDefaults setObject:nil forKey:@"fine"];
  381. XCTAssertNil([newUserDefaults objectForKey:@"fine"]);
  382. [self removePreferenceFileWithSuiteName:suiteName];
  383. }
  384. - (void)testRemoveObject {
  385. NSString *suiteName = @"test_suite_remove";
  386. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  387. [newUserDefaults setObject:@"blabla" forKey:@"fine"];
  388. XCTAssertEqualObjects([newUserDefaults objectForKey:@"fine"], @"blabla");
  389. [newUserDefaults removeObjectForKey:@"fine"];
  390. XCTAssertNil([newUserDefaults objectForKey:@"fine"]);
  391. [self removePreferenceFileWithSuiteName:suiteName];
  392. }
  393. - (void)testNewUserDefaultsWithNSUserDefaultsFile {
  394. NSString *suiteName = @"test_suite_file";
  395. // Create a user defaults with a key and value. This is to make sure that the new user defaults
  396. // also uses the same plist file.
  397. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  398. XCTAssertNil([userDefaults objectForKey:@"key1"]);
  399. XCTAssertNil([userDefaults objectForKey:@"key2"]);
  400. [userDefaults setObject:@"value1" forKey:@"key1"];
  401. [userDefaults setObject:@"value2" forKey:@"key2"];
  402. [userDefaults synchronize];
  403. userDefaults = nil;
  404. // Now the new user defaults should access the same values.
  405. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  406. XCTAssertEqualObjects([newUserDefaults objectForKey:@"key1"], @"value1");
  407. XCTAssertEqualObjects([newUserDefaults objectForKey:@"key2"], @"value2");
  408. // Clean up.
  409. [self removePreferenceFileWithSuiteName:suiteName];
  410. }
  411. #pragma mark - Thread-safety test
  412. - (void)testNewUserDefaultsThreadSafeAddingObjects {
  413. NSString *suiteName = @"test_adding_threadsafe";
  414. int itemCount = 100;
  415. int itemsPerThread = 10;
  416. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  417. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  418. // Have 100 threads to add 100 unique keys and values into the dictionary.
  419. for (int threadNum = 0; threadNum < 10; threadNum++) {
  420. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  421. args.userDefaults = userDefaults;
  422. args.dictionary = dictionary;
  423. args.itemsPerThread = itemsPerThread;
  424. args.index = threadNum;
  425. [NSThread detachNewThreadSelector:@selector(addObjectsThread:) toTarget:self withObject:args];
  426. }
  427. // Verify the size of the dictionary.
  428. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == %d", itemCount];
  429. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  430. evaluatedWithObject:dictionary
  431. handler:nil];
  432. [self waitForExpectations:@[ expectation ] timeout:kGULTestCaseTimeoutInterval];
  433. for (int i = 0; i < itemCount; i++) {
  434. NSString *key = [NSString stringWithFormat:@"%d", i];
  435. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  436. }
  437. [self removePreferenceFileWithSuiteName:suiteName];
  438. }
  439. - (void)testNewUserDefaultsRemovingObjects {
  440. NSString *suiteName = @"test_removing_threadsafe";
  441. int itemCount = 100;
  442. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  443. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  444. // Create a dictionary of 100 unique keys and values.
  445. for (int i = 0; i < itemCount; i++) {
  446. NSString *key = [NSString stringWithFormat:@"%d", i];
  447. [userDefaults setObject:@(i) forKey:key];
  448. dictionary[key] = @(i);
  449. }
  450. XCTAssertEqual(dictionary.count, 100);
  451. // Spawn 10 threads to remove all items inside the dictionary.
  452. int itemsPerThread = 100;
  453. for (int threadNum = 0; threadNum < 10; threadNum++) {
  454. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  455. args.userDefaults = userDefaults;
  456. args.dictionary = dictionary;
  457. args.itemsPerThread = itemsPerThread;
  458. args.index = threadNum;
  459. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  460. toTarget:self
  461. withObject:args];
  462. }
  463. // Ensure the dictionary is empty after removing objects.
  464. NSPredicate *emptyDictionary = [NSPredicate predicateWithFormat:@"count == 0"];
  465. XCTestExpectation *expectation = [self expectationForPredicate:emptyDictionary
  466. evaluatedWithObject:dictionary
  467. handler:nil];
  468. [self waitForExpectations:@[ expectation ] timeout:kGULTestCaseTimeoutInterval];
  469. for (int i = 0; i < itemCount; i++) {
  470. NSString *key = [NSString stringWithFormat:@"%d", i];
  471. XCTAssertNil([userDefaults objectForKey:key]);
  472. }
  473. [self removePreferenceFileWithSuiteName:suiteName];
  474. }
  475. - (void)testNewUserDefaultsRemovingSomeObjects {
  476. NSString *suiteName = @"test_remove_some_objs";
  477. int itemCount = 200;
  478. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  479. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  480. // Create a dictionary of 100 unique keys and values.
  481. for (int i = 0; i < itemCount; i++) {
  482. NSString *key = [NSString stringWithFormat:@"%d", i];
  483. [userDefaults setObject:@(i) forKey:key];
  484. dictionary[key] = @(i);
  485. }
  486. // Spawn 10 threads to remove the first 100 items inside the dictionary.
  487. int itemsPerThread = 10;
  488. for (int threadNum = 0; threadNum < 10; threadNum++) {
  489. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  490. args.userDefaults = userDefaults;
  491. args.dictionary = dictionary;
  492. args.itemsPerThread = itemsPerThread;
  493. args.index = threadNum;
  494. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  495. toTarget:self
  496. withObject:args];
  497. }
  498. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == 100"];
  499. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  500. evaluatedWithObject:dictionary
  501. handler:nil];
  502. [self waitForExpectations:@[ expectation ] timeout:kGULTestCaseTimeoutInterval];
  503. // Check the remaining of the user defaults.
  504. for (int i = 0; i < itemCount; i++) {
  505. NSString *key = [NSString stringWithFormat:@"%d", i];
  506. if (i < 100) {
  507. XCTAssertNil([userDefaults objectForKey:key]);
  508. } else {
  509. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  510. }
  511. }
  512. [self removePreferenceFileWithSuiteName:suiteName];
  513. }
  514. - (void)testBothUserDefaultsThreadSafeAddingObjects {
  515. NSString *suiteName = @"test_adding_both_user_defaults_threadsafe";
  516. int itemCount = 100;
  517. int itemsPerThread = 10;
  518. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  519. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"testing"];
  520. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  521. // Have 100 threads to add 100 unique keys and values into the dictionary.
  522. for (int threadNum = 0; threadNum < 10; threadNum++) {
  523. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  524. args.userDefaults = newUserDefaults;
  525. args.oldUserDefaults = userDefaults;
  526. args.dictionary = dictionary;
  527. args.itemsPerThread = itemsPerThread;
  528. args.index = threadNum;
  529. [NSThread detachNewThreadSelector:@selector(addObjectsBothUserDefaultsThread:)
  530. toTarget:self
  531. withObject:args];
  532. }
  533. // Verify the size of the dictionary.
  534. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == %d", itemCount];
  535. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  536. evaluatedWithObject:dictionary
  537. handler:nil];
  538. [self waitForExpectations:@[ expectation ] timeout:kGULTestCaseTimeoutInterval];
  539. for (int i = 0; i < itemCount; i++) {
  540. NSString *key = [NSString stringWithFormat:@"%d", i];
  541. if (i % 2 == 0) {
  542. XCTAssertEqualObjects([newUserDefaults objectForKey:key], @(i));
  543. } else {
  544. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  545. }
  546. }
  547. [self removePreferenceFileWithSuiteName:suiteName];
  548. }
  549. - (void)testBothUserDefaultsRemovingSomeObjects {
  550. NSString *suiteName = @"test_remove_some_objs_both_user_defaults";
  551. int itemCount = 200;
  552. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  553. NSUserDefaults *oldUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  554. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  555. // Create a dictionary of 100 unique keys and values.
  556. for (int i = 0; i < itemCount; i++) {
  557. NSString *key = [NSString stringWithFormat:@"%d", i];
  558. [userDefaults setObject:@(i) forKey:key];
  559. dictionary[key] = @(i);
  560. }
  561. // Spawn 10 threads to remove the first 100 items inside the dictionary.
  562. int itemsPerThread = 10;
  563. for (int threadNum = 0; threadNum < 10; threadNum++) {
  564. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  565. args.userDefaults = userDefaults;
  566. args.oldUserDefaults = oldUserDefaults;
  567. args.dictionary = dictionary;
  568. args.itemsPerThread = itemsPerThread;
  569. args.index = threadNum;
  570. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  571. toTarget:self
  572. withObject:args];
  573. }
  574. // Verify the size of the dictionary.
  575. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == 100"];
  576. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  577. evaluatedWithObject:dictionary
  578. handler:nil];
  579. [self waitForExpectations:@[ expectation ] timeout:kGULTestCaseTimeoutInterval];
  580. // Check the remaining of the user defaults.
  581. for (int i = 0; i < itemCount; i++) {
  582. NSString *key = [NSString stringWithFormat:@"%d", i];
  583. if (i < 100) {
  584. if (i % 2 == 0) {
  585. XCTAssertNil([userDefaults objectForKey:key]);
  586. } else {
  587. XCTAssertNil([oldUserDefaults objectForKey:key]);
  588. }
  589. } else {
  590. if (i % 2 == 0) {
  591. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  592. } else {
  593. XCTAssertEqualObjects([oldUserDefaults objectForKey:key], @(i));
  594. }
  595. }
  596. }
  597. [self removePreferenceFileWithSuiteName:suiteName];
  598. }
  599. #pragma mark - Thread methods
  600. /// Add objects into the current GULUserDefaults given arguments.
  601. - (void)addObjectsThread:(GULUserDefaultsThreadArgs *)args {
  602. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  603. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  604. NSString *key = [NSString stringWithFormat:@"%d", i];
  605. [args.userDefaults setObject:@(i) forKey:key];
  606. args.dictionary[key] = @(i);
  607. }
  608. }
  609. /// Remove objects from the current GULUserDefaults given arguments.
  610. - (void)removeObjectsThread:(GULUserDefaultsThreadArgs *)args {
  611. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  612. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  613. NSString *key = [NSString stringWithFormat:@"%d", i];
  614. [args.userDefaults removeObjectForKey:key];
  615. [args.dictionary removeObjectForKey:key];
  616. }
  617. }
  618. /// Add objects into both user defaults given arguments.
  619. - (void)addObjectsBothUserDefaultsThread:(GULUserDefaultsThreadArgs *)args {
  620. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  621. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  622. NSString *key = [NSString stringWithFormat:@"%d", i];
  623. if (i % 2 == 0) {
  624. [args.userDefaults setObject:@(i) forKey:key];
  625. } else {
  626. [args.oldUserDefaults setObject:@(i) forKey:key];
  627. }
  628. args.dictionary[key] = @(i);
  629. }
  630. }
  631. /// Remove objects from both user defaults given arguments.
  632. - (void)removeObjectsFromBothUserDefaultsThread:(GULUserDefaultsThreadArgs *)args {
  633. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  634. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  635. NSString *key = [NSString stringWithFormat:@"%d", i];
  636. if (i % 2 == 0) {
  637. [args.userDefaults removeObjectForKey:key];
  638. } else {
  639. [args.oldUserDefaults removeObjectForKey:key];
  640. }
  641. [args.dictionary removeObjectForKey:key];
  642. }
  643. }
  644. #pragma mark - Helper
  645. - (NSString *)filePathForPreferencesName:(NSString *)preferencesName {
  646. if (!preferencesName.length) {
  647. return @"";
  648. }
  649. // User Defaults exist in the Library directory, get the path to use it as a prefix.
  650. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
  651. if (!paths.lastObject) {
  652. XCTFail(@"Library directory not found - NSSearchPath results are empty.");
  653. }
  654. NSArray *components = @[
  655. paths.lastObject, @"Preferences", [preferencesName stringByAppendingPathExtension:@"plist"]
  656. ];
  657. return [NSString pathWithComponents:components];
  658. }
  659. - (void)removePreferenceFileWithSuiteName:(NSString *)suiteName {
  660. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  661. [userDefaults removePersistentDomainForName:suiteName];
  662. NSString *path = [self filePathForPreferencesName:suiteName];
  663. NSFileManager *fileManager = [NSFileManager defaultManager];
  664. if ([fileManager fileExistsAtPath:path]) {
  665. XCTAssertTrue([fileManager removeItemAtPath:path error:NULL]);
  666. }
  667. }
  668. @end