aboutsummaryrefslogtreecommitdiffstats
path: root/document/src/tests/fieldvalue/referencefieldvalue_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'document/src/tests/fieldvalue/referencefieldvalue_test.cpp')
-rw-r--r--document/src/tests/fieldvalue/referencefieldvalue_test.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/document/src/tests/fieldvalue/referencefieldvalue_test.cpp b/document/src/tests/fieldvalue/referencefieldvalue_test.cpp
new file mode 100644
index 00000000000..1399c71fcc3
--- /dev/null
+++ b/document/src/tests/fieldvalue/referencefieldvalue_test.cpp
@@ -0,0 +1,193 @@
+// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/document/base/field.h>
+#include <vespa/document/datatype/referencedatatype.h>
+#include <vespa/document/fieldvalue/referencefieldvalue.h>
+#include <vespa/document/fieldvalue/stringfieldvalue.h>
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <ostream>
+#include <sstream>
+
+using namespace document;
+
+namespace {
+
+struct Fixture {
+ DocumentType docType{"foo"};
+ ReferenceDataType refType{docType, 12345};
+
+ DocumentType otherDocType{"bar"};
+ ReferenceDataType otherRefType{otherDocType, 54321};
+};
+
+}
+
+using vespalib::IllegalArgumentException;
+
+TEST_F("Default-constructed reference is empty and bound to type", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+ ASSERT_TRUE(fv.getDataType() != nullptr);
+ EXPECT_EQUAL(f.refType, *fv.getDataType());
+ ASSERT_FALSE(fv.hasValidDocumentId());
+}
+
+TEST_F("Reference can be constructed with document ID", Fixture) {
+ ReferenceFieldValue fv(f.refType, DocumentId("id:ns:foo::itsa-me"));
+ ASSERT_TRUE(fv.getDataType() != nullptr);
+ EXPECT_EQUAL(f.refType, *fv.getDataType());
+ ASSERT_TRUE(fv.hasValidDocumentId());
+ EXPECT_EQUAL(DocumentId("id:ns:foo::itsa-me"), fv.getDocumentId());
+}
+
+TEST_F("Newly constructed reference is marked as changed", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+ EXPECT_TRUE(fv.hasChanged());
+
+ ReferenceFieldValue fv2(f.refType, DocumentId("id:ns:foo::itsa-me"));
+ EXPECT_TRUE(fv2.hasChanged());
+}
+
+TEST_F("Exception is thrown if constructor doc ID type does not match referenced document type", Fixture) {
+ EXPECT_EXCEPTION(
+ ReferenceFieldValue(f.refType, DocumentId("id:ns:bar::wario-time")),
+ IllegalArgumentException,
+ "Can't assign document ID 'id:ns:bar::wario-time' (of type 'bar') "
+ "to reference of document type 'foo'");
+}
+
+TEST_F("Exception is thrown if doc ID does not have a type", Fixture) {
+ // Could have had a special cased message for this, but type-less IDs are
+ // not expected to be allowed through the feed pipeline at all. We just
+ // want to ensure it fails in a controlled fashion if encountered.
+ EXPECT_EXCEPTION(
+ ReferenceFieldValue(f.refType, DocumentId("doc:foo:bario")),
+ IllegalArgumentException,
+ "Can't assign document ID 'doc:foo:bario' (of type '') "
+ "to reference of document type 'foo'");
+}
+
+TEST_F("assign()ing a non-reference field value throws exception", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+ EXPECT_EXCEPTION(fv.assign(StringFieldValue("waluigi time!!")),
+ IllegalArgumentException,
+ "Can't assign field value of type String to a "
+ "ReferenceFieldValue");
+}
+
+TEST_F("Can explicitly assign new document ID to reference", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+ fv.setDeserializedDocumentId(DocumentId("id:ns:foo::yoshi-eggs"));
+
+ ASSERT_TRUE(fv.hasValidDocumentId());
+ EXPECT_EQUAL(DocumentId("id:ns:foo::yoshi-eggs"), fv.getDocumentId());
+ // Type remains unchanged
+ EXPECT_EQUAL(f.refType, *fv.getDataType());
+}
+
+TEST_F("Assigning explicit document ID clears changed-flag", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+ fv.setDeserializedDocumentId(DocumentId("id:ns:foo::yoshi-eggs"));
+ EXPECT_FALSE(fv.hasChanged());
+}
+
+TEST_F("Exception is thrown if explicitly assigned doc ID does not have same type as reference target type", Fixture) {
+ ReferenceFieldValue fv(f.refType);
+
+ EXPECT_EXCEPTION(
+ fv.setDeserializedDocumentId(DocumentId("id:ns:bar::another-castle")),
+ IllegalArgumentException,
+ "Can't assign document ID 'id:ns:bar::another-castle' (of type "
+ "'bar') to reference of document type 'foo'");
+}
+
+TEST_F("assign()ing another reference field value assigns doc ID and type", Fixture) {
+ ReferenceFieldValue src(f.refType, DocumentId("id:ns:foo::yoshi"));
+ ReferenceFieldValue dest(f.otherRefType);
+
+ dest.assign(src);
+ ASSERT_TRUE(dest.hasValidDocumentId());
+ EXPECT_EQUAL(src.getDocumentId(), dest.getDocumentId());
+ EXPECT_EQUAL(src.getDataType(), dest.getDataType());
+}
+
+// Different FieldValue subclasses actually disagree on whether this should be
+// the case, e.g. LiteralFieldValue and TensorFieldValue. We go with the
+// latter's approach, as that should be the most conservative one.
+TEST_F("assign() marks assignee as changed", Fixture) {
+ ReferenceFieldValue src(f.refType, DocumentId("id:ns:foo::yoshi"));
+ ReferenceFieldValue dest(f.refType);
+
+ dest.setDeserializedDocumentId(DocumentId("id:ns:foo::yoshi-eggs"));
+ EXPECT_FALSE(dest.hasChanged());
+
+ dest.assign(src);
+ EXPECT_TRUE(dest.hasChanged());
+}
+
+TEST_F("clone()ing creates new instance with same ID and type", Fixture) {
+ ReferenceFieldValue src(f.refType, DocumentId("id:ns:foo::yoshi"));
+
+ std::unique_ptr<ReferenceFieldValue> cloned(src.clone());
+ ASSERT_TRUE(cloned.get() != nullptr);
+ ASSERT_TRUE(cloned->hasValidDocumentId());
+ EXPECT_EQUAL(src.getDocumentId(), cloned->getDocumentId());
+ EXPECT_EQUAL(src.getDataType(), cloned->getDataType());
+ EXPECT_TRUE(cloned->hasChanged());
+}
+
+TEST_F("Can clone() value without document ID", Fixture) {
+ ReferenceFieldValue src(f.refType);
+
+ std::unique_ptr<ReferenceFieldValue> cloned(src.clone());
+ ASSERT_TRUE(cloned.get() != nullptr);
+ EXPECT_FALSE(cloned->hasValidDocumentId());
+ EXPECT_EQUAL(src.getDataType(), cloned->getDataType());
+ EXPECT_TRUE(cloned->hasChanged());
+}
+
+TEST_F("compare() orders first on type ID, then on document ID", Fixture) {
+ // foo type has id 12345
+ ReferenceFieldValue fvType1Id1(f.refType, DocumentId("id:ns:foo::AA"));
+ ReferenceFieldValue fvType1Id2(f.refType, DocumentId("id:ns:foo::AB"));
+ // bar type has id 54321
+ ReferenceFieldValue fvType2Id1(f.otherRefType, DocumentId("id:ns:bar::AA"));
+ ReferenceFieldValue fvType2Id2(f.otherRefType, DocumentId("id:ns:bar::AB"));
+
+ // Different types
+ EXPECT_TRUE(fvType1Id1.compare(fvType2Id1) < 0);
+ EXPECT_TRUE(fvType2Id1.compare(fvType1Id1) > 0);
+
+ // Same types, different IDs
+ EXPECT_TRUE(fvType1Id1.compare(fvType1Id2) < 0);
+ EXPECT_TRUE(fvType1Id2.compare(fvType1Id1) > 0);
+ EXPECT_TRUE(fvType2Id1.compare(fvType2Id2) < 0);
+
+ // Different types and IDs
+ EXPECT_TRUE(fvType1Id1.compare(fvType2Id2) < 0);
+ EXPECT_TRUE(fvType2Id2.compare(fvType1Id1) > 0);
+
+ // Equal types and ID
+ EXPECT_EQUAL(0, fvType1Id1.compare(fvType1Id1));
+ EXPECT_EQUAL(0, fvType1Id2.compare(fvType1Id2));
+ EXPECT_EQUAL(0, fvType2Id1.compare(fvType2Id1));
+}
+
+TEST_F("print() includes reference type and document ID", Fixture) {
+ ReferenceFieldValue src(f.refType, DocumentId("id:ns:foo::yoshi"));
+ std::ostringstream ss;
+ src.print(ss, false, "");
+ EXPECT_EQUAL("ReferenceFieldValue(ReferenceDataType(foo, id 12345), "
+ "DocumentId(id:ns:foo::yoshi))", ss.str());
+}
+
+TEST_F("print() only indents start of output line", Fixture) {
+ ReferenceFieldValue src(f.refType, DocumentId("id:ns:foo::yoshi"));
+ std::ostringstream ss;
+ src.print(ss, false, " ");
+ EXPECT_EQUAL(" ReferenceFieldValue(ReferenceDataType(foo, id 12345), "
+ "DocumentId(id:ns:foo::yoshi))", ss.str());
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
+