FSTFieldValueTests.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Model/FSTFieldValue.h"
  17. #import <XCTest/XCTest.h>
  18. #import "FirebaseFirestore/FIRGeoPoint.h"
  19. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  20. #import "Firestore/Source/API/FSTUserDataConverter.h"
  21. #import "Firestore/Source/Core/FSTTimestamp.h"
  22. #import "Firestore/Source/Model/FSTDatabaseID.h"
  23. #import "Firestore/Source/Model/FSTFieldValue.h"
  24. #import "Firestore/Source/Model/FSTPath.h"
  25. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  26. /** Helper to wrap the values in a set of equality groups using FSTTestFieldValue(). */
  27. NSArray *FSTWrapGroups(NSArray *groups) {
  28. NSMutableArray *wrapped = [NSMutableArray array];
  29. for (NSArray<id> *group in groups) {
  30. NSMutableArray *wrappedGroup = [NSMutableArray array];
  31. for (id value in group) {
  32. FSTFieldValue *wrappedValue;
  33. // Server Timestamp values can't be parsed directly, so we have a couple predefined sentinel
  34. // strings that can be used instead.
  35. if ([value isEqual:@"server-timestamp-1"]) {
  36. wrappedValue = [FSTServerTimestampValue
  37. serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 5, 20, 10, 20, 0)];
  38. } else if ([value isEqual:@"server-timestamp-2"]) {
  39. wrappedValue = [FSTServerTimestampValue
  40. serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 10, 21, 15, 32, 0)];
  41. } else if ([value isKindOfClass:[FSTDocumentKeyReference class]]) {
  42. // We directly convert these here so that the databaseIDs can be different.
  43. FSTDocumentKeyReference *reference = (FSTDocumentKeyReference *)value;
  44. wrappedValue =
  45. [FSTReferenceValue referenceValue:reference.key databaseID:reference.databaseID];
  46. } else {
  47. wrappedValue = FSTTestFieldValue(value);
  48. }
  49. [wrappedGroup addObject:wrappedValue];
  50. }
  51. [wrapped addObject:wrappedGroup];
  52. }
  53. return wrapped;
  54. }
  55. @interface FSTFieldValueTests : XCTestCase
  56. @end
  57. @implementation FSTFieldValueTests {
  58. NSDate *date1;
  59. NSDate *date2;
  60. }
  61. - (void)setUp {
  62. [super setUp];
  63. // Create a couple date objects for use in tests.
  64. date1 = FSTTestDate(2016, 5, 20, 10, 20, 0);
  65. date2 = FSTTestDate(2016, 10, 21, 15, 32, 0);
  66. }
  67. - (void)testWrapIntegers {
  68. NSArray *values = @[
  69. @(INT_MIN), @(-1), @0, @1, @2, @(UCHAR_MAX), @(INT_MAX), // Standard integers
  70. @(LONG_MIN), @(LONG_MAX), @(LLONG_MIN), @(LLONG_MAX) // Larger values
  71. ];
  72. for (id value in values) {
  73. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  74. XCTAssertEqualObjects([wrapped class], [FSTIntegerValue class]);
  75. XCTAssertEqualObjects([wrapped value], @([value longLongValue]));
  76. }
  77. }
  78. - (void)testWrapsDoubles {
  79. // Note that 0x1.0p-1074 is a hex floating point literal representing the minimum subnormal
  80. // number: <https://en.wikipedia.org/wiki/Denormal_number>.
  81. NSArray *values = @[
  82. @(-INFINITY), @(-DBL_MAX), @(LLONG_MIN * -1.0), @(-1.1), @(-0x1.0p-1074), @(-0.0), @(0.0),
  83. @(0x1.0p-1074), @(DBL_MIN), @(1.1), @(LLONG_MAX * 1.0), @(DBL_MAX), @(INFINITY)
  84. ];
  85. for (id value in values) {
  86. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  87. XCTAssertEqualObjects([wrapped class], [FSTDoubleValue class]);
  88. XCTAssertEqualObjects([wrapped value], value);
  89. }
  90. }
  91. - (void)testWrapsNilAndNSNull {
  92. FSTNullValue *nullValue = [FSTNullValue nullValue];
  93. XCTAssertEqual(FSTTestFieldValue(nil), nullValue);
  94. XCTAssertEqual(FSTTestFieldValue([NSNull null]), nullValue);
  95. XCTAssertEqual([nullValue value], [NSNull null]);
  96. }
  97. - (void)testWrapsBooleans {
  98. NSArray *values = @[ @YES, @NO, [NSNumber numberWithChar:1], [NSNumber numberWithChar:0] ];
  99. for (id value in values) {
  100. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  101. XCTAssertEqualObjects([wrapped class], [FSTBooleanValue class]);
  102. XCTAssertEqualObjects([wrapped value], value);
  103. }
  104. // Unsigned chars could conceivably be handled consistently with signed chars but on arm64 these
  105. // end up being stored as signed shorts.
  106. FSTFieldValue *wrapped = FSTTestFieldValue([NSNumber numberWithUnsignedChar:1]);
  107. XCTAssertEqualObjects(wrapped, [FSTIntegerValue integerValue:1]);
  108. }
  109. union DoubleBits {
  110. double d;
  111. uint64_t bits;
  112. };
  113. - (void)testNormalizesNaNs {
  114. // NOTE: With v1beta1 query semantics, it's no longer as important that our NaN representation
  115. // matches the backend, since all NaNs are defined to sort as equal, but we preserve the
  116. // normalization and this test regardless for now.
  117. // We use a canonical NaN bit pattern that's common for both Java and Objective-C. Specifically:
  118. // - sign: 0
  119. // - exponent: 11 bits, all 1
  120. // - significand: 52 bits, MSB=1, rest=0
  121. //
  122. // This matches the Firestore backend which uses Java's Double.doubleToLongBits which is defined
  123. // to normalize all NaNs to this value.
  124. union DoubleBits canonical = {.bits = 0x7ff8000000000000ULL};
  125. // IEEE 754 specifies that NaN isn't equal to itself.
  126. XCTAssertTrue(isnan(canonical.d));
  127. XCTAssertEqual(canonical.bits, canonical.bits);
  128. XCTAssertNotEqual(canonical.d, canonical.d);
  129. // All permutations of the 51 other non-MSB significand bits are also NaNs.
  130. union DoubleBits alternate = {.bits = 0x7fff000000000000ULL};
  131. XCTAssertTrue(isnan(alternate.d));
  132. XCTAssertNotEqual(alternate.bits, canonical.bits);
  133. XCTAssertNotEqual(alternate.d, canonical.d);
  134. // Even though at the C-level assignment preserves non-canonical NaNs, NSNumber normalizes all
  135. // NaNs to single shared instance, kCFNumberNaN. That NaN has no public definition for its value
  136. // but it happens to match what we need.
  137. union DoubleBits normalized = {.d = [[NSNumber numberWithDouble:alternate.d] doubleValue]};
  138. XCTAssertEqual(normalized.bits, canonical.bits);
  139. // Ensure we get the same normalization behavior (currently implemented explicitly by checking
  140. // for isnan() and then explicitly assigning NAN).
  141. union DoubleBits result;
  142. result.d = [[FSTDoubleValue doubleValue:canonical.d] internalValue];
  143. XCTAssertEqual(result.bits, canonical.bits);
  144. result.d = [[FSTDoubleValue doubleValue:alternate.d] internalValue];
  145. XCTAssertEqual(result.bits, canonical.bits);
  146. // A NaN that's canonical except it has the sign bit set (would be negative if signs mattered)
  147. union DoubleBits negative = {.bits = 0xfff8000000000000ULL};
  148. result.d = [[FSTDoubleValue doubleValue:negative.d] internalValue];
  149. XCTAssertTrue(isnan(negative.d));
  150. XCTAssertEqual(result.bits, canonical.bits);
  151. // A signaling NaN with significand where MSB is 0, and some non-MSB bit is one.
  152. union DoubleBits signaling = {.bits = 0xfff4000000000000ULL};
  153. XCTAssertTrue(isnan(signaling.d));
  154. result.d = [[FSTDoubleValue doubleValue:signaling.d] internalValue];
  155. XCTAssertEqual(result.bits, canonical.bits);
  156. }
  157. - (void)testZeros {
  158. // Floating point numbers have an explicit sign bit so it's possible to end up with negative
  159. // zero as a distinct value from positive zero.
  160. union DoubleBits zero = {.d = 0.0};
  161. union DoubleBits negativeZero = {.d = -0.0};
  162. // IEEE 754 requires these two zeros to compare equal.
  163. XCTAssertNotEqual(zero.bits, negativeZero.bits);
  164. XCTAssertEqual(zero.d, negativeZero.d);
  165. // NSNumber preserves the negative zero value but compares equal according to IEEE 754.
  166. union DoubleBits normalized = {.d = [[NSNumber numberWithDouble:negativeZero.d] doubleValue]};
  167. XCTAssertEqual(normalized.bits, negativeZero.bits);
  168. XCTAssertEqualObjects([NSNumber numberWithDouble:0.0], [NSNumber numberWithDouble:-0.0]);
  169. // FSTDoubleValue preserves positive/negative zero
  170. union DoubleBits result;
  171. result.d = [[[FSTDoubleValue doubleValue:zero.d] value] doubleValue];
  172. XCTAssertEqual(result.bits, zero.bits);
  173. result.d = [[[FSTDoubleValue doubleValue:negativeZero.d] value] doubleValue];
  174. XCTAssertEqual(result.bits, negativeZero.bits);
  175. // ... but compares positive/negative zero as unequal, compatibly with Firestore.
  176. XCTAssertNotEqualObjects([FSTDoubleValue doubleValue:0.0], [FSTDoubleValue doubleValue:-0.0]);
  177. }
  178. - (void)testWrapStrings {
  179. NSArray *values = @[ @"", @"abc" ];
  180. for (id value in values) {
  181. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  182. XCTAssertEqualObjects([wrapped class], [FSTStringValue class]);
  183. XCTAssertEqualObjects([wrapped value], value);
  184. }
  185. }
  186. - (void)testWrapDates {
  187. NSArray *values = @[ FSTTestDate(1900, 12, 1, 1, 20, 30), FSTTestDate(2017, 4, 24, 13, 20, 30) ];
  188. for (id value in values) {
  189. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  190. XCTAssertEqualObjects([wrapped class], [FSTTimestampValue class]);
  191. XCTAssertEqualObjects([wrapped value], value);
  192. XCTAssertEqualObjects(((FSTTimestampValue *)wrapped).internalValue,
  193. [FSTTimestamp timestampWithDate:value]);
  194. }
  195. }
  196. - (void)testWrapGeoPoints {
  197. NSArray *values = @[ FSTTestGeoPoint(1.24, 4.56), FSTTestGeoPoint(-20, 100) ];
  198. for (id value in values) {
  199. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  200. XCTAssertEqualObjects([wrapped class], [FSTGeoPointValue class]);
  201. XCTAssertEqualObjects([wrapped value], value);
  202. }
  203. }
  204. - (void)testWrapBlobs {
  205. NSArray *values = @[ FSTTestData(1, 2, 3), FSTTestData(1, 2) ];
  206. for (id value in values) {
  207. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  208. XCTAssertEqualObjects([wrapped class], [FSTBlobValue class]);
  209. XCTAssertEqualObjects([wrapped value], value);
  210. }
  211. }
  212. - (void)testWrapResourceNames {
  213. NSArray *values = @[
  214. FSTTestRef(@"project", kDefaultDatabaseID, @"foo/bar"),
  215. FSTTestRef(@"project", kDefaultDatabaseID, @"foo/baz")
  216. ];
  217. for (FSTDocumentKeyReference *value in values) {
  218. FSTFieldValue *wrapped = FSTTestFieldValue(value);
  219. XCTAssertEqualObjects([wrapped class], [FSTReferenceValue class]);
  220. XCTAssertEqualObjects([wrapped value], value.key);
  221. XCTAssertEqualObjects(((FSTDatabaseID *)wrapped).databaseID, value.databaseID);
  222. }
  223. }
  224. - (void)testWrapsEmptyObjects {
  225. XCTAssertEqualObjects(FSTTestFieldValue(@{}), [FSTObjectValue objectValue]);
  226. }
  227. - (void)testWrapsSimpleObjects {
  228. FSTObjectValue *actual = FSTTestObjectValue(
  229. @{ @"a" : @"foo",
  230. @"b" : @(1L),
  231. @"c" : @YES,
  232. @"d" : [NSNull null] });
  233. FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{
  234. @"a" : [FSTStringValue stringValue:@"foo"],
  235. @"b" : [FSTIntegerValue integerValue:1LL],
  236. @"c" : [FSTBooleanValue trueValue],
  237. @"d" : [FSTNullValue nullValue]
  238. }];
  239. XCTAssertEqualObjects(actual, expected);
  240. }
  241. - (void)testWrapsNestedObjects {
  242. FSTObjectValue *actual = FSTTestObjectValue(@{ @"a" : @{@"b" : @{@"c" : @"foo"}, @"d" : @YES} });
  243. FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{
  244. @"a" : [[FSTObjectValue alloc] initWithDictionary:@{
  245. @"b" :
  246. [[FSTObjectValue alloc] initWithDictionary:@{@"c" : [FSTStringValue stringValue:@"foo"]}],
  247. @"d" : [FSTBooleanValue booleanValue:YES]
  248. }]
  249. }];
  250. XCTAssertEqualObjects(actual, expected);
  251. }
  252. - (void)testExtractsFields {
  253. FSTObjectValue *obj = FSTTestObjectValue(@{ @"foo" : @{@"a" : @YES, @"b" : @"string"} });
  254. FSTAssertIsKindOfClass(obj, FSTObjectValue);
  255. FSTAssertIsKindOfClass([obj valueForPath:FSTTestFieldPath(@"foo")], FSTObjectValue);
  256. XCTAssertEqualObjects([obj valueForPath:FSTTestFieldPath(@"foo.a")], [FSTBooleanValue trueValue]);
  257. XCTAssertEqualObjects([obj valueForPath:FSTTestFieldPath(@"foo.b")],
  258. [FSTStringValue stringValue:@"string"]);
  259. XCTAssertNil([obj valueForPath:FSTTestFieldPath(@"foo.a.b")]);
  260. XCTAssertNil([obj valueForPath:FSTTestFieldPath(@"bar")]);
  261. XCTAssertNil([obj valueForPath:FSTTestFieldPath(@"bar.a")]);
  262. }
  263. - (void)testOverwritesExistingFields {
  264. FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @"old"});
  265. FSTObjectValue *mod =
  266. [old objectBySettingValue:FSTTestFieldValue(@"mod") forPath:FSTTestFieldPath(@"a")];
  267. // Should return a new object, leaving the old one unmodified.
  268. XCTAssertNotEqual(old, mod);
  269. XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"old"}));
  270. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @"mod"}));
  271. }
  272. - (void)testAddsNewFields {
  273. FSTObjectValue *empty = [FSTObjectValue objectValue];
  274. FSTObjectValue *mod =
  275. [empty objectBySettingValue:FSTTestFieldValue(@"mod") forPath:FSTTestFieldPath(@"a")];
  276. XCTAssertNotEqual(empty, mod);
  277. XCTAssertEqualObjects(empty, FSTTestFieldValue(@{}));
  278. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @"mod"}));
  279. FSTObjectValue *old = mod;
  280. mod = [old objectBySettingValue:FSTTestFieldValue(@1) forPath:FSTTestFieldPath(@"b")];
  281. XCTAssertNotEqual(old, mod);
  282. XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"mod"}));
  283. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @"mod", @"b" : @1 }));
  284. }
  285. - (void)testImplicitlyCreatesObjects {
  286. FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @"old"});
  287. FSTObjectValue *mod =
  288. [old objectBySettingValue:FSTTestFieldValue(@"mod") forPath:FSTTestFieldPath(@"b.c.d")];
  289. XCTAssertNotEqual(old, mod);
  290. XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"old"}));
  291. XCTAssertEqualObjects(mod, FSTTestFieldValue(
  292. @{ @"a" : @"old",
  293. @"b" : @{@"c" : @{@"d" : @"mod"}} }));
  294. }
  295. - (void)testCanOverwritePrimitivesWithObjects {
  296. FSTObjectValue *old = FSTTestObjectValue(@{ @"a" : @{@"b" : @"old"} });
  297. FSTObjectValue *mod =
  298. [old objectBySettingValue:FSTTestFieldValue(@{@"b" : @"mod"}) forPath:FSTTestFieldPath(@"a")];
  299. XCTAssertNotEqual(old, mod);
  300. XCTAssertEqualObjects(old, FSTTestFieldValue(@{ @"a" : @{@"b" : @"old"} }));
  301. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @"mod"} }));
  302. }
  303. - (void)testAddsToNestedObjects {
  304. FSTObjectValue *old = FSTTestObjectValue(@{ @"a" : @{@"b" : @"old"} });
  305. FSTObjectValue *mod =
  306. [old objectBySettingValue:FSTTestFieldValue(@"mod") forPath:FSTTestFieldPath(@"a.c")];
  307. XCTAssertNotEqual(old, mod);
  308. XCTAssertEqualObjects(old, FSTTestFieldValue(@{ @"a" : @{@"b" : @"old"} }));
  309. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @"old", @"c" : @"mod"} }));
  310. }
  311. - (void)testDeletesKeys {
  312. FSTObjectValue *old = FSTTestObjectValue(@{ @"a" : @1, @"b" : @2 });
  313. FSTObjectValue *mod = [old objectByDeletingPath:FSTTestFieldPath(@"a")];
  314. XCTAssertNotEqual(old, mod);
  315. XCTAssertEqualObjects(old, FSTTestFieldValue(@{ @"a" : @1, @"b" : @2 }));
  316. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"b" : @2 }));
  317. FSTObjectValue *empty = [mod objectByDeletingPath:FSTTestFieldPath(@"b")];
  318. XCTAssertNotEqual(mod, empty);
  319. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"b" : @2 }));
  320. XCTAssertEqualObjects(empty, FSTTestFieldValue(@{}));
  321. }
  322. - (void)testDeletesHandleMissingKeys {
  323. FSTObjectValue *old = FSTTestObjectValue(@{ @"a" : @{@"b" : @1, @"c" : @2} });
  324. FSTObjectValue *mod = [old objectByDeletingPath:FSTTestFieldPath(@"b")];
  325. XCTAssertEqualObjects(old, mod);
  326. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @1, @"c" : @2} }));
  327. mod = [old objectByDeletingPath:FSTTestFieldPath(@"a.d")];
  328. XCTAssertEqualObjects(old, mod);
  329. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @1, @"c" : @2} }));
  330. mod = [old objectByDeletingPath:FSTTestFieldPath(@"a.b.c")];
  331. XCTAssertEqualObjects(old, mod);
  332. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @1, @"c" : @2} }));
  333. }
  334. - (void)testDeletesNestedKeys {
  335. FSTObjectValue *old = FSTTestObjectValue(
  336. @{ @"a" : @{@"b" : @1, @"c" : @{@"d" : @2, @"e" : @3}} });
  337. FSTObjectValue *mod = [old objectByDeletingPath:FSTTestFieldPath(@"a.c.d")];
  338. XCTAssertNotEqual(old, mod);
  339. XCTAssertEqualObjects(old, FSTTestFieldValue(
  340. @{ @"a" : @{@"b" : @1, @"c" : @{@"d" : @2, @"e" : @3}} }));
  341. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @1, @"c" : @{@"e" : @3}} }));
  342. old = mod;
  343. mod = [old objectByDeletingPath:FSTTestFieldPath(@"a.c")];
  344. XCTAssertEqualObjects(old, FSTTestFieldValue(@{ @"a" : @{@"b" : @1, @"c" : @{@"e" : @3}} }));
  345. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{ @"a" : @{@"b" : @1} }));
  346. old = mod;
  347. mod = [old objectByDeletingPath:FSTTestFieldPath(@"a")];
  348. XCTAssertEqualObjects(old, FSTTestFieldValue(@{ @"a" : @{@"b" : @1} }));
  349. XCTAssertEqualObjects(mod, FSTTestFieldValue(@{}));
  350. }
  351. - (void)testArrays {
  352. FSTArrayValue *expected = [[FSTArrayValue alloc]
  353. initWithValueNoCopy:@[ [FSTStringValue stringValue:@"value"], [FSTBooleanValue trueValue] ]];
  354. FSTArrayValue *actual = (FSTArrayValue *)FSTTestFieldValue(@[ @"value", @YES ]);
  355. XCTAssertEqualObjects(actual, expected);
  356. }
  357. - (void)testValueEquality {
  358. NSArray *groups = @[
  359. @[ FSTTestFieldValue(@YES), [FSTBooleanValue booleanValue:YES] ],
  360. @[ FSTTestFieldValue(@NO), [FSTBooleanValue booleanValue:NO] ],
  361. @[ FSTTestFieldValue([NSNull null]), [FSTNullValue nullValue] ],
  362. @[ FSTTestFieldValue(@(0.0 / 0.0)), FSTTestFieldValue(@(NAN)), [FSTDoubleValue nanValue] ],
  363. // -0.0 and 0.0 compare: the same (but are not isEqual:)
  364. @[ FSTTestFieldValue(@(-0.0)) ], @[ FSTTestFieldValue(@0.0) ],
  365. @[ FSTTestFieldValue(@1), FSTTestFieldValue(@1LL), [FSTIntegerValue integerValue:1LL] ],
  366. // double and unit64_t values can compare: the same (but won't be isEqual:)
  367. @[ FSTTestFieldValue(@1.0), [FSTDoubleValue doubleValue:1.0] ],
  368. @[ FSTTestFieldValue(@1.1), [FSTDoubleValue doubleValue:1.1] ],
  369. @[
  370. FSTTestFieldValue(FSTTestData(0, 1, 2, -1)), [FSTBlobValue blobValue:FSTTestData(0, 1, 2, -1)]
  371. ],
  372. @[ FSTTestFieldValue(FSTTestData(0, 1, -1)) ],
  373. @[ FSTTestFieldValue(@"string"), [FSTStringValue stringValue:@"string"] ],
  374. @[ FSTTestFieldValue(@"strin") ],
  375. @[ FSTTestFieldValue(@"e\u0301b") ], // latin small letter e + combining acute accent
  376. @[ FSTTestFieldValue(@"\u00e9a") ], // latin small letter e with acute accent
  377. @[
  378. FSTTestFieldValue(date1),
  379. [FSTTimestampValue timestampValue:[FSTTimestamp timestampWithDate:date1]]
  380. ],
  381. @[ FSTTestFieldValue(date2) ],
  382. @[
  383. // NOTE: ServerTimestampValues can't be parsed via FSTTestFieldValue().
  384. [FSTServerTimestampValue
  385. serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]],
  386. [FSTServerTimestampValue
  387. serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date1]]
  388. ],
  389. @[ [FSTServerTimestampValue
  390. serverTimestampValueWithLocalWriteTime:[FSTTimestamp timestampWithDate:date2]] ],
  391. @[
  392. FSTTestFieldValue(FSTTestGeoPoint(0, 1)),
  393. [FSTGeoPointValue geoPointValue:FSTTestGeoPoint(0, 1)]
  394. ],
  395. @[ FSTTestFieldValue(FSTTestGeoPoint(1, 0)) ],
  396. @[
  397. [FSTReferenceValue referenceValue:FSTTestDocKey(@"coll/doc1")
  398. databaseID:[FSTDatabaseID databaseIDWithProject:@"project"
  399. database:kDefaultDatabaseID]],
  400. FSTTestFieldValue(FSTTestRef(@"project", kDefaultDatabaseID, @"coll/doc1"))
  401. ],
  402. @[ FSTTestRef(@"project", @"(default)", @"coll/doc2") ],
  403. @[ FSTTestFieldValue(@[ @"foo", @"bar" ]), FSTTestFieldValue(@[ @"foo", @"bar" ]) ],
  404. @[ FSTTestFieldValue(@[ @"foo", @"bar", @"baz" ]) ], @[ FSTTestFieldValue(@[ @"foo" ]) ],
  405. @[
  406. FSTTestFieldValue(
  407. @{ @"bar" : @1,
  408. @"foo" : @2 }),
  409. FSTTestFieldValue(
  410. @{ @"foo" : @2,
  411. @"bar" : @1 })
  412. ],
  413. @[ FSTTestFieldValue(
  414. @{ @"bar" : @2,
  415. @"foo" : @1 }) ],
  416. @[ FSTTestFieldValue(
  417. @{ @"bar" : @1,
  418. @"foo" : @1 }) ],
  419. @[ FSTTestFieldValue(
  420. @{ @"foo" : @1 }) ]
  421. ];
  422. FSTAssertEqualityGroups(groups);
  423. }
  424. - (void)testValueOrdering {
  425. NSArray *groups = @[
  426. // null first
  427. @[ [NSNull null] ],
  428. // booleans
  429. @[ @NO ], @[ @YES ],
  430. // numbers
  431. @[ @(0.0 / 0.0) ], @[ @(-INFINITY) ], @[ @(-DBL_MAX) ], @[ @(LLONG_MIN) ], @[ @(-1.1) ],
  432. @[ @(-1.0), @(-1LL) ], // longs and doubles compare the same
  433. @[ @(-DBL_MIN) ],
  434. @[ @(-0x1.0p-1074) ], // negative smallest subnormal
  435. @[ @(-0.0), @(0.0), @(0LL) ], // zeros all compare the same
  436. @[ @(0x1.0p-1074) ], // positive smallest subnormal
  437. @[ @(DBL_MIN) ], @[ @1.0, @1LL ], // longs and doubles compare the same
  438. @[ @1.1 ], @[ @(LLONG_MAX) ], @[ @(DBL_MAX) ], @[ @(INFINITY) ],
  439. // timestamps
  440. @[ date1 ], @[ date2 ],
  441. // server timestamps come after all concrete timestamps.
  442. // NOTE: server timestamps can't be parsed directly, so we have special sentinel strings (see
  443. // FSTWrapGroups()).
  444. @[ @"server-timestamp-1" ], @[ @"server-timestamp-2" ],
  445. // strings
  446. @[ @"" ], @[ @"\000\ud7ff\ue000\uffff" ], @[ @"(╯°□°)╯︵ ┻━┻" ], @[ @"a" ], @[ @"abc def" ],
  447. @[ @"e\u0301b" ], // latin small letter e + combining acute accent + latin small letter b
  448. @[ @"æ" ],
  449. @[ @"\u00e9a" ], // latin small letter e with acute accent + latin small letter a
  450. // blobs
  451. @[ FSTTestData(-1) ], @[ FSTTestData(0, -1) ], @[ FSTTestData(0, 1, 2, 3, 4, -1) ],
  452. @[ FSTTestData(0, 1, 2, 4, 3, -1) ], @[ FSTTestData(255, -1) ],
  453. // resource names
  454. @[ FSTTestRef(@"p1", @"d1", @"c1/doc1") ], @[ FSTTestRef(@"p1", @"d1", @"c1/doc2") ],
  455. @[ FSTTestRef(@"p1", @"d1", @"c10/doc1") ], @[ FSTTestRef(@"p1", @"d1", @"c2/doc1") ],
  456. @[ FSTTestRef(@"p1", @"d2", @"c1/doc1") ], @[ FSTTestRef(@"p2", @"d1", @"c1/doc1") ],
  457. // Geo points
  458. @[ FSTTestGeoPoint(-90, -180) ], @[ FSTTestGeoPoint(-90, 0) ], @[ FSTTestGeoPoint(-90, 180) ],
  459. @[ FSTTestGeoPoint(0, -180) ], @[ FSTTestGeoPoint(0, 0) ], @[ FSTTestGeoPoint(0, 180) ],
  460. @[ FSTTestGeoPoint(1, -180) ], @[ FSTTestGeoPoint(1, 0) ], @[ FSTTestGeoPoint(1, 180) ],
  461. @[ FSTTestGeoPoint(90, -180) ], @[ FSTTestGeoPoint(90, 0) ], @[ FSTTestGeoPoint(90, 180) ],
  462. // Arrays
  463. @[ @[] ], @[ @[ @"bar" ] ], @[ @[ @"foo" ] ], @[ @[ @"foo", @1 ] ], @[ @[ @"foo", @2 ] ],
  464. @[ @[ @"foo", @"0" ] ],
  465. // Objects
  466. @[
  467. @{ @"bar" : @0 }
  468. ],
  469. @[
  470. @{ @"bar" : @0,
  471. @"foo" : @1 }
  472. ],
  473. @[
  474. @{ @"foo" : @1 }
  475. ],
  476. @[
  477. @{ @"foo" : @2 }
  478. ],
  479. @[ @{@"foo" : @"0"} ]
  480. ];
  481. NSArray *wrapped = FSTWrapGroups(groups);
  482. FSTAssertComparisons(wrapped);
  483. }
  484. - (void)testValue {
  485. NSDate *date = [NSDate date];
  486. id input = @{ @"array" : @[ @1, date ], @"obj" : @{@"date" : date, @"string" : @"hi"} };
  487. FSTObjectValue *value = FSTTestObjectValue(input);
  488. id output = [value value];
  489. {
  490. XCTAssertTrue([output[@"array"][1] isKindOfClass:[NSDate class]]);
  491. NSDate *actual = output[@"array"][1];
  492. XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970,
  493. 0.000000001);
  494. }
  495. {
  496. XCTAssertTrue([output[@"obj"][@"date"] isKindOfClass:[NSDate class]]);
  497. NSDate *actual = output[@"obj"][@"date"];
  498. XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970,
  499. 0.000000001);
  500. }
  501. }
  502. @end