aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/core
diff options
context:
space:
mode:
authorGravatar zxu <zxu@google.com>2018-01-25 09:14:19 -0500
committerGravatar GitHub <noreply@github.com>2018-01-25 09:14:19 -0500
commitbfa0e40795ba676fd02d616794cce1c9b2d6a95f (patch)
tree875e86ca1282c275a7f4ad68d03dbc0a2fb22b45 /Firestore/core
parent1f772120365641eb66f0cc9db9975721c8285d6e (diff)
Implement the rest of FieldValue types for C++ (#687)
* implement FieldValue for null and boolean. * Implement number and string FieldValue. * Implement object FieldValue. * implement timestamp FieldValue. * Implement number and string FieldValue. * implement public type `Blob` and `GeoPoint` * implement Blob FieldValue * Implement GeoPoint FieldValue * refactoring `Blob`
Diffstat (limited to 'Firestore/core')
-rw-r--r--Firestore/core/CMakeLists.txt2
-rw-r--r--Firestore/core/include/firebase/firestore/geo_point.h89
-rw-r--r--Firestore/core/src/firebase/firestore/CMakeLists.txt22
-rw-r--r--Firestore/core/src/firebase/firestore/geo_point.cc50
-rw-r--r--Firestore/core/src/firebase/firestore/model/CMakeLists.txt2
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.cc209
-rw-r--r--Firestore/core/src/firebase/firestore/model/field_value.h39
-rw-r--r--Firestore/core/src/firebase/firestore/model/timestamp.cc54
-rw-r--r--Firestore/core/src/firebase/firestore/model/timestamp.h94
-rw-r--r--Firestore/core/test/firebase/firestore/CMakeLists.txt21
-rw-r--r--Firestore/core/test/firebase/firestore/geo_point_test.cc41
-rw-r--r--Firestore/core/test/firebase/firestore/model/CMakeLists.txt1
-rw-r--r--Firestore/core/test/firebase/firestore/model/field_value_test.cc300
-rw-r--r--Firestore/core/test/firebase/firestore/model/timestamp_test.cc49
14 files changed, 972 insertions, 1 deletions
diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt
index da08dfc..f023446 100644
--- a/Firestore/core/CMakeLists.txt
+++ b/Firestore/core/CMakeLists.txt
@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+add_subdirectory(src/firebase/firestore)
add_subdirectory(src/firebase/firestore/model)
add_subdirectory(src/firebase/firestore/remote)
add_subdirectory(src/firebase/firestore/util)
+add_subdirectory(test/firebase/firestore)
add_subdirectory(test/firebase/firestore/model)
add_subdirectory(test/firebase/firestore/remote)
add_subdirectory(test/firebase/firestore/util)
diff --git a/Firestore/core/include/firebase/firestore/geo_point.h b/Firestore/core/include/firebase/firestore/geo_point.h
new file mode 100644
index 0000000..cc366be
--- /dev/null
+++ b/Firestore/core/include/firebase/firestore/geo_point.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_
+#define FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_
+
+namespace firebase {
+namespace firestore {
+
+/**
+ * An immutable object representing a geographical point in Firestore. The point
+ * is represented as a latitude/longitude pair.
+ *
+ * Latitude values are in the range of [-90, 90].
+ * Longitude values are in the range of [-180, 180].
+ */
+class GeoPoint {
+ public:
+ /**
+ * Creates a `GeoPoint` with both latitude and longitude being 0.
+ */
+ GeoPoint();
+
+ /**
+ * Creates a `GeoPoint` from the provided latitude and longitude degrees.
+ *
+ * @param latitude The latitude as number between -90 and 90.
+ * @param longitude The longitude as number between -180 and 180.
+ */
+ GeoPoint(double latitude, double longitude);
+
+ GeoPoint(const GeoPoint& other) = default;
+ GeoPoint(GeoPoint&& other) = default;
+ GeoPoint& operator=(const GeoPoint& other) = default;
+ GeoPoint& operator=(GeoPoint&& other) = default;
+
+ double latitude() const {
+ return latitude_;
+ }
+
+ double longitude() const {
+ return longitude_;
+ }
+
+ private:
+ double latitude_;
+ double longitude_;
+};
+
+/** Compares against another GeoPoint. */
+bool operator<(const GeoPoint& lhs, const GeoPoint& rhs);
+
+inline bool operator>(const GeoPoint& lhs, const GeoPoint& rhs) {
+ return rhs < lhs;
+}
+
+inline bool operator>=(const GeoPoint& lhs, const GeoPoint& rhs) {
+ return !(lhs < rhs);
+}
+
+inline bool operator<=(const GeoPoint& lhs, const GeoPoint& rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator!=(const GeoPoint& lhs, const GeoPoint& rhs) {
+ return lhs < rhs || lhs > rhs;
+}
+
+inline bool operator==(const GeoPoint& lhs, const GeoPoint& rhs) {
+ return !(lhs != rhs);
+}
+
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_INCLUDE_FIREBASE_FIRESTORE_GEO_POINT_H_
diff --git a/Firestore/core/src/firebase/firestore/CMakeLists.txt b/Firestore/core/src/firebase/firestore/CMakeLists.txt
new file mode 100644
index 0000000..3f5522c
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright 2018 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.
+
+# Public types to be used both internally and externally.
+cc_library(
+ firebase_firestore_types
+ SOURCES
+ geo_point.cc
+ DEPENDS
+ firebase_firestore_util
+)
diff --git a/Firestore/core/src/firebase/firestore/geo_point.cc b/Firestore/core/src/firebase/firestore/geo_point.cc
new file mode 100644
index 0000000..fb01023
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/geo_point.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/include/firebase/firestore/geo_point.h"
+
+#include <math.h>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+
+GeoPoint::GeoPoint() : GeoPoint(0, 0) {
+}
+
+GeoPoint::GeoPoint(double latitude, double longitude)
+ : latitude_(latitude), longitude_(longitude) {
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ !isnan(latitude) && -90 <= latitude && latitude <= 90,
+ -90 <= latitude && latitude <= 90,
+ "Latitude must be in the range of [-90, 90]");
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ !isnan(longitude) && -180 <= longitude && longitude <= 180,
+ -180 <= longitude && longitude <= 180,
+ "Latitude must be in the range of [-180, 180]");
+}
+
+bool operator<(const GeoPoint& lhs, const GeoPoint& rhs) {
+ if (lhs.latitude() == rhs.latitude()) {
+ return lhs.longitude() < rhs.longitude();
+ } else {
+ return lhs.latitude() < rhs.latitude();
+ }
+}
+
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
index ae80de3..3af56ec 100644
--- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt
@@ -16,6 +16,8 @@ cc_library(
firebase_firestore_model
SOURCES
field_value.cc
+ timestamp.cc
DEPENDS
firebase_firestore_util
+ firebase_firestore_types
)
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc
index 5583afc..b063543 100644
--- a/Firestore/core/src/firebase/firestore/model/field_value.cc
+++ b/Firestore/core/src/firebase/firestore/model/field_value.cc
@@ -16,6 +16,8 @@
#include "Firestore/core/src/firebase/firestore/model/field_value.h"
+#include <math.h>
+
#include <algorithm>
#include <memory>
#include <utility>
@@ -31,6 +33,7 @@ namespace firestore {
namespace model {
using Type = FieldValue::Type;
+using firebase::firestore::util::ComparisonResult;
namespace {
/**
@@ -75,12 +78,42 @@ FieldValue& FieldValue::operator=(const FieldValue& value) {
case Type::Boolean:
boolean_value_ = value.boolean_value_;
break;
+ case Type::Long:
+ integer_value_ = value.integer_value_;
+ break;
+ case Type::Double:
+ double_value_ = value.double_value_;
+ break;
+ case Type::Timestamp:
+ timestamp_value_ = value.timestamp_value_;
+ break;
+ case Type::ServerTimestamp:
+ server_timestamp_value_ = value.server_timestamp_value_;
+ break;
+ case Type::String:
+ string_value_ = value.string_value_;
+ break;
+ case Type::Blob: {
+ // copy-and-swap
+ std::vector<const uint8_t> tmp = value.blob_value_;
+ std::swap(blob_value_, tmp);
+ break;
+ }
+ case Type::GeoPoint:
+ geo_point_value_ = value.geo_point_value_;
+ break;
case Type::Array: {
// copy-and-swap
std::vector<const FieldValue> tmp = value.array_value_;
std::swap(array_value_, tmp);
break;
}
+ case Type::Object: {
+ // copy-and-swap
+ std::map<const std::string, const FieldValue> tmp = value.object_value_;
+ std::swap(object_value_, tmp);
+ break;
+ }
default:
FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
false, lhs.type(), "Unsupported type %d", value.type());
@@ -90,10 +123,22 @@ FieldValue& FieldValue::operator=(const FieldValue& value) {
FieldValue& FieldValue::operator=(FieldValue&& value) {
switch (value.tag_) {
+ case Type::String:
+ SwitchTo(Type::String);
+ string_value_.swap(value.string_value_);
+ return *this;
+ case Type::Blob:
+ SwitchTo(Type::Blob);
+ std::swap(blob_value_, value.blob_value_);
+ return *this;
case Type::Array:
SwitchTo(Type::Array);
std::swap(array_value_, value.array_value_);
return *this;
+ case Type::Object:
+ SwitchTo(Type::Object);
+ std::swap(object_value_, value.object_value_);
+ return *this;
default:
// We just copy over POD union types.
return *this = value;
@@ -119,6 +164,82 @@ const FieldValue& FieldValue::BooleanValue(bool value) {
return value ? TrueValue() : FalseValue();
}
+const FieldValue& FieldValue::NanValue() {
+ static const FieldValue kNanInstance = FieldValue::DoubleValue(NAN);
+ return kNanInstance;
+}
+
+FieldValue FieldValue::IntegerValue(int64_t value) {
+ FieldValue result;
+ result.SwitchTo(Type::Long);
+ result.integer_value_ = value;
+ return result;
+}
+
+FieldValue FieldValue::DoubleValue(double value) {
+ FieldValue result;
+ result.SwitchTo(Type::Double);
+ result.double_value_ = value;
+ return result;
+}
+
+FieldValue FieldValue::TimestampValue(const Timestamp& value) {
+ FieldValue result;
+ result.SwitchTo(Type::Timestamp);
+ result.timestamp_value_ = value;
+ return result;
+}
+
+FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time,
+ const Timestamp& previous_value) {
+ FieldValue result;
+ result.SwitchTo(Type::ServerTimestamp);
+ result.server_timestamp_value_.local_write_time = local_write_time;
+ result.server_timestamp_value_.previous_value = previous_value;
+ result.server_timestamp_value_.has_previous_value_ = true;
+ return result;
+}
+
+FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time) {
+ FieldValue result;
+ result.SwitchTo(Type::ServerTimestamp);
+ result.server_timestamp_value_.local_write_time = local_write_time;
+ result.server_timestamp_value_.has_previous_value_ = false;
+ return result;
+}
+
+FieldValue FieldValue::StringValue(const char* value) {
+ std::string copy(value);
+ return StringValue(std::move(copy));
+}
+
+FieldValue FieldValue::StringValue(const std::string& value) {
+ std::string copy(value);
+ return StringValue(std::move(copy));
+}
+
+FieldValue FieldValue::StringValue(std::string&& value) {
+ FieldValue result;
+ result.SwitchTo(Type::String);
+ result.string_value_.swap(value);
+ return result;
+}
+
+FieldValue FieldValue::BlobValue(const uint8_t* source, size_t size) {
+ FieldValue result;
+ result.SwitchTo(Type::Blob);
+ std::vector<const uint8_t> copy(source, source + size);
+ std::swap(result.blob_value_, copy);
+ return result;
+}
+
+FieldValue FieldValue::GeoPointValue(const GeoPoint& value) {
+ FieldValue result;
+ result.SwitchTo(Type::GeoPoint);
+ result.geo_point_value_ = value;
+ return result;
+}
+
FieldValue FieldValue::ArrayValue(const std::vector<const FieldValue>& value) {
std::vector<const FieldValue> copy(value);
return ArrayValue(std::move(copy));
@@ -131,6 +252,20 @@ FieldValue FieldValue::ArrayValue(std::vector<const FieldValue>&& value) {
return result;
}
+FieldValue FieldValue::ObjectValue(
+ const std::map<const std::string, const FieldValue>& value) {
+ std::map<const std::string, const FieldValue> copy(value);
+ return ObjectValue(std::move(copy));
+}
+
+FieldValue FieldValue::ObjectValue(
+ std::map<const std::string, const FieldValue>&& value) {
+ FieldValue result;
+ result.SwitchTo(Type::Object);
+ std::swap(result.object_value_, value);
+ return result;
+}
+
bool operator<(const FieldValue& lhs, const FieldValue& rhs) {
if (!Comparable(lhs.type(), rhs.type())) {
return lhs.type() < rhs.type();
@@ -141,8 +276,45 @@ bool operator<(const FieldValue& lhs, const FieldValue& rhs) {
return false;
case Type::Boolean:
return Comparator<bool>()(lhs.boolean_value_, rhs.boolean_value_);
+ case Type::Long:
+ if (rhs.type() == Type::Long) {
+ return Comparator<int64_t>()(lhs.integer_value_, rhs.integer_value_);
+ } else {
+ return util::CompareMixedNumber(rhs.double_value_,
+ lhs.integer_value_) ==
+ ComparisonResult::Descending;
+ }
+ case Type::Double:
+ if (rhs.type() == Type::Double) {
+ return Comparator<double>()(lhs.double_value_, rhs.double_value_);
+ } else {
+ return util::CompareMixedNumber(lhs.double_value_,
+ rhs.integer_value_) ==
+ ComparisonResult::Ascending;
+ }
+ case Type::Timestamp:
+ if (rhs.type() == Type::Timestamp) {
+ return lhs.timestamp_value_ < rhs.timestamp_value_;
+ } else {
+ return true;
+ }
+ case Type::ServerTimestamp:
+ if (rhs.type() == Type::ServerTimestamp) {
+ return lhs.server_timestamp_value_.local_write_time <
+ rhs.server_timestamp_value_.local_write_time;
+ } else {
+ return false;
+ }
+ case Type::String:
+ return lhs.string_value_.compare(rhs.string_value_) < 0;
+ case Type::Blob:
+ return lhs.blob_value_ < rhs.blob_value_;
+ case Type::GeoPoint:
+ return lhs.geo_point_value_ < rhs.geo_point_value_;
case Type::Array:
return lhs.array_value_ < rhs.array_value_;
+ case Type::Object:
+ return lhs.object_value_ < rhs.object_value_;
default:
FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
false, lhs.type(), "Unsupported type %d", lhs.type());
@@ -159,17 +331,54 @@ void FieldValue::SwitchTo(const Type type) {
// Not same type. Destruct old type first and then initialize new type.
// Must call destructor explicitly for any non-POD type.
switch (tag_) {
+ case Type::Timestamp:
+ timestamp_value_.~Timestamp();
+ break;
+ case Type::ServerTimestamp:
+ server_timestamp_value_.~ServerTimestamp();
+ break;
+ case Type::String:
+ string_value_.~basic_string();
+ break;
+ case Type::Blob:
+ blob_value_.~vector();
+ break;
+ case Type::GeoPoint:
+ geo_point_value_.~GeoPoint();
+ break;
case Type::Array:
array_value_.~vector();
break;
+ case Type::Object:
+ object_value_.~map();
+ break;
default: {} // The other types where there is nothing to worry about.
}
tag_ = type;
// Must call constructor explicitly for any non-POD type to initialize.
switch (tag_) {
+ case Type::Timestamp:
+ new (&timestamp_value_) Timestamp(0, 0);
+ break;
+ case Type::ServerTimestamp:
+ new (&server_timestamp_value_) ServerTimestamp();
+ break;
+ case Type::String:
+ new (&string_value_) std::string();
+ break;
+ case Type::Blob:
+ // Do not even bother to allocate a new array of size 0.
+ new (&blob_value_) std::vector<const uint8_t>();
+ break;
+ case Type::GeoPoint:
+ new (&geo_point_value_) GeoPoint();
+ break;
case Type::Array:
new (&array_value_) std::vector<const FieldValue>();
break;
+ case Type::Object:
+ new (&object_value_) std::map<const std::string, const FieldValue>();
+ break;
default: {} // The other types where there is nothing to worry about.
}
}
diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h
index 781e34f..bb6594f 100644
--- a/Firestore/core/src/firebase/firestore/model/field_value.h
+++ b/Firestore/core/src/firebase/firestore/model/field_value.h
@@ -17,13 +17,27 @@
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
+#include <stdint.h>
+
+#include <map>
#include <memory>
+#include <string>
#include <vector>
+#include "Firestore/core/include/firebase/firestore/geo_point.h"
+#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
+
namespace firebase {
namespace firestore {
namespace model {
+struct ServerTimestamp {
+ Timestamp local_write_time;
+ Timestamp previous_value;
+ // TODO(zxu123): adopt absl::optional once abseil is ported.
+ bool has_previous_value_;
+};
+
/**
* tagged-union class representing an immutable data value as stored in
* Firestore. FieldValue represents all the different kinds of values
@@ -78,8 +92,25 @@ class FieldValue {
static const FieldValue& TrueValue();
static const FieldValue& FalseValue();
static const FieldValue& BooleanValue(bool value);
+ static const FieldValue& NanValue();
+ static FieldValue IntegerValue(int64_t value);
+ static FieldValue DoubleValue(double value);
+ static FieldValue TimestampValue(const Timestamp& value);
+ static FieldValue ServerTimestampValue(const Timestamp& local_write_time,
+ const Timestamp& previous_value);
+ static FieldValue ServerTimestampValue(const Timestamp& local_write_time);
+ static FieldValue StringValue(const char* value);
+ static FieldValue StringValue(const std::string& value);
+ static FieldValue StringValue(std::string&& value);
+ static FieldValue BlobValue(const uint8_t* source, size_t size);
+ // static FieldValue ReferenceValue();
+ static FieldValue GeoPointValue(const GeoPoint& value);
static FieldValue ArrayValue(const std::vector<const FieldValue>& value);
static FieldValue ArrayValue(std::vector<const FieldValue>&& value);
+ static FieldValue ObjectValue(
+ const std::map<const std::string, const FieldValue>& value);
+ static FieldValue ObjectValue(
+ std::map<const std::string, const FieldValue>&& value);
friend bool operator<(const FieldValue& lhs, const FieldValue& rhs);
@@ -96,7 +127,15 @@ class FieldValue {
union {
// There is no null type as tag_ alone is enough for Null FieldValue.
bool boolean_value_;
+ int64_t integer_value_;
+ double double_value_;
+ Timestamp timestamp_value_;
+ ServerTimestamp server_timestamp_value_;
+ std::string string_value_;
+ std::vector<const uint8_t> blob_value_;
+ GeoPoint geo_point_value_;
std::vector<const FieldValue> array_value_;
+ std::map<const std::string, const FieldValue> object_value_;
};
};
diff --git a/Firestore/core/src/firebase/firestore/model/timestamp.cc b/Firestore/core/src/firebase/firestore/model/timestamp.cc
new file mode 100644
index 0000000..c2bb008
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/timestamp.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
+
+#include <time.h>
+
+#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+Timestamp::Timestamp(long seconds, int nanos)
+ : seconds_(seconds), nanos_(nanos) {
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ nanos >= 0, nanos >= 0, "timestamp nanoseconds out of range: %d", nanos);
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ nanos < 1e9, nanos < 1e9, "timestamp nanoseconds out of range: %d",
+ nanos);
+ // Midnight at the beginning of 1/1/1 is the earliest timestamp Firestore
+ // supports.
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ seconds >= -62135596800L, seconds >= -62135596800L,
+ "timestamp seconds out of range: %lld", seconds);
+ // This will break in the year 10,000.
+ FIREBASE_ASSERT_MESSAGE_WITH_EXPRESSION(
+ seconds < 253402300800L, seconds < 253402300800L,
+ "timestamp seconds out of range: %lld", seconds);
+}
+
+Timestamp::Timestamp() : seconds_(0), nanos_(0) {
+}
+
+Timestamp Timestamp::Now() {
+ return Timestamp(time(nullptr), 0);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/src/firebase/firestore/model/timestamp.h b/Firestore/core/src/firebase/firestore/model/timestamp.h
new file mode 100644
index 0000000..3ffee48
--- /dev/null
+++ b/Firestore/core/src/firebase/firestore/model/timestamp.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TIMESTAMP_H_
+#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_TIMESTAMP_H_
+
+#include <stdint.h>
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+/**
+ * A Timestamp represents an absolute time from the backend at up to nanosecond
+ * precision. A Timestamp is always UTC.
+ */
+class Timestamp {
+ public:
+ /**
+ * Creates a new timestamp with seconds and nanos set to 0.
+ *
+ * PORTING NOTE: This does NOT set to current timestamp by default. To get the
+ * current timestamp, call Timestamp::Now().
+ */
+ Timestamp();
+
+ /**
+ * Creates a new timestamp.
+ *
+ * @param seconds the number of seconds since epoch.
+ * @param nanos the number of nanoseconds after the seconds.
+ */
+ Timestamp(long seconds, int nanos);
+
+ /** Returns a timestamp with the current date / time. */
+ static Timestamp Now();
+
+ long seconds() const {
+ return seconds_;
+ }
+
+ int nanos() const {
+ return nanos_;
+ }
+
+ private:
+ long seconds_;
+ int nanos_;
+};
+
+/** Compares against another Timestamp. */
+inline bool operator<(const Timestamp& lhs, const Timestamp& rhs) {
+ return lhs.seconds() < rhs.seconds() ||
+ (lhs.seconds() == rhs.seconds() && lhs.nanos() < rhs.nanos());
+}
+
+inline bool operator>(const Timestamp& lhs, const Timestamp& rhs) {
+ return rhs < lhs;
+}
+
+inline bool operator>=(const Timestamp& lhs, const Timestamp& rhs) {
+ return !(lhs < rhs);
+}
+
+inline bool operator<=(const Timestamp& lhs, const Timestamp& rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator!=(const Timestamp& lhs, const Timestamp& rhs) {
+ return lhs < rhs || lhs > rhs;
+}
+
+inline bool operator==(const Timestamp& lhs, const Timestamp& rhs) {
+ return !(lhs != rhs);
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase
+
+#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_VALUE_H_
diff --git a/Firestore/core/test/firebase/firestore/CMakeLists.txt b/Firestore/core/test/firebase/firestore/CMakeLists.txt
new file mode 100644
index 0000000..ed5760f
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright 2018 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.
+
+cc_test(
+ firebase_firestore_types_test
+ SOURCES
+ geo_point_test.cc
+ DEPENDS
+ firebase_firestore_types
+)
diff --git a/Firestore/core/test/firebase/firestore/geo_point_test.cc b/Firestore/core/test/firebase/firestore/geo_point_test.cc
new file mode 100644
index 0000000..bd8d76f
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/geo_point_test.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/include/firebase/firestore/geo_point.h"
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+
+TEST(GeoPoint, Getter) {
+ const GeoPoint zero;
+ EXPECT_EQ(0, zero.latitude());
+ EXPECT_EQ(0, zero.longitude());
+
+ const GeoPoint point{12, 34};
+ EXPECT_EQ(12, point.latitude());
+ EXPECT_EQ(34, point.longitude());
+}
+
+TEST(GeoPoint, Comparison) {
+ EXPECT_EQ(GeoPoint(12, 34), GeoPoint(12, 34));
+ EXPECT_LT(GeoPoint(12, 34), GeoPoint(34, 12));
+ EXPECT_LT(GeoPoint(12, 34), GeoPoint(12, 56));
+}
+
+} // namespace firestore
+} // namespace firebase
diff --git a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
index 31fe040..9153a57 100644
--- a/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
+++ b/Firestore/core/test/firebase/firestore/model/CMakeLists.txt
@@ -16,6 +16,7 @@ cc_test(
firebase_firestore_model_test
SOURCES
field_value_test.cc
+ timestamp_test.cc
DEPENDS
firebase_firestore_model
)
diff --git a/Firestore/core/test/firebase/firestore/model/field_value_test.cc b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
index 1194e63..a006d46 100644
--- a/Firestore/core/test/firebase/firestore/model/field_value_test.cc
+++ b/Firestore/core/test/firebase/firestore/model/field_value_test.cc
@@ -26,6 +26,14 @@ namespace model {
using Type = FieldValue::Type;
+namespace {
+
+const uint8_t* Bytes(const char* value) {
+ return reinterpret_cast<const uint8_t*>(value);
+}
+
+} // namespace
+
TEST(FieldValue, NullType) {
const FieldValue value = FieldValue::NullValue();
EXPECT_EQ(Type::Null, value.type());
@@ -42,6 +50,107 @@ TEST(FieldValue, BooleanType) {
EXPECT_TRUE(false_value < true_value);
}
+TEST(FieldValue, NumberType) {
+ const FieldValue nan_value = FieldValue::NanValue();
+ const FieldValue integer_value = FieldValue::IntegerValue(10L);
+ const FieldValue double_value = FieldValue::DoubleValue(10.1);
+ EXPECT_EQ(Type::Double, nan_value.type());
+ EXPECT_EQ(Type::Long, integer_value.type());
+ EXPECT_EQ(Type::Double, double_value.type());
+ EXPECT_TRUE(nan_value < integer_value);
+ EXPECT_TRUE(nan_value < double_value);
+ EXPECT_FALSE(nan_value < nan_value);
+ EXPECT_FALSE(integer_value < nan_value);
+ EXPECT_FALSE(integer_value < nan_value);
+ EXPECT_TRUE(integer_value < double_value); // 10 < 10.1
+ EXPECT_FALSE(double_value < integer_value);
+ EXPECT_FALSE(integer_value < integer_value);
+ EXPECT_FALSE(double_value < double_value);
+
+ // Number comparison craziness
+ // Integers
+ EXPECT_TRUE(FieldValue::IntegerValue(1L) < FieldValue::IntegerValue(2L));
+ EXPECT_FALSE(FieldValue::IntegerValue(1L) < FieldValue::IntegerValue(1L));
+ EXPECT_FALSE(FieldValue::IntegerValue(2L) < FieldValue::IntegerValue(1L));
+ // Doubles
+ EXPECT_TRUE(FieldValue::DoubleValue(1.0) < FieldValue::DoubleValue(2.0));
+ EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::DoubleValue(1.0));
+ EXPECT_FALSE(FieldValue::DoubleValue(2.0) < FieldValue::DoubleValue(1.0));
+ EXPECT_TRUE(FieldValue::NanValue() < FieldValue::DoubleValue(1.0));
+ EXPECT_FALSE(FieldValue::NanValue() < FieldValue::NanValue());
+ EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::NanValue());
+ // Mixed
+ EXPECT_TRUE(FieldValue::DoubleValue(-1e20) <
+ FieldValue::IntegerValue(LLONG_MIN));
+ EXPECT_FALSE(FieldValue::DoubleValue(1e20) <
+ FieldValue::IntegerValue(LLONG_MAX));
+ EXPECT_TRUE(FieldValue::DoubleValue(1.234) < FieldValue::IntegerValue(2L));
+ EXPECT_FALSE(FieldValue::DoubleValue(2.345) < FieldValue::IntegerValue(1L));
+ EXPECT_FALSE(FieldValue::DoubleValue(1.0) < FieldValue::IntegerValue(1L));
+ EXPECT_FALSE(FieldValue::DoubleValue(1.234) < FieldValue::IntegerValue(1L));
+ EXPECT_FALSE(FieldValue::IntegerValue(LLONG_MIN) <
+ FieldValue::DoubleValue(-1e20));
+ EXPECT_TRUE(FieldValue::IntegerValue(LLONG_MAX) <
+ FieldValue::DoubleValue(1e20));
+ EXPECT_FALSE(FieldValue::IntegerValue(1) < FieldValue::DoubleValue(1.0));
+ EXPECT_TRUE(FieldValue::IntegerValue(1) < FieldValue::DoubleValue(1.234));
+}
+
+TEST(FieldValue, TimestampType) {
+ const FieldValue o = FieldValue::TimestampValue(Timestamp());
+ const FieldValue a = FieldValue::TimestampValue({100, 0});
+ const FieldValue b = FieldValue::TimestampValue({200, 0});
+ EXPECT_EQ(Type::Timestamp, a.type());
+ EXPECT_TRUE(o < a);
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(a < a);
+ const FieldValue c = FieldValue::ServerTimestampValue({100, 0});
+ const FieldValue d = FieldValue::ServerTimestampValue({200, 0}, {300, 0});
+ EXPECT_EQ(Type::ServerTimestamp, c.type());
+ EXPECT_EQ(Type::ServerTimestamp, d.type());
+ EXPECT_TRUE(c < d);
+ EXPECT_FALSE(c < c);
+ // Mixed
+ EXPECT_TRUE(o < c);
+ EXPECT_TRUE(a < c);
+ EXPECT_TRUE(b < c);
+ EXPECT_TRUE(b < d);
+ EXPECT_FALSE(c < o);
+ EXPECT_FALSE(c < a);
+ EXPECT_FALSE(c < b);
+ EXPECT_FALSE(d < b);
+}
+
+TEST(FieldValue, StringType) {
+ const FieldValue a = FieldValue::StringValue("abc");
+ std::string xyz("xyz");
+ const FieldValue b = FieldValue::StringValue(xyz);
+ const FieldValue c = FieldValue::StringValue(std::move(xyz));
+ EXPECT_EQ(Type::String, a.type());
+ EXPECT_EQ(Type::String, b.type());
+ EXPECT_EQ(Type::String, c.type());
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(a < a);
+}
+
+TEST(FieldValue, BlobType) {
+ const FieldValue a = FieldValue::BlobValue(Bytes("abc"), 4);
+ const FieldValue b = FieldValue::BlobValue(Bytes("def"), 4);
+ EXPECT_EQ(Type::Blob, a.type());
+ EXPECT_EQ(Type::Blob, b.type());
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(a < a);
+}
+
+TEST(FieldValue, GeoPointType) {
+ const FieldValue a = FieldValue::GeoPointValue({1, 2});
+ const FieldValue b = FieldValue::GeoPointValue({3, 4});
+ EXPECT_EQ(Type::GeoPoint, a.type());
+ EXPECT_EQ(Type::GeoPoint, b.type());
+ EXPECT_TRUE(a < b);
+ EXPECT_FALSE(a < a);
+}
+
TEST(FieldValue, ArrayType) {
const FieldValue empty =
FieldValue::ArrayValue(std::vector<const FieldValue>{});
@@ -64,6 +173,29 @@ TEST(FieldValue, ArrayType) {
EXPECT_FALSE(large < small);
}
+TEST(FieldValue, ObjectType) {
+ const FieldValue empty =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{});
+ std::map<const std::string, const FieldValue> object{
+ {"null", FieldValue::NullValue()},
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}};
+ // copy the map
+ const FieldValue small = FieldValue::ObjectValue(object);
+ std::map<const std::string, const FieldValue> another_object{
+ {"null", FieldValue::NullValue()}, {"true", FieldValue::FalseValue()}};
+ // move the array
+ const FieldValue large = FieldValue::ObjectValue(std::move(another_object));
+ EXPECT_EQ(Type::Object, empty.type());
+ EXPECT_EQ(Type::Object, small.type());
+ EXPECT_EQ(Type::Object, large.type());
+ EXPECT_TRUE(empty < small);
+ EXPECT_FALSE(small < empty);
+ EXPECT_FALSE(small < small);
+ EXPECT_TRUE(small < large);
+ EXPECT_FALSE(large < small);
+}
+
TEST(FieldValue, Copy) {
FieldValue clone = FieldValue::TrueValue();
const FieldValue null_value = FieldValue::NullValue();
@@ -82,6 +214,80 @@ TEST(FieldValue, Copy) {
clone = null_value;
EXPECT_EQ(FieldValue::NullValue(), clone);
+ const FieldValue nan_value = FieldValue::NanValue();
+ clone = nan_value;
+ EXPECT_EQ(FieldValue::NanValue(), clone);
+ EXPECT_EQ(FieldValue::NanValue(), nan_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::NanValue(), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue integer_value = FieldValue::IntegerValue(1L);
+ clone = integer_value;
+ EXPECT_EQ(FieldValue::IntegerValue(1L), clone);
+ EXPECT_EQ(FieldValue::IntegerValue(1L), integer_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::IntegerValue(1L), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue double_value = FieldValue::DoubleValue(1.0);
+ clone = double_value;
+ EXPECT_EQ(FieldValue::DoubleValue(1.0), clone);
+ EXPECT_EQ(FieldValue::DoubleValue(1.0), double_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::DoubleValue(1.0), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
+ clone = timestamp_value;
+ EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone);
+ EXPECT_EQ(FieldValue::TimestampValue({100, 200}), timestamp_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue server_timestamp_value =
+ FieldValue::ServerTimestampValue({1, 2}, {3, 4});
+ clone = server_timestamp_value;
+ EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}), clone);
+ EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}),
+ server_timestamp_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::ServerTimestampValue({1, 2}, {3, 4}), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue string_value = FieldValue::StringValue("abc");
+ clone = string_value;
+ EXPECT_EQ(FieldValue::StringValue("abc"), clone);
+ EXPECT_EQ(FieldValue::StringValue("abc"), string_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::StringValue("abc"), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
+ clone = blob_value;
+ EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone);
+ EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), blob_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
+ clone = geo_point_value;
+ EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone);
+ EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), geo_point_value);
+ clone = clone;
+ EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
const FieldValue array_value =
FieldValue::ArrayValue(std::vector<const FieldValue>{
FieldValue::TrueValue(), FieldValue::FalseValue()});
@@ -98,6 +304,30 @@ TEST(FieldValue, Copy) {
clone);
clone = null_value;
EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}});
+ clone = object_value;
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ clone);
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ object_value);
+ clone = clone;
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
}
TEST(FieldValue, Move) {
@@ -113,6 +343,48 @@ TEST(FieldValue, Move) {
clone = FieldValue::NullValue();
EXPECT_EQ(FieldValue::NullValue(), clone);
+ FieldValue nan_value = FieldValue::NanValue();
+ clone = std::move(nan_value);
+ EXPECT_EQ(FieldValue::NanValue(), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue integer_value = FieldValue::IntegerValue(1L);
+ clone = std::move(integer_value);
+ EXPECT_EQ(FieldValue::IntegerValue(1L), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue double_value = FieldValue::DoubleValue(1.0);
+ clone = std::move(double_value);
+ EXPECT_EQ(FieldValue::DoubleValue(1.0), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
+ clone = std::move(timestamp_value);
+ EXPECT_EQ(FieldValue::TimestampValue({100, 200}), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue string_value = FieldValue::StringValue("abc");
+ clone = std::move(string_value);
+ EXPECT_EQ(FieldValue::StringValue("abc"), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
+ clone = std::move(blob_value);
+ EXPECT_EQ(FieldValue::BlobValue(Bytes("abc"), 4), clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
+ clone = std::move(geo_point_value);
+ EXPECT_EQ(FieldValue::GeoPointValue({1, 2}), clone);
+ clone = null_value;
+ EXPECT_EQ(FieldValue::NullValue(), clone);
+
FieldValue array_value = FieldValue::ArrayValue(std::vector<const FieldValue>{
FieldValue::TrueValue(), FieldValue::FalseValue()});
clone = std::move(array_value);
@@ -121,15 +393,41 @@ TEST(FieldValue, Move) {
clone);
clone = FieldValue::NullValue();
EXPECT_EQ(FieldValue::NullValue(), clone);
+
+ FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}});
+ clone = std::move(object_value);
+ EXPECT_EQ(
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>{
+ {"true", FieldValue::TrueValue()},
+ {"false", FieldValue::FalseValue()}}),
+ clone);
+ clone = FieldValue::NullValue();
+ EXPECT_EQ(FieldValue::NullValue(), clone);
}
TEST(FieldValue, CompareMixedType) {
const FieldValue null_value = FieldValue::NullValue();
const FieldValue true_value = FieldValue::TrueValue();
+ const FieldValue number_value = FieldValue::NanValue();
+ const FieldValue timestamp_value = FieldValue::TimestampValue({100, 200});
+ const FieldValue string_value = FieldValue::StringValue("abc");
+ const FieldValue blob_value = FieldValue::BlobValue(Bytes("abc"), 4);
+ const FieldValue geo_point_value = FieldValue::GeoPointValue({1, 2});
const FieldValue array_value =
FieldValue::ArrayValue(std::vector<const FieldValue>());
+ const FieldValue object_value =
+ FieldValue::ObjectValue(std::map<const std::string, const FieldValue>());
EXPECT_TRUE(null_value < true_value);
- EXPECT_TRUE(true_value < array_value);
+ EXPECT_TRUE(true_value < number_value);
+ EXPECT_TRUE(number_value < timestamp_value);
+ EXPECT_TRUE(timestamp_value < string_value);
+ EXPECT_TRUE(string_value < blob_value);
+ EXPECT_TRUE(blob_value < geo_point_value);
+ EXPECT_TRUE(geo_point_value < array_value);
+ EXPECT_TRUE(array_value < object_value);
}
TEST(FieldValue, CompareWithOperator) {
diff --git a/Firestore/core/test/firebase/firestore/model/timestamp_test.cc b/Firestore/core/test/firebase/firestore/model/timestamp_test.cc
new file mode 100644
index 0000000..55ee378
--- /dev/null
+++ b/Firestore/core/test/firebase/firestore/model/timestamp_test.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Firestore/core/src/firebase/firestore/model/timestamp.h"
+
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace firebase {
+namespace firestore {
+namespace model {
+
+TEST(Timestamp, Getter) {
+ const Timestamp timestamp_zero;
+ EXPECT_EQ(0, timestamp_zero.seconds());
+ EXPECT_EQ(0, timestamp_zero.nanos());
+
+ const Timestamp timestamp(100, 200);
+ EXPECT_EQ(100, timestamp.seconds());
+ EXPECT_EQ(200, timestamp.nanos());
+
+ const Timestamp timestamp_now = Timestamp::Now();
+ EXPECT_LT(0, timestamp_now.seconds());
+ EXPECT_LE(0, timestamp_now.nanos());
+}
+
+TEST(Timestamp, Comparison) {
+ EXPECT_TRUE(Timestamp() < Timestamp(1, 2));
+ EXPECT_TRUE(Timestamp(1, 2) < Timestamp(2, 1));
+ EXPECT_TRUE(Timestamp(2, 1) < Timestamp(2, 2));
+}
+
+} // namespace model
+} // namespace firestore
+} // namespace firebase