GULUserDefaultsTests.m 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  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]],
  95. 0.0, 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]],
  198. 0.0, 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]],
  302. 0.0, 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_MAC
  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_MAC
  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], @"The user defaults file was not synchronized to disk.");
  387. // Now get the file directly from disk.
  388. XCTAssertTrue([fileManager fileExistsAtPath:filePath]);
  389. [newUserDefaults clearAllData];
  390. [newUserDefaults synchronize];
  391. [self removePreferenceFileWithSuiteName:suiteName];
  392. }
  393. - (void)testInvalidKeys {
  394. NSString *suiteName = @"test_suite_invalid_key";
  395. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  396. #pragma clang diagnostic push
  397. #pragma clang diagnostic ignored "-Wnonnull"
  398. // These mostly to make sure that we don't crash.
  399. [newUserDefaults setObject:@"test" forKey:nil];
  400. [newUserDefaults setObject:@"test" forKey:(NSString *)@123];
  401. [newUserDefaults setObject:@"test" forKey:@""];
  402. [newUserDefaults objectForKey:@""];
  403. [newUserDefaults objectForKey:(NSString *)@123];
  404. [newUserDefaults objectForKey:nil];
  405. #pragma clang diagnostic pop
  406. [self removePreferenceFileWithSuiteName:suiteName];
  407. }
  408. - (void)testInvalidObjects {
  409. NSString *suiteName = @"test_suite_invalid_obj";
  410. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  411. GULMutableDictionary *invalidObject = [[GULMutableDictionary alloc] init];
  412. [newUserDefaults setObject:invalidObject forKey:@"Key"];
  413. XCTAssertNil([newUserDefaults objectForKey:@"Key"]);
  414. [self removePreferenceFileWithSuiteName:suiteName];
  415. }
  416. - (void)testSetNilObject {
  417. NSString *suiteName = @"test_suite_set_nil";
  418. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  419. [newUserDefaults setObject:@"blabla" forKey:@"fine"];
  420. XCTAssertEqualObjects([newUserDefaults objectForKey:@"fine"], @"blabla");
  421. [newUserDefaults setObject:nil forKey:@"fine"];
  422. XCTAssertNil([newUserDefaults objectForKey:@"fine"]);
  423. [newUserDefaults clearAllData];
  424. [self removePreferenceFileWithSuiteName:suiteName];
  425. }
  426. - (void)testRemoveObject {
  427. NSString *suiteName = @"test_suite_remove";
  428. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  429. [newUserDefaults setObject:@"blabla" forKey:@"fine"];
  430. XCTAssertEqualObjects([newUserDefaults objectForKey:@"fine"], @"blabla");
  431. [newUserDefaults removeObjectForKey:@"fine"];
  432. XCTAssertNil([newUserDefaults objectForKey:@"fine"]);
  433. [newUserDefaults clearAllData];
  434. [self removePreferenceFileWithSuiteName:suiteName];
  435. }
  436. - (void)testNewUserDefaultsWithNSUserDefaultsFile {
  437. NSString *suiteName = @"test_suite_file";
  438. // Create a user defaults with a key and value. This is to make sure that the new user defaults
  439. // also uses the same plist file.
  440. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  441. XCTAssertNil([userDefaults objectForKey:@"key1"]);
  442. XCTAssertNil([userDefaults objectForKey:@"key2"]);
  443. [userDefaults setObject:@"value1" forKey:@"key1"];
  444. [userDefaults setObject:@"value2" forKey:@"key2"];
  445. [userDefaults synchronize];
  446. userDefaults = nil;
  447. // Now the new user defaults should access the same values.
  448. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  449. XCTAssertEqualObjects([newUserDefaults objectForKey:@"key1"], @"value1");
  450. XCTAssertEqualObjects([newUserDefaults objectForKey:@"key2"], @"value2");
  451. [newUserDefaults clearAllData];
  452. // Clean up.
  453. [self removePreferenceFileWithSuiteName:suiteName];
  454. }
  455. #pragma mark - Thread-safety test
  456. - (void)testNewUserDefaultsThreadSafeAddingObjects {
  457. NSString *suiteName = @"test_adding_threadsafe";
  458. int itemCount = 100;
  459. int itemsPerThread = 10;
  460. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  461. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  462. // Have 100 threads to add 100 unique keys and values into the dictionary.
  463. for (int threadNum = 0; threadNum < 10; threadNum++) {
  464. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  465. args.userDefaults = userDefaults;
  466. args.dictionary = dictionary;
  467. args.itemsPerThread = itemsPerThread;
  468. args.index = threadNum;
  469. [NSThread detachNewThreadSelector:@selector(addObjectsThread:) toTarget:self withObject:args];
  470. }
  471. // Verify the size of the dictionary.
  472. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == %d", itemCount];
  473. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  474. evaluatedWithObject:dictionary
  475. handler:nil];
  476. [self waitForExpectations:@[expectation] timeout:kGULTestCaseTimeoutInterval];
  477. for (int i = 0; i < itemCount; i++) {
  478. NSString *key = [NSString stringWithFormat:@"%d", i];
  479. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  480. }
  481. [userDefaults clearAllData];
  482. [self removePreferenceFileWithSuiteName:suiteName];
  483. }
  484. - (void)testNewUserDefaultsRemovingObjects {
  485. NSString *suiteName = @"test_removing_threadsafe";
  486. int itemCount = 100;
  487. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  488. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  489. // Create a dictionary of 100 unique keys and values.
  490. for (int i = 0; i < itemCount; i++) {
  491. NSString *key = [NSString stringWithFormat:@"%d", i];
  492. [userDefaults setObject:@(i) forKey:key];
  493. dictionary[key] = @(i);
  494. }
  495. XCTAssertEqual(dictionary.count, 100);
  496. // Spawn 10 threads to remove all items inside the dictionary.
  497. int itemsPerThread = 100;
  498. for (int threadNum = 0; threadNum < 10; threadNum++) {
  499. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  500. args.userDefaults = userDefaults;
  501. args.dictionary = dictionary;
  502. args.itemsPerThread = itemsPerThread;
  503. args.index = threadNum;
  504. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  505. toTarget:self
  506. withObject:args];
  507. }
  508. // Ensure the dictionary is empty after removing objects.
  509. NSPredicate *emptyDictionary = [NSPredicate predicateWithFormat:@"count == 0"];
  510. XCTestExpectation *expectation = [self expectationForPredicate:emptyDictionary
  511. evaluatedWithObject:dictionary
  512. handler:nil];
  513. [self waitForExpectations:@[expectation] timeout:kGULTestCaseTimeoutInterval];
  514. for (int i = 0; i < itemCount; i++) {
  515. NSString *key = [NSString stringWithFormat:@"%d", i];
  516. XCTAssertNil([userDefaults objectForKey:key]);
  517. }
  518. [userDefaults clearAllData];
  519. [self removePreferenceFileWithSuiteName:suiteName];
  520. }
  521. - (void)testNewUserDefaultsRemovingSomeObjects {
  522. NSString *suiteName = @"test_remove_some_objs";
  523. int itemCount = 200;
  524. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  525. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  526. // Create a dictionary of 100 unique keys and values.
  527. for (int i = 0; i < itemCount; i++) {
  528. NSString *key = [NSString stringWithFormat:@"%d", i];
  529. [userDefaults setObject:@(i) forKey:key];
  530. dictionary[key] = @(i);
  531. }
  532. // Spawn 10 threads to remove the first 100 items inside the dictionary.
  533. int itemsPerThread = 10;
  534. for (int threadNum = 0; threadNum < 10; threadNum++) {
  535. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  536. args.userDefaults = userDefaults;
  537. args.dictionary = dictionary;
  538. args.itemsPerThread = itemsPerThread;
  539. args.index = threadNum;
  540. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  541. toTarget:self
  542. withObject:args];
  543. }
  544. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == 100"];
  545. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  546. evaluatedWithObject:dictionary
  547. handler:nil];
  548. [self waitForExpectations:@[expectation] timeout:kGULTestCaseTimeoutInterval];
  549. // Check the remaining of the user defaults.
  550. for (int i = 0; i < itemCount; i++) {
  551. NSString *key = [NSString stringWithFormat:@"%d", i];
  552. if (i < 100) {
  553. XCTAssertNil([userDefaults objectForKey:key]);
  554. } else {
  555. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  556. }
  557. }
  558. [userDefaults clearAllData];
  559. [self removePreferenceFileWithSuiteName:suiteName];
  560. }
  561. - (void)testBothUserDefaultsThreadSafeAddingObjects {
  562. NSString *suiteName = @"test_adding_both_user_defaults_threadsafe";
  563. int itemCount = 100;
  564. int itemsPerThread = 10;
  565. GULUserDefaults *newUserDefaults = [[GULUserDefaults alloc] initWithSuiteName:@"testing"];
  566. NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"testing"];
  567. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  568. // Have 100 threads to add 100 unique keys and values into the dictionary.
  569. for (int threadNum = 0; threadNum < 10; threadNum++) {
  570. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  571. args.userDefaults = newUserDefaults;
  572. args.oldUserDefaults = userDefaults;
  573. args.dictionary = dictionary;
  574. args.itemsPerThread = itemsPerThread;
  575. args.index = threadNum;
  576. [NSThread detachNewThreadSelector:@selector(addObjectsBothUserDefaultsThread:)
  577. toTarget:self
  578. withObject:args];
  579. }
  580. // Verify the size of the dictionary.
  581. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == %d", itemCount];
  582. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  583. evaluatedWithObject:dictionary
  584. handler:nil];
  585. [self waitForExpectations:@[expectation] timeout:kGULTestCaseTimeoutInterval];
  586. for (int i = 0; i < itemCount; i++) {
  587. NSString *key = [NSString stringWithFormat:@"%d", i];
  588. if (i % 2 == 0) {
  589. XCTAssertEqualObjects([newUserDefaults objectForKey:key], @(i));
  590. } else {
  591. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  592. }
  593. }
  594. [newUserDefaults clearAllData];
  595. [self removePreferenceFileWithSuiteName:suiteName];
  596. }
  597. - (void)testBothUserDefaultsRemovingSomeObjects {
  598. NSString *suiteName = @"test_remove_some_objs_both_user_defaults";
  599. int itemCount = 200;
  600. GULUserDefaults *userDefaults = [[GULUserDefaults alloc] initWithSuiteName:suiteName];
  601. NSUserDefaults *oldUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:suiteName];
  602. GULMutableDictionary *dictionary = [[GULMutableDictionary alloc] init];
  603. // Create a dictionary of 100 unique keys and values.
  604. for (int i = 0; i < itemCount; i++) {
  605. NSString *key = [NSString stringWithFormat:@"%d", i];
  606. [userDefaults setObject:@(i) forKey:key];
  607. dictionary[key] = @(i);
  608. }
  609. // Spawn 10 threads to remove the first 100 items inside the dictionary.
  610. int itemsPerThread = 10;
  611. for (int threadNum = 0; threadNum < 10; threadNum++) {
  612. GULUserDefaultsThreadArgs *args = [[GULUserDefaultsThreadArgs alloc] init];
  613. args.userDefaults = userDefaults;
  614. args.oldUserDefaults = oldUserDefaults;
  615. args.dictionary = dictionary;
  616. args.itemsPerThread = itemsPerThread;
  617. args.index = threadNum;
  618. [NSThread detachNewThreadSelector:@selector(removeObjectsThread:)
  619. toTarget:self
  620. withObject:args];
  621. }
  622. // Verify the size of the dictionary.
  623. NSPredicate *dictionarySize = [NSPredicate predicateWithFormat:@"count == 100"];
  624. XCTestExpectation *expectation = [self expectationForPredicate:dictionarySize
  625. evaluatedWithObject:dictionary
  626. handler:nil];
  627. [self waitForExpectations:@[expectation] timeout:kGULTestCaseTimeoutInterval];
  628. // Check the remaining of the user defaults.
  629. for (int i = 0; i < itemCount; i++) {
  630. NSString *key = [NSString stringWithFormat:@"%d", i];
  631. if (i < 100) {
  632. if (i % 2 == 0) {
  633. XCTAssertNil([userDefaults objectForKey:key]);
  634. } else {
  635. XCTAssertNil([oldUserDefaults objectForKey:key]);
  636. }
  637. } else {
  638. if (i % 2 == 0) {
  639. XCTAssertEqualObjects([userDefaults objectForKey:key], @(i));
  640. } else {
  641. XCTAssertEqualObjects([oldUserDefaults objectForKey:key], @(i));
  642. }
  643. }
  644. }
  645. [userDefaults clearAllData];
  646. [self removePreferenceFileWithSuiteName:suiteName];
  647. }
  648. #pragma mark - Thread methods
  649. /// Add objects into the current GULUserDefaults given arguments.
  650. - (void)addObjectsThread:(GULUserDefaultsThreadArgs *)args {
  651. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  652. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  653. NSString *key = [NSString stringWithFormat:@"%d", i];
  654. [args.userDefaults setObject:@(i) forKey:key];
  655. args.dictionary[key] = @(i);
  656. }
  657. }
  658. /// Remove objects from the current GULUserDefaults given arguments.
  659. - (void)removeObjectsThread:(GULUserDefaultsThreadArgs *)args {
  660. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  661. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  662. NSString *key = [NSString stringWithFormat:@"%d", i];
  663. [args.userDefaults removeObjectForKey:key];
  664. [args.dictionary removeObjectForKey:key];
  665. }
  666. }
  667. /// Add objects into both user defaults given arguments.
  668. - (void)addObjectsBothUserDefaultsThread:(GULUserDefaultsThreadArgs *)args {
  669. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  670. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  671. NSString *key = [NSString stringWithFormat:@"%d", i];
  672. if (i % 2 == 0) {
  673. [args.userDefaults setObject:@(i) forKey:key];
  674. } else {
  675. [args.oldUserDefaults setObject:@(i) forKey:key];
  676. }
  677. args.dictionary[key] = @(i);
  678. }
  679. }
  680. /// Remove objects from both user defaults given arguments.
  681. - (void)removeObjectsFromBothUserDefaultsThread:(GULUserDefaultsThreadArgs *)args {
  682. int totalItemsPerThread = args.itemsPerThread + args.itemsPerThread * args.index;
  683. for (int i = args.index * args.itemsPerThread; i < totalItemsPerThread; i++) {
  684. NSString *key = [NSString stringWithFormat:@"%d", i];
  685. if (i % 2 == 0) {
  686. [args.userDefaults removeObjectForKey:key];
  687. } else {
  688. [args.oldUserDefaults removeObjectForKey:key];
  689. }
  690. [args.dictionary removeObjectForKey:key];
  691. }
  692. }
  693. #pragma mark - Helper
  694. - (NSString *)filePathForPreferencesName:(NSString *)preferencesName {
  695. if (!preferencesName.length) {
  696. return @"";
  697. }
  698. // User Defaults exist in the Library directory, get the path to use it as a prefix.
  699. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
  700. if (!paths.lastObject) {
  701. XCTFail(@"Library directory not found - NSSearchPath results are empty.");
  702. }
  703. NSArray *components = @[
  704. paths.lastObject,
  705. @"Preferences",
  706. [preferencesName stringByAppendingPathExtension:@"plist"]
  707. ];
  708. return [NSString pathWithComponents:components];
  709. }
  710. - (void)removePreferenceFileWithSuiteName:(NSString *)suiteName {
  711. NSString *path = [self filePathForPreferencesName:suiteName];
  712. NSFileManager *fileManager = [NSFileManager defaultManager];
  713. if ([fileManager fileExistsAtPath:path]) {
  714. XCTAssertTrue([fileManager removeItemAtPath:path error:NULL]);
  715. }
  716. }
  717. @end