GULUserDefaultsTests.m 36 KB

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