| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- /*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #import "Firestore/Source/Model/FSTFieldValue.h"
- #import <FirebaseFirestore/FIRGeoPoint.h>
- #import <FirebaseFirestore/FIRTimestamp.h>
- #import <XCTest/XCTest.h>
- #import "Firestore/Source/API/FIRFirestore+Internal.h"
- #import "Firestore/Source/API/FSTUserDataConverter.h"
- #import "Firestore/Example/Tests/API/FSTAPIHelpers.h"
- #import "Firestore/Example/Tests/Util/FSTHelpers.h"
- #include "Firestore/core/src/firebase/firestore/model/database_id.h"
- #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
- #include "Firestore/core/test/firebase/firestore/testutil/testutil.h"
- namespace testutil = firebase::firestore::testutil;
- namespace util = firebase::firestore::util;
- using firebase::firestore::model::DatabaseId;
- /** Helper to wrap the values in a set of equality groups using FSTTestFieldValue(). */
- NSArray *FSTWrapGroups(NSArray *groups) {
- NSMutableArray *wrapped = [NSMutableArray array];
- for (NSArray<id> *group in groups) {
- NSMutableArray *wrappedGroup = [NSMutableArray array];
- for (id value in group) {
- FSTFieldValue *wrappedValue;
- // Server Timestamp values can't be parsed directly, so we have a couple predefined sentinel
- // strings that can be used instead.
- if ([value isEqual:@"server-timestamp-1"]) {
- wrappedValue = [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 5, 20, 10, 20, 0)
- previousValue:nil];
- } else if ([value isEqual:@"server-timestamp-2"]) {
- wrappedValue = [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:FSTTestTimestamp(2016, 10, 21, 15, 32, 0)
- previousValue:nil];
- } else if ([value isKindOfClass:[FSTDocumentKeyReference class]]) {
- // We directly convert these here so that the databaseIDs can be different.
- FSTDocumentKeyReference *reference = (FSTDocumentKeyReference *)value;
- wrappedValue = [FSTReferenceValue referenceValue:reference.key
- databaseID:reference.databaseID];
- } else {
- wrappedValue = FSTTestFieldValue(value);
- }
- [wrappedGroup addObject:wrappedValue];
- }
- [wrapped addObject:wrappedGroup];
- }
- return wrapped;
- }
- @interface FSTFieldValueTests : XCTestCase
- @end
- @implementation FSTFieldValueTests {
- NSDate *date1;
- NSDate *date2;
- }
- - (void)setUp {
- [super setUp];
- // Create a couple date objects for use in tests.
- date1 = FSTTestDate(2016, 5, 20, 10, 20, 0);
- date2 = FSTTestDate(2016, 10, 21, 15, 32, 0);
- }
- - (void)testWrapIntegers {
- NSArray *values = @[
- @(INT_MIN), @(-1), @0, @1, @2, @(UCHAR_MAX), @(INT_MAX), // Standard integers
- @(LONG_MIN), @(LONG_MAX), @(LLONG_MIN), @(LLONG_MAX) // Larger values
- ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTIntegerValue class]);
- XCTAssertEqualObjects([wrapped value], @([value longLongValue]));
- }
- }
- - (void)testWrapsDoubles {
- // Note that 0x1.0p-1074 is a hex floating point literal representing the minimum subnormal
- // number: <https://en.wikipedia.org/wiki/Denormal_number>.
- NSArray *values = @[
- @(-INFINITY), @(-DBL_MAX), @(LLONG_MIN * -1.0), @(-1.1), @(-0x1.0p-1074), @(-0.0), @(0.0),
- @(0x1.0p-1074), @(DBL_MIN), @(1.1), @(LLONG_MAX * 1.0), @(DBL_MAX), @(INFINITY)
- ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTDoubleValue class]);
- XCTAssertEqualObjects([wrapped value], value);
- }
- }
- - (void)testWrapsNilAndNSNull {
- FSTNullValue *nullValue = [FSTNullValue nullValue];
- XCTAssertEqual(FSTTestFieldValue(nil), nullValue);
- XCTAssertEqual(FSTTestFieldValue([NSNull null]), nullValue);
- XCTAssertEqual([nullValue value], [NSNull null]);
- }
- - (void)testWrapsBooleans {
- NSArray *values = @[ @YES, @NO, [NSNumber numberWithChar:1], [NSNumber numberWithChar:0] ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTBooleanValue class]);
- XCTAssertEqualObjects([wrapped value], value);
- }
- // Unsigned chars could conceivably be handled consistently with signed chars but on arm64 these
- // end up being stored as signed shorts.
- FSTFieldValue *wrapped = FSTTestFieldValue([NSNumber numberWithUnsignedChar:1]);
- XCTAssertEqualObjects(wrapped, [FSTIntegerValue integerValue:1]);
- }
- union DoubleBits {
- double d;
- uint64_t bits;
- };
- - (void)testNormalizesNaNs {
- // NOTE: With v1 query semantics, it's no longer as important that our NaN representation matches
- // the backend, since all NaNs are defined to sort as equal, but we preserve the normalization and
- // this test regardless for now.
- // We use a canonical NaN bit pattern that's common for both Java and Objective-C. Specifically:
- // - sign: 0
- // - exponent: 11 bits, all 1
- // - significand: 52 bits, MSB=1, rest=0
- //
- // This matches the Firestore backend which uses Java's Double.doubleToLongBits which is defined
- // to normalize all NaNs to this value.
- union DoubleBits canonical = {.bits = 0x7ff8000000000000ULL};
- // IEEE 754 specifies that NaN isn't equal to itself.
- XCTAssertTrue(isnan(canonical.d));
- XCTAssertEqual(canonical.bits, canonical.bits);
- XCTAssertNotEqual(canonical.d, canonical.d);
- // All permutations of the 51 other non-MSB significand bits are also NaNs.
- union DoubleBits alternate = {.bits = 0x7fff000000000000ULL};
- XCTAssertTrue(isnan(alternate.d));
- XCTAssertNotEqual(alternate.bits, canonical.bits);
- XCTAssertNotEqual(alternate.d, canonical.d);
- // Even though at the C-level assignment preserves non-canonical NaNs, NSNumber normalizes all
- // NaNs to single shared instance, kCFNumberNaN. That NaN has no public definition for its value
- // but it happens to match what we need.
- union DoubleBits normalized = {.d = [[NSNumber numberWithDouble:alternate.d] doubleValue]};
- XCTAssertEqual(normalized.bits, canonical.bits);
- // Ensure we get the same normalization behavior (currently implemented explicitly by checking
- // for isnan() and then explicitly assigning NAN).
- union DoubleBits result;
- result.d = [[FSTDoubleValue doubleValue:canonical.d] internalValue];
- XCTAssertEqual(result.bits, canonical.bits);
- result.d = [[FSTDoubleValue doubleValue:alternate.d] internalValue];
- XCTAssertEqual(result.bits, canonical.bits);
- // A NaN that's canonical except it has the sign bit set (would be negative if signs mattered)
- union DoubleBits negative = {.bits = 0xfff8000000000000ULL};
- result.d = [[FSTDoubleValue doubleValue:negative.d] internalValue];
- XCTAssertTrue(isnan(negative.d));
- XCTAssertEqual(result.bits, canonical.bits);
- // A signaling NaN with significand where MSB is 0, and some non-MSB bit is one.
- union DoubleBits signaling = {.bits = 0xfff4000000000000ULL};
- XCTAssertTrue(isnan(signaling.d));
- result.d = [[FSTDoubleValue doubleValue:signaling.d] internalValue];
- XCTAssertEqual(result.bits, canonical.bits);
- }
- - (void)testZeros {
- // Floating point numbers have an explicit sign bit so it's possible to end up with negative
- // zero as a distinct value from positive zero.
- union DoubleBits zero = {.d = 0.0};
- union DoubleBits negativeZero = {.d = -0.0};
- // IEEE 754 requires these two zeros to compare equal.
- XCTAssertNotEqual(zero.bits, negativeZero.bits);
- XCTAssertEqual(zero.d, negativeZero.d);
- // NSNumber preserves the negative zero value but compares equal according to IEEE 754.
- union DoubleBits normalized = {.d = [[NSNumber numberWithDouble:negativeZero.d] doubleValue]};
- XCTAssertEqual(normalized.bits, negativeZero.bits);
- XCTAssertEqualObjects([NSNumber numberWithDouble:0.0], [NSNumber numberWithDouble:-0.0]);
- // FSTDoubleValue preserves positive/negative zero
- union DoubleBits result;
- result.d = [[[FSTDoubleValue doubleValue:zero.d] value] doubleValue];
- XCTAssertEqual(result.bits, zero.bits);
- result.d = [[[FSTDoubleValue doubleValue:negativeZero.d] value] doubleValue];
- XCTAssertEqual(result.bits, negativeZero.bits);
- // ... but compares positive/negative zero as unequal, compatibly with Firestore.
- XCTAssertNotEqualObjects([FSTDoubleValue doubleValue:0.0], [FSTDoubleValue doubleValue:-0.0]);
- }
- - (void)testWrapStrings {
- NSArray *values = @[ @"", @"abc" ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTStringValue class]);
- XCTAssertEqualObjects([wrapped value], value);
- }
- }
- - (void)testWrapDates {
- NSArray *values = @[ FSTTestDate(1900, 12, 1, 1, 20, 30), FSTTestDate(2017, 4, 24, 13, 20, 30) ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTTimestampValue class]);
- XCTAssertEqualObjects([[wrapped value] class], [FIRTimestamp class]);
- XCTAssertEqualObjects([wrapped value], [FIRTimestamp timestampWithDate:value]);
- }
- }
- - (void)testWrapGeoPoints {
- NSArray *values = @[ FSTTestGeoPoint(1.24, 4.56), FSTTestGeoPoint(-20, 100) ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTGeoPointValue class]);
- XCTAssertEqualObjects([wrapped value], value);
- }
- }
- - (void)testWrapBlobs {
- NSArray *values = @[ FSTTestData(1, 2, 3), FSTTestData(1, 2) ];
- for (id value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTBlobValue class]);
- XCTAssertEqualObjects([wrapped value], value);
- }
- }
- - (void)testWrapResourceNames {
- NSArray *values = @[
- FSTTestRef("project", DatabaseId::kDefault, @"foo/bar"),
- FSTTestRef("project", DatabaseId::kDefault, @"foo/baz")
- ];
- for (FSTDocumentKeyReference *value in values) {
- FSTFieldValue *wrapped = FSTTestFieldValue(value);
- XCTAssertEqualObjects([wrapped class], [FSTReferenceValue class]);
- XCTAssertEqualObjects([wrapped value], value.key);
- XCTAssertTrue(*((FSTReferenceValue *)wrapped).databaseID ==
- *(const DatabaseId *)(value.databaseID));
- }
- }
- - (void)testWrapsEmptyObjects {
- XCTAssertEqualObjects(FSTTestFieldValue(@{}), [FSTObjectValue objectValue]);
- }
- - (void)testWrapsSimpleObjects {
- FSTObjectValue *actual =
- FSTTestObjectValue(@{@"a" : @"foo", @"b" : @(1L), @"c" : @YES, @"d" : [NSNull null]});
- FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{
- @"a" : [FSTStringValue stringValue:@"foo"],
- @"b" : [FSTIntegerValue integerValue:1LL],
- @"c" : [FSTBooleanValue trueValue],
- @"d" : [FSTNullValue nullValue]
- }];
- XCTAssertEqualObjects(actual, expected);
- }
- - (void)testWrapsNestedObjects {
- FSTObjectValue *actual = FSTTestObjectValue(@{@"a" : @{@"b" : @{@"c" : @"foo"}, @"d" : @YES}});
- FSTObjectValue *expected = [[FSTObjectValue alloc] initWithDictionary:@{
- @"a" : [[FSTObjectValue alloc] initWithDictionary:@{
- @"b" :
- [[FSTObjectValue alloc] initWithDictionary:@{@"c" : [FSTStringValue stringValue:@"foo"]}],
- @"d" : [FSTBooleanValue booleanValue:YES]
- }]
- }];
- XCTAssertEqualObjects(actual, expected);
- }
- - (void)testExtractsFields {
- FSTObjectValue *obj = FSTTestObjectValue(@{@"foo" : @{@"a" : @YES, @"b" : @"string"}});
- FSTAssertIsKindOfClass(obj, FSTObjectValue);
- FSTAssertIsKindOfClass([obj valueForPath:testutil::Field("foo")], FSTObjectValue);
- XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.a")], [FSTBooleanValue trueValue]);
- XCTAssertEqualObjects([obj valueForPath:testutil::Field("foo.b")],
- [FSTStringValue stringValue:@"string"]);
- XCTAssertNil([obj valueForPath:testutil::Field("foo.a.b")]);
- XCTAssertNil([obj valueForPath:testutil::Field("bar")]);
- XCTAssertNil([obj valueForPath:testutil::Field("bar.a")]);
- }
- - (void)testOverwritesExistingFields {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @"old"});
- FSTObjectValue *mod = [old objectBySettingValue:FSTTestFieldValue(@"mod")
- forPath:testutil::Field("a")];
- // Should return a new object, leaving the old one unmodified.
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"old"}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @"mod"}));
- }
- - (void)testAddsNewFields {
- FSTObjectValue *empty = [FSTObjectValue objectValue];
- FSTObjectValue *mod = [empty objectBySettingValue:FSTTestFieldValue(@"mod")
- forPath:testutil::Field("a")];
- XCTAssertNotEqual(empty, mod);
- XCTAssertEqualObjects(empty, FSTTestFieldValue(@{}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @"mod"}));
- FSTObjectValue *old = mod;
- mod = [old objectBySettingValue:FSTTestFieldValue(@1) forPath:testutil::Field("b")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"mod"}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @"mod", @"b" : @1}));
- }
- - (void)testImplicitlyCreatesObjects {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @"old"});
- FSTObjectValue *mod = [old objectBySettingValue:FSTTestFieldValue(@"mod")
- forPath:testutil::Field("b.c.d")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @"old"}));
- XCTAssertEqualObjects(mod,
- FSTTestFieldValue(@{@"a" : @"old", @"b" : @{@"c" : @{@"d" : @"mod"}}}));
- }
- - (void)testCanOverwritePrimitivesWithObjects {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @{@"b" : @"old"}});
- FSTObjectValue *mod = [old objectBySettingValue:FSTTestFieldValue(@{@"b" : @"mod"})
- forPath:testutil::Field("a")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @{@"b" : @"old"}}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @"mod"}}));
- }
- - (void)testAddsToNestedObjects {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @{@"b" : @"old"}});
- FSTObjectValue *mod = [old objectBySettingValue:FSTTestFieldValue(@"mod")
- forPath:testutil::Field("a.c")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @{@"b" : @"old"}}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @"old", @"c" : @"mod"}}));
- }
- - (void)testDeletesKeys {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @1, @"b" : @2});
- FSTObjectValue *mod = [old objectByDeletingPath:testutil::Field("a")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @1, @"b" : @2}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"b" : @2}));
- FSTObjectValue *empty = [mod objectByDeletingPath:testutil::Field("b")];
- XCTAssertNotEqual(mod, empty);
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"b" : @2}));
- XCTAssertEqualObjects(empty, FSTTestFieldValue(@{}));
- }
- - (void)testDeletesHandleMissingKeys {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @{@"b" : @1, @"c" : @2}});
- FSTObjectValue *mod = [old objectByDeletingPath:testutil::Field("b")];
- XCTAssertEqualObjects(old, mod);
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @2}}));
- mod = [old objectByDeletingPath:testutil::Field("a.d")];
- XCTAssertEqualObjects(old, mod);
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @2}}));
- mod = [old objectByDeletingPath:testutil::Field("a.b.c")];
- XCTAssertEqualObjects(old, mod);
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @2}}));
- }
- - (void)testDeletesNestedKeys {
- FSTObjectValue *old = FSTTestObjectValue(@{@"a" : @{@"b" : @1, @"c" : @{@"d" : @2, @"e" : @3}}});
- FSTObjectValue *mod = [old objectByDeletingPath:testutil::Field("a.c.d")];
- XCTAssertNotEqual(old, mod);
- XCTAssertEqualObjects(old,
- FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @{@"d" : @2, @"e" : @3}}}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @{@"e" : @3}}}));
- old = mod;
- mod = [old objectByDeletingPath:testutil::Field("a.c")];
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @{@"b" : @1, @"c" : @{@"e" : @3}}}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{@"a" : @{@"b" : @1}}));
- old = mod;
- mod = [old objectByDeletingPath:testutil::Field("a")];
- XCTAssertEqualObjects(old, FSTTestFieldValue(@{@"a" : @{@"b" : @1}}));
- XCTAssertEqualObjects(mod, FSTTestFieldValue(@{}));
- }
- - (void)testArrays {
- FSTArrayValue *expected = [[FSTArrayValue alloc]
- initWithValueNoCopy:@[ [FSTStringValue stringValue:@"value"], [FSTBooleanValue trueValue] ]];
- FSTArrayValue *actual = (FSTArrayValue *)FSTTestFieldValue(@[ @"value", @YES ]);
- XCTAssertEqualObjects(actual, expected);
- }
- - (void)testValueEquality {
- DatabaseId database_id = DatabaseId("project", DatabaseId::kDefault);
- NSArray *groups = @[
- @[ FSTTestFieldValue(@YES), [FSTBooleanValue booleanValue:YES] ],
- @[ FSTTestFieldValue(@NO), [FSTBooleanValue booleanValue:NO] ],
- @[ FSTTestFieldValue([NSNull null]), [FSTNullValue nullValue] ],
- @[ FSTTestFieldValue(@(0.0 / 0.0)), FSTTestFieldValue(@(NAN)), [FSTDoubleValue nanValue] ],
- // -0.0 and 0.0 compare: the same (but are not isEqual:)
- @[ FSTTestFieldValue(@(-0.0)) ], @[ FSTTestFieldValue(@0.0) ],
- @[ FSTTestFieldValue(@1), FSTTestFieldValue(@1LL), [FSTIntegerValue integerValue:1LL] ],
- // double and unit64_t values can compare: the same (but won't be isEqual:)
- @[ FSTTestFieldValue(@1.0), [FSTDoubleValue doubleValue:1.0] ],
- @[ FSTTestFieldValue(@1.1), [FSTDoubleValue doubleValue:1.1] ],
- @[
- FSTTestFieldValue(FSTTestData(0, 1, 2, -1)), [FSTBlobValue blobValue:FSTTestData(0, 1, 2, -1)]
- ],
- @[ FSTTestFieldValue(FSTTestData(0, 1, -1)) ],
- @[ FSTTestFieldValue(@"string"), [FSTStringValue stringValue:@"string"] ],
- @[ FSTTestFieldValue(@"strin") ],
- @[ FSTTestFieldValue(@"e\u0301b") ], // latin small letter e + combining acute accent
- @[ FSTTestFieldValue(@"\u00e9a") ], // latin small letter e with acute accent
- @[
- FSTTestFieldValue(date1),
- [FSTTimestampValue timestampValue:[FIRTimestamp timestampWithDate:date1]]
- ],
- @[ FSTTestFieldValue(date2) ],
- @[
- // NOTE: ServerTimestampValues can't be parsed via FSTTestFieldValue().
- [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date1]
- previousValue:nil],
- [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date1]
- previousValue:nil]
- ],
- @[ [FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:[FIRTimestamp timestampWithDate:date2]
- previousValue:nil] ],
- @[
- FSTTestFieldValue(FSTTestGeoPoint(0, 1)),
- [FSTGeoPointValue geoPointValue:FSTTestGeoPoint(0, 1)]
- ],
- @[ FSTTestFieldValue(FSTTestGeoPoint(1, 0)) ],
- @[
- [FSTReferenceValue referenceValue:FSTTestDocKey(@"coll/doc1") databaseID:&database_id],
- FSTTestFieldValue(FSTTestRef("project", DatabaseId::kDefault, @"coll/doc1"))
- ],
- @[ FSTTestRef("project", "(default)", @"coll/doc2") ],
- @[ FSTTestFieldValue(@[ @"foo", @"bar" ]), FSTTestFieldValue(@[ @"foo", @"bar" ]) ],
- @[ FSTTestFieldValue(@[ @"foo", @"bar", @"baz" ]) ], @[ FSTTestFieldValue(@[ @"foo" ]) ],
- @[
- FSTTestFieldValue(@{@"bar" : @1, @"foo" : @2}), FSTTestFieldValue(@{@"foo" : @2, @"bar" : @1})
- ],
- @[ FSTTestFieldValue(@{@"bar" : @2, @"foo" : @1}) ],
- @[ FSTTestFieldValue(@{@"bar" : @1, @"foo" : @1}) ], @[ FSTTestFieldValue(@{@"foo" : @1}) ]
- ];
- FSTAssertEqualityGroups(groups);
- }
- - (void)testValueOrdering {
- NSArray *groups = @[
- // null first
- @[ [NSNull null] ],
- // booleans
- @[ @NO ], @[ @YES ],
- // numbers
- @[ @(0.0 / 0.0) ], @[ @(-INFINITY) ], @[ @(-DBL_MAX) ], @[ @(LLONG_MIN) ], @[ @(-1.1) ],
- @[ @(-1.0), @(-1LL) ], // longs and doubles compare the same
- @[ @(-DBL_MIN) ],
- @[ @(-0x1.0p-1074) ], // negative smallest subnormal
- @[ @(-0.0), @(0.0), @(0LL) ], // zeros all compare the same
- @[ @(0x1.0p-1074) ], // positive smallest subnormal
- @[ @(DBL_MIN) ], @[ @1.0, @1LL ], // longs and doubles compare the same
- @[ @1.1 ], @[ @(LLONG_MAX) ], @[ @(DBL_MAX) ], @[ @(INFINITY) ],
- // timestamps
- @[ date1 ], @[ date2 ],
- // server timestamps come after all concrete timestamps.
- // NOTE: server timestamps can't be parsed directly, so we have special sentinel strings (see
- // FSTWrapGroups()).
- @[ @"server-timestamp-1" ], @[ @"server-timestamp-2" ],
- // strings
- @[ @"" ], @[ @"\000\ud7ff\ue000\uffff" ], @[ @"(╯°□°)╯︵ ┻━┻" ], @[ @"a" ], @[ @"abc def" ],
- @[ @"e\u0301b" ], // latin small letter e + combining acute accent + latin small letter b
- @[ @"æ" ],
- @[ @"\u00e9a" ], // latin small letter e with acute accent + latin small letter a
- // blobs
- @[ FSTTestData(-1) ], @[ FSTTestData(0, -1) ], @[ FSTTestData(0, 1, 2, 3, 4, -1) ],
- @[ FSTTestData(0, 1, 2, 4, 3, -1) ], @[ FSTTestData(255, -1) ],
- // resource names
- @[ FSTTestRef("p1", "d1", @"c1/doc1") ], @[ FSTTestRef("p1", "d1", @"c1/doc2") ],
- @[ FSTTestRef("p1", "d1", @"c10/doc1") ], @[ FSTTestRef("p1", "d1", @"c2/doc1") ],
- @[ FSTTestRef("p1", "d2", @"c1/doc1") ], @[ FSTTestRef("p2", "d1", @"c1/doc1") ],
- // Geo points
- @[ FSTTestGeoPoint(-90, -180) ], @[ FSTTestGeoPoint(-90, 0) ], @[ FSTTestGeoPoint(-90, 180) ],
- @[ FSTTestGeoPoint(0, -180) ], @[ FSTTestGeoPoint(0, 0) ], @[ FSTTestGeoPoint(0, 180) ],
- @[ FSTTestGeoPoint(1, -180) ], @[ FSTTestGeoPoint(1, 0) ], @[ FSTTestGeoPoint(1, 180) ],
- @[ FSTTestGeoPoint(90, -180) ], @[ FSTTestGeoPoint(90, 0) ], @[ FSTTestGeoPoint(90, 180) ],
- // Arrays
- @[ @[] ], @[ @[ @"bar" ] ], @[ @[ @"foo" ] ], @[ @[ @"foo", @1 ] ], @[ @[ @"foo", @2 ] ],
- @[ @[ @"foo", @"0" ] ],
- // Objects
- @[ @{@"bar" : @0} ], @[ @{@"bar" : @0, @"foo" : @1} ], @[ @{@"foo" : @1} ], @[ @{@"foo" : @2} ],
- @[ @{@"foo" : @"0"} ]
- ];
- NSArray *wrapped = FSTWrapGroups(groups);
- FSTAssertComparisons(wrapped);
- }
- - (void)testValue {
- NSDate *date = [NSDate date];
- id input = @{@"array" : @[ @1, date ], @"obj" : @{@"date" : date, @"string" : @"hi"}};
- FSTObjectValue *value = FSTTestObjectValue(input);
- id output = [value value];
- {
- XCTAssertTrue([output[@"array"][1] isKindOfClass:[FIRTimestamp class]]);
- FIRTimestamp *actual = output[@"array"][1];
- XCTAssertEqualObjects([FIRTimestamp timestampWithDate:date], actual);
- }
- {
- XCTAssertTrue([output[@"obj"][@"date"] isKindOfClass:[FIRTimestamp class]]);
- FIRTimestamp *actual = output[@"array"][1];
- XCTAssertEqualObjects([FIRTimestamp timestampWithDate:date], actual);
- }
- }
- @end
|