diff options
Diffstat (limited to 'document/src')
12 files changed, 132 insertions, 112 deletions
diff --git a/document/src/main/java/com/yahoo/document/DocumentUpdate.java b/document/src/main/java/com/yahoo/document/DocumentUpdate.java index d3063b76feb..20d9b352d2d 100644 --- a/document/src/main/java/com/yahoo/document/DocumentUpdate.java +++ b/document/src/main/java/com/yahoo/document/DocumentUpdate.java @@ -147,7 +147,7 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP Map.Entry<Integer, FieldUpdate> entry = iter.next(); FieldUpdate update = entry.getValue(); if (!update.isEmpty()) { - ValueUpdate last = update.getValueUpdate(update.size() - 1); + ValueUpdate<?> last = update.getValueUpdate(update.size() - 1); if (last instanceof AssignValueUpdate) { FieldValue currentValue = doc.getFieldValue(update.getField()); if ((currentValue != null) && currentValue.equals(last.getValue())) { @@ -190,7 +190,7 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP /** Returns the type of the document this updates * - * @return The documentype of the document + * @return The document type of the document */ public DocumentType getDocumentType() { return documentType; @@ -357,9 +357,7 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof DocumentUpdate)) return false; - - DocumentUpdate that = (DocumentUpdate) o; + if (!(o instanceof DocumentUpdate that)) return false; if (docId != null ? !docId.equals(that.docId) : that.docId != null) return false; if (documentType != null ? !documentType.equals(that.documentType) : that.documentType != null) return false; @@ -413,7 +411,7 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP } /** - * Returns whether or not this field update contains any field- or field path updates. + * Returns whether this field update contains any field- or field path updates. * * @return True if this update is empty. */ diff --git a/document/src/main/java/com/yahoo/document/datatypes/StringFieldValue.java b/document/src/main/java/com/yahoo/document/datatypes/StringFieldValue.java index 8b4b94f6bbf..d09967f973f 100644 --- a/document/src/main/java/com/yahoo/document/datatypes/StringFieldValue.java +++ b/document/src/main/java/com/yahoo/document/datatypes/StringFieldValue.java @@ -29,6 +29,9 @@ import java.util.Objects; */ public class StringFieldValue extends FieldValue { + // TODO: remove this, it's a temporary workaround for invalid data stored before unicode validation was fixed + private static final boolean replaceInvalidUnicode = System.getProperty("vespa.replace_invalid_unicode", "false").equals("true"); + private static class Factory extends PrimitiveDataType.Factory { @Override public FieldValue create() { return new StringFieldValue(); } @Override public FieldValue create(String value) { return new StringFieldValue(value); } @@ -56,16 +59,17 @@ public class StringFieldValue extends FieldValue { setValue(value); } - private static void validateTextString(String value) { + private static String validateTextString(String value) { if ( ! Text.isValidTextString(value)) { - throw new IllegalArgumentException("The string field value contains illegal code point 0x" + - Integer.toHexString(Text.validateTextString(value).getAsInt()).toUpperCase()); + if (replaceInvalidUnicode) return Text.stripInvalidCharacters(value); + else throw new IllegalArgumentException("The string field value contains illegal code point 0x" + + Integer.toHexString(Text.validateTextString(value).getAsInt()).toUpperCase()); } + return value; } private void setValue(String value) { - validateTextString(value); - this.value = value; + this.value = validateTextString(value); } /** diff --git a/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java b/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java index 7b1042903ec..ed6bdc721a0 100644 --- a/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java +++ b/document/src/main/java/com/yahoo/document/json/JsonSerializationHelper.java @@ -347,4 +347,5 @@ public class JsonSerializationHelper { wrapIOException(() -> generator.writeFieldName(field.getName())); } } + } diff --git a/document/src/main/java/com/yahoo/document/json/JsonWriter.java b/document/src/main/java/com/yahoo/document/json/JsonWriter.java index 7e82e830064..2b0ba138466 100644 --- a/document/src/main/java/com/yahoo/document/json/JsonWriter.java +++ b/document/src/main/java/com/yahoo/document/json/JsonWriter.java @@ -7,7 +7,9 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.StreamReadConstraints; import com.yahoo.document.Document; import com.yahoo.document.DocumentId; +import com.yahoo.document.DocumentRemove; import com.yahoo.document.DocumentType; +import com.yahoo.document.DocumentUpdate; import com.yahoo.document.Field; import com.yahoo.document.annotation.AnnotationReference; import com.yahoo.document.datatypes.Array; @@ -263,6 +265,26 @@ public class JsonWriter implements DocumentWriter { // NOP, fetched from Document } + @Override + public void write(DocumentRemove documentRemove) { + try { + generator.writeStartObject(); + + serializeStringField(generator, new FieldBase("remove"), new StringFieldValue(documentRemove.getId().toString())); + + generator.writeEndObject(); + generator.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void write(DocumentUpdate documentUpdate) { + var serializer = new DocumentUpdateJsonSerializer(generator); + serializer.serialize(documentUpdate); + } + /** * Utility method to easily serialize a single document. * diff --git a/document/src/main/java/com/yahoo/document/serialization/DocumentUpdateWriter.java b/document/src/main/java/com/yahoo/document/serialization/DocumentUpdateWriter.java index 0202ec8bf23..b9cc439ad54 100644 --- a/document/src/main/java/com/yahoo/document/serialization/DocumentUpdateWriter.java +++ b/document/src/main/java/com/yahoo/document/serialization/DocumentUpdateWriter.java @@ -17,8 +17,7 @@ import com.yahoo.document.update.TensorRemoveUpdate; /** * Interface for writing document updates in custom serializers. * - * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> - * @since 5.1.27 + * @author Einar M R Rosenvinge */ public interface DocumentUpdateWriter { void write(DocumentUpdate update); diff --git a/document/src/main/java/com/yahoo/document/serialization/DocumentWriter.java b/document/src/main/java/com/yahoo/document/serialization/DocumentWriter.java index c84140c9ea0..16125926fe6 100644 --- a/document/src/main/java/com/yahoo/document/serialization/DocumentWriter.java +++ b/document/src/main/java/com/yahoo/document/serialization/DocumentWriter.java @@ -3,7 +3,9 @@ package com.yahoo.document.serialization; import com.yahoo.document.Document; import com.yahoo.document.DocumentId; +import com.yahoo.document.DocumentRemove; import com.yahoo.document.DocumentType; +import com.yahoo.document.DocumentUpdate; /** * @author ravishar @@ -17,4 +19,8 @@ public interface DocumentWriter extends FieldWriter { void write(DocumentType type); + void write(DocumentRemove documentRemove); + + void write(DocumentUpdate documentUpdate); + } diff --git a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java index 17ab3890bcf..4cb836860be 100644 --- a/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java +++ b/document/src/main/java/com/yahoo/document/serialization/VespaDocumentSerializer6.java @@ -8,6 +8,7 @@ import com.yahoo.document.CollectionDataType; import com.yahoo.document.DataType; import com.yahoo.document.Document; import com.yahoo.document.DocumentId; +import com.yahoo.document.DocumentRemove; import com.yahoo.document.DocumentType; import com.yahoo.document.DocumentUpdate; import com.yahoo.document.Field; @@ -426,6 +427,10 @@ public class VespaDocumentSerializer6 extends BufferSerializer implements Docume putShort(null, (short) 0); // Used to hold the version. Is now always 0. } + public void write(DocumentRemove documentRemove) { + throw new UnsupportedOperationException("serializing remove not implemented"); + } + public void write(Annotation annotation) { buf.putInt(annotation.getType().getId()); //name hash diff --git a/document/src/tests/serialization/CMakeLists.txt b/document/src/tests/serialization/CMakeLists.txt index e5bf91f401d..8a997299e38 100644 --- a/document/src/tests/serialization/CMakeLists.txt +++ b/document/src/tests/serialization/CMakeLists.txt @@ -11,5 +11,6 @@ vespa_add_executable(document_annotationserializer_test_app TEST annotationserializer_test.cpp DEPENDS document + GTest::gtest ) vespa_add_test(NAME document_annotationserializer_test_app COMMAND document_annotationserializer_test_app) diff --git a/document/src/tests/serialization/annotationserializer_test.cpp b/document/src/tests/serialization/annotationserializer_test.cpp index 14edd38d0b0..3499f9d8012 100644 --- a/document/src/tests/serialization/annotationserializer_test.cpp +++ b/document/src/tests/serialization/annotationserializer_test.cpp @@ -11,11 +11,13 @@ #include <vespa/document/serialization/vespadocumentdeserializer.h> #include <vespa/document/serialization/vespadocumentserializer.h> #include <vespa/document/repo/documenttyperepo.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/objects/nbostream.h> -#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/testkit/test_path.h> #include <vespa/fastos/file.h> -#include <fstream> #include <algorithm> +#include <fstream> +#include <optional> using std::fstream; @@ -27,35 +29,11 @@ using namespace document; namespace { -class Test : public vespalib::TestApp { - StringFieldValue::SpanTrees readSpanTree(const string &file_name, const FixedTypeRepo &repo); - - void requireThatSimpleSpanTreeIsDeserialized(); - void requireThatAdvancedSpanTreeIsDeserialized(); - void requireThatSpanTreeCanBeSerialized(); - void requireThatUnknownAnnotationIsSkipped(); - -public: - int Main() override; -}; - -int -Test::Main() -{ - if (getenv("TEST_SUBSET") != 0) { return 0; } - TEST_INIT("annotationserializer_test"); - TEST_DO(requireThatSimpleSpanTreeIsDeserialized()); - TEST_DO(requireThatAdvancedSpanTreeIsDeserialized()); - TEST_DO(requireThatSpanTreeCanBeSerialized()); - TEST_DO(requireThatUnknownAnnotationIsSkipped()); - - TEST_DONE(); -} - template <typename T, int N> int arraysize(const T (&)[N]) { return N; } -StringFieldValue::SpanTrees -Test::readSpanTree(const string &file_name, const FixedTypeRepo &repo) { +void +read_span_trees(const string &file_name, const FixedTypeRepo &repo, std::optional<StringFieldValue::SpanTrees>& span_trees) +{ FastOS_File file(file_name.c_str()); ASSERT_TRUE(file.OpenReadOnlyExisting()); char buffer[1024]; @@ -67,26 +45,31 @@ Test::readSpanTree(const string &file_name, const FixedTypeRepo &repo) { StringFieldValue value; deserializer.read(value); - EXPECT_EQUAL(0u, stream.size()); + EXPECT_EQ(0u, stream.size()); ASSERT_TRUE(value.hasSpanTrees()); - return value.getSpanTrees(); + span_trees = value.getSpanTrees(); } -void Test::requireThatSimpleSpanTreeIsDeserialized() { +} + +TEST(AnnotationSerializerTest, require_that_simple_span_tree_is_deserialized) +{ DocumentTypeRepo type_repo(readDocumenttypesConfig(TEST_PATH("annotation.serialize.test.repo.cfg"))); FixedTypeRepo repo(type_repo); - SpanTree::UP span_tree = std::move(readSpanTree(TEST_PATH("test_data_serialized_simple"), repo).front()); + std::optional<StringFieldValue::SpanTrees> span_trees; + ASSERT_NO_FATAL_FAILURE(read_span_trees(TEST_PATH("test_data_serialized_simple"), repo, span_trees)); + auto span_tree = std::move(span_trees.value().front()); - EXPECT_EQUAL("html", span_tree->getName()); + EXPECT_EQ("html", span_tree->getName()); const SimpleSpanList *root = dynamic_cast<const SimpleSpanList *>(&span_tree->getRoot()); ASSERT_TRUE(root); - EXPECT_EQUAL(5u, root->size()); + EXPECT_EQ(5u, root->size()); SimpleSpanList::const_iterator it = root->begin(); - EXPECT_EQUAL(Span(0, 19), (*it++)); - EXPECT_EQUAL(Span(19, 5), (*it++)); - EXPECT_EQUAL(Span(24, 21), (*it++)); - EXPECT_EQUAL(Span(45, 23), (*it++)); - EXPECT_EQUAL(Span(68, 14), (*it++)); + EXPECT_EQ(Span(0, 19), (*it++)); + EXPECT_EQ(Span(19, 5), (*it++)); + EXPECT_EQ(Span(24, 21), (*it++)); + EXPECT_EQ(Span(45, 23), (*it++)); + EXPECT_EQ(Span(68, 14), (*it++)); EXPECT_TRUE(it == root->end()); } @@ -107,49 +90,51 @@ struct AnnotationComparator { void compare() { std::sort(expect.begin(), expect.end()); std::sort(actual.begin(), actual.end()); - EXPECT_EQUAL(expect.size(), actual.size()); + EXPECT_EQ(expect.size(), actual.size()); for (size_t i = 0; i < expect.size() && i < actual.size(); ++i) { - EXPECT_EQUAL(expect[i].size(), actual[i].size()); - EXPECT_EQUAL(expect[i], actual[i]); + EXPECT_EQ(expect[i].size(), actual[i].size()); + EXPECT_EQ(expect[i], actual[i]); } } }; -void Test::requireThatAdvancedSpanTreeIsDeserialized() { +TEST(AnnotationSerializerTest, require_that_advanced_span_tree_is_deserialized) +{ DocumentTypeRepo type_repo(readDocumenttypesConfig(TEST_PATH("annotation.serialize.test.repo.cfg"))); FixedTypeRepo repo(type_repo, "my_document"); - SpanTree::UP span_tree = std::move(readSpanTree(TEST_PATH("test_data_serialized_advanced"), - repo).front()); + std::optional<StringFieldValue::SpanTrees> span_trees; + ASSERT_NO_FATAL_FAILURE(read_span_trees(TEST_PATH("test_data_serialized_advanced"), repo, span_trees)); + auto span_tree = std::move(span_trees.value().front()); - EXPECT_EQUAL("html", span_tree->getName()); + EXPECT_EQ("html", span_tree->getName()); const SpanList *root = dynamic_cast<const SpanList *>(&span_tree->getRoot()); ASSERT_TRUE(root); - EXPECT_EQUAL(4u, root->size()); + EXPECT_EQ(4u, root->size()); SpanList::const_iterator it = root->begin(); - EXPECT_EQUAL(Span(0, 6), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(0, 6), *(static_cast<Span *>(*it++))); AlternateSpanList *alt_list = dynamic_cast<AlternateSpanList *>(*it++); - EXPECT_EQUAL(Span(27, 9), *(static_cast<Span *>(*it++))); - EXPECT_EQUAL(Span(36, 8), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(27, 9), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(36, 8), *(static_cast<Span *>(*it++))); EXPECT_TRUE(it == root->end()); ASSERT_TRUE(alt_list); - EXPECT_EQUAL(2u, alt_list->getNumSubtrees()); - EXPECT_EQUAL(0.9, alt_list->getProbability(0)); - EXPECT_EQUAL(0.1, alt_list->getProbability(1)); - EXPECT_EQUAL(4u, alt_list->getSubtree(0).size()); + EXPECT_EQ(2u, alt_list->getNumSubtrees()); + EXPECT_EQ(0.9, alt_list->getProbability(0)); + EXPECT_EQ(0.1, alt_list->getProbability(1)); + EXPECT_EQ(4u, alt_list->getSubtree(0).size()); it = alt_list->getSubtree(0).begin(); - EXPECT_EQUAL(Span(6, 3), *(static_cast<Span *>(*it++))); - EXPECT_EQUAL(Span(9, 10), *(static_cast<Span *>(*it++))); - EXPECT_EQUAL(Span(19, 4), *(static_cast<Span *>(*it++))); - EXPECT_EQUAL(Span(23, 4), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(6, 3), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(9, 10), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(19, 4), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(23, 4), *(static_cast<Span *>(*it++))); EXPECT_TRUE(it == alt_list->getSubtree(0).end()); - EXPECT_EQUAL(2u, alt_list->getSubtree(1).size()); + EXPECT_EQ(2u, alt_list->getSubtree(1).size()); it = alt_list->getSubtree(1).begin(); - EXPECT_EQUAL(Span(6, 13), *(static_cast<Span *>(*it++))); - EXPECT_EQUAL(Span(19, 8), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(6, 13), *(static_cast<Span *>(*it++))); + EXPECT_EQ(Span(19, 8), *(static_cast<Span *>(*it++))); EXPECT_TRUE(it == alt_list->getSubtree(1).end()); - EXPECT_EQUAL(12u, span_tree->numAnnotations()); + EXPECT_EQ(12u, span_tree->numAnnotations()); AnnotationComparator comparator; comparator.addActual(span_tree->begin(), span_tree->end()) @@ -213,10 +198,11 @@ void Test::requireThatAdvancedSpanTreeIsDeserialized() { " AnnotationReferenceFieldValue(n)\n" " )\n" "))"); - TEST_DO(comparator.compare()); + comparator.compare(); } -void Test::requireThatSpanTreeCanBeSerialized() { +TEST(AnnotationSerializerTest, require_that_span_tree_can_be_serialized) +{ DocumentTypeRepo type_repo( readDocumenttypesConfig(TEST_PATH("annotation.serialize.test.repo.cfg"))); FixedTypeRepo repo(type_repo, "my_document"); @@ -233,25 +219,26 @@ void Test::requireThatSpanTreeCanBeSerialized() { StringFieldValue value; deserializer.read(value); - SpanTree::UP span_tree = std::move(value.getSpanTrees().front()); - EXPECT_EQUAL("html", span_tree->getName()); - EXPECT_EQUAL(0u, stream.size()); + auto span_tree = std::move(value.getSpanTrees().front()); + EXPECT_EQ("html", span_tree->getName()); + EXPECT_EQ(0u, stream.size()); stream.clear(); VespaDocumentSerializer serializer(stream); serializer.write(value); - EXPECT_EQUAL(size, static_cast<ssize_t>(stream.size())); + EXPECT_EQ(size, static_cast<ssize_t>(stream.size())); int diff_count = 0; for (size_t i = 0; i < stream.size(); ++i) { if (buffer[i] != stream.peek()[i]) { ++diff_count; } - EXPECT_EQUAL((int) buffer[i], (int) stream.peek()[i]); + EXPECT_EQ((int) buffer[i], (int) stream.peek()[i]); } - EXPECT_EQUAL(0, diff_count); + EXPECT_EQ(0, diff_count); } -void Test::requireThatUnknownAnnotationIsSkipped() { +TEST(AnnotationSerializerTest, require_that_unknown_annotation_is_skipped) +{ AnnotationType type(42, "my type"); Annotation annotation(type, FieldValue::UP(new StringFieldValue("foo"))); nbostream stream; @@ -264,9 +251,7 @@ void Test::requireThatUnknownAnnotationIsSkipped() { Annotation a; deserializer.readAnnotation(a); EXPECT_FALSE(a.valid()); - EXPECT_EQUAL(0u, stream.size()); + EXPECT_EQ(0u, stream.size()); } -} // namespace - -TEST_APPHOOK(Test); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/document/src/tests/struct_anno/CMakeLists.txt b/document/src/tests/struct_anno/CMakeLists.txt index 855cba606a3..2d688454058 100644 --- a/document/src/tests/struct_anno/CMakeLists.txt +++ b/document/src/tests/struct_anno/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(document_struct_anno_test_app TEST struct_anno_test.cpp DEPENDS document + GTest::gtest ) vespa_add_test(NAME document_struct_anno_test_app COMMAND document_struct_anno_test_app) diff --git a/document/src/tests/struct_anno/struct_anno_test.cpp b/document/src/tests/struct_anno/struct_anno_test.cpp index 31d3900c6bc..8ab883c6de1 100644 --- a/document/src/tests/struct_anno/struct_anno_test.cpp +++ b/document/src/tests/struct_anno/struct_anno_test.cpp @@ -11,8 +11,9 @@ #include <vespa/document/serialization/vespadocumentdeserializer.h> #include <vespa/document/serialization/vespadocumentserializer.h> #include <vespa/document/repo/documenttyperepo.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/objects/nbostream.h> -#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/testkit/test_path.h> #include <vespa/fastos/file.h> using std::ostringstream; @@ -23,23 +24,12 @@ using namespace document; namespace { -class Test : public vespalib::TestApp { - void requireThatStructFieldsCanContainAnnotations(); - -public: - int Main() override; -}; +template <typename T, int N> int arraysize(const T (&)[N]) { return N; } -int Test::Main() { - if (getenv("TEST_SUBSET") != 0) { return 0; } - TEST_INIT("struct_anno_test"); - TEST_DO(requireThatStructFieldsCanContainAnnotations()); - TEST_DONE(); } -template <typename T, int N> int arraysize(const T (&)[N]) { return N; } - -void Test::requireThatStructFieldsCanContainAnnotations() { +TEST(StructAnnoTest, require_that_struct_fields_can_contain_annotations) +{ DocumentTypeRepo repo(readDocumenttypesConfig(TEST_PATH("documenttypes.cfg"))); FastOS_File file(TEST_PATH("document.dat").c_str()); @@ -62,19 +52,17 @@ void Test::requireThatStructFieldsCanContainAnnotations() { const StringFieldValue *str = dynamic_cast<const StringFieldValue*>(strRef.get()); ASSERT_TRUE(str != NULL); - SpanTree::UP tree = std::move(str->getSpanTrees().front()); + auto tree = std::move(str->getSpanTrees().front()); - EXPECT_EQUAL("my_tree", tree->getName()); + EXPECT_EQ("my_tree", tree->getName()); const SimpleSpanList *root = dynamic_cast<const SimpleSpanList*>(&tree->getRoot()); ASSERT_TRUE(root != NULL); - EXPECT_EQUAL(1u, root->size()); + EXPECT_EQ(1u, root->size()); SimpleSpanList::const_iterator it = root->begin(); - EXPECT_EQUAL(Span(0, 6), (*it++)); + EXPECT_EQ(Span(0, 6), (*it++)); EXPECT_TRUE(it == root->end()); - EXPECT_EQUAL(1u, tree->numAnnotations()); + EXPECT_EQ(1u, tree->numAnnotations()); } -} // namespace - -TEST_APPHOOK(Test); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/document/src/vespa/document/select/branch.cpp b/document/src/vespa/document/select/branch.cpp index 02f767c96b5..6035b5fe9a0 100644 --- a/document/src/vespa/document/select/branch.cpp +++ b/document/src/vespa/document/select/branch.cpp @@ -39,6 +39,10 @@ namespace { ResultList traceAndValue(const T& value, std::ostream& out, const Node& leftNode, const Node& rightNode) { + out << "And (lhs):\n"; + (void)leftNode.trace(value, out); + out << "And (rhs):\n"; + (void)rightNode.trace(value, out); out << "And - Left branch returned " << leftNode.contains(value) << ".\n"; out << "And - Right branch returned " << rightNode.contains(value) << ".\n"; return leftNode.contains(value) && rightNode.contains(value); @@ -83,6 +87,10 @@ namespace { ResultList traceOrValue(const T& value, std::ostream& out, const Node& leftNode, const Node& rightNode) { + out << "Or (lhs):\n"; + (void)leftNode.trace(value, out); + out << "Or (rhs):\n"; + (void)rightNode.trace(value, out); out << "Or - Left branch returned " << leftNode.contains(value) << ".\n"; out << "Or - Right branch returned " << rightNode.contains(value) << ".\n"; return leftNode.contains(value) || rightNode.contains(value); @@ -122,6 +130,8 @@ namespace { template<typename T> ResultList traceNotValue(const T& value, std::ostream& out, const Node& node) { + out << "Not:\n"; + (void)node.trace(value, out); out << "Not - Child returned " << node.contains(value) << ". Returning opposite.\n"; return !node.contains(value); |