FSTFieldValueTests.mm 24 KB

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