aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/objects
diff options
context:
space:
mode:
authorHenning Baldersheim <balder@yahoo-inc.com>2022-05-19 21:57:59 +0000
committerHenning Baldersheim <balder@yahoo-inc.com>2022-05-20 12:38:48 +0000
commit64500ab17deb86b394edc81f4ad42b5a2c43fe30 (patch)
tree64334ba1513b697dacd5068981a8ee5b7ad92f3b /vespalib/src/tests/objects
parentcfa6ec5cdbd1cf39558d3f85101de05230d6c225 (diff)
Fold staging_vespalib into vespalib
Diffstat (limited to 'vespalib/src/tests/objects')
-rw-r--r--vespalib/src/tests/objects/identifiable/.gitignore5
-rw-r--r--vespalib/src/tests/objects/identifiable/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/objects/identifiable/identifiable_test.cpp338
-rw-r--r--vespalib/src/tests/objects/identifiable/namedobject.cpp19
-rw-r--r--vespalib/src/tests/objects/identifiable/namedobject.h23
-rw-r--r--vespalib/src/tests/objects/objectdump/.gitignore4
-rw-r--r--vespalib/src/tests/objects/objectdump/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/objects/objectdump/objectdump.cpp115
-rw-r--r--vespalib/src/tests/objects/objectselection/.gitignore4
-rw-r--r--vespalib/src/tests/objects/objectselection/CMakeLists.txt8
-rw-r--r--vespalib/src/tests/objects/objectselection/objectselection.cpp94
11 files changed, 627 insertions, 0 deletions
diff --git a/vespalib/src/tests/objects/identifiable/.gitignore b/vespalib/src/tests/objects/identifiable/.gitignore
new file mode 100644
index 00000000000..a547ace8ee4
--- /dev/null
+++ b/vespalib/src/tests/objects/identifiable/.gitignore
@@ -0,0 +1,5 @@
+.depend
+Makefile
+asciistream_test
+identifiable_test
+vespalib_identifiable_test_app
diff --git a/vespalib/src/tests/objects/identifiable/CMakeLists.txt b/vespalib/src/tests/objects/identifiable/CMakeLists.txt
new file mode 100644
index 00000000000..c4aefa44350
--- /dev/null
+++ b/vespalib/src/tests/objects/identifiable/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_identifiable_test_app TEST
+ SOURCES
+ identifiable_test.cpp
+ namedobject.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_identifiable_test_app COMMAND vespalib_identifiable_test_app)
diff --git a/vespalib/src/tests/objects/identifiable/identifiable_test.cpp b/vespalib/src/tests/objects/identifiable/identifiable_test.cpp
new file mode 100644
index 00000000000..b3adfbfa9e2
--- /dev/null
+++ b/vespalib/src/tests/objects/identifiable/identifiable_test.cpp
@@ -0,0 +1,338 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "namedobject.h"
+#include <vespa/vespalib/objects/identifiable.hpp>
+#include <vespa/vespalib/objects/nbostream.h>
+#include <vespa/vespalib/util/exceptions.h>
+#include <vespa/vespalib/testkit/testapp.h>
+
+using namespace vespalib;
+
+class IdentifiableTest : public TestApp {
+ void requireThatIdentifiableCastCanCastPointers();
+ void requireThatIdentifiableCastCanCastReferences();
+ void testNamedObject();
+ void testNboStream();
+ template <typename T>
+ void testStream(const T & a);
+ void testNboSerializer();
+ template <typename T>
+ void testSerializer(const T & a);
+public:
+ int Main() override;
+};
+
+#define CID_Abstract 0x700000
+#define CID_A 0x700001
+#define CID_B 0x700002
+#define CID_C 0x700003
+
+class Abstract : public Identifiable
+{
+public:
+ DECLARE_IDENTIFIABLE_ABSTRACT(Abstract);
+ virtual ~Abstract() { }
+ virtual void someAbstractVirtualMethod() = 0;
+};
+
+class A : public Abstract
+{
+public:
+ DECLARE_IDENTIFIABLE(A);
+ A() { }
+ void someAbstractVirtualMethod() override { };
+};
+
+class B : public A
+{
+public:
+ DECLARE_IDENTIFIABLE(B);
+ B() { }
+};
+
+class C : public Identifiable
+{
+private:
+ int _value;
+
+public:
+ DECLARE_IDENTIFIABLE(C);
+ C() : _value(0) {}
+ C(int value) : _value(value) {}
+ C *clone() const { return new C(*this); }
+ virtual int cmp(const Identifiable &rhs) const {
+ int result(cmpClassId(rhs));
+ if (result == 0) {
+ result = _value - static_cast<const C &>(rhs)._value;
+ }
+ return result;
+ }
+};
+
+IMPLEMENT_IDENTIFIABLE_ABSTRACT(Abstract, Identifiable);
+IMPLEMENT_IDENTIFIABLE(A, Abstract);
+IMPLEMENT_IDENTIFIABLE(B, A);
+IMPLEMENT_IDENTIFIABLE(C, Identifiable);
+
+void
+IdentifiableTest::testNamedObject()
+{
+ NamedObject a("first"), b("second");;
+ nbostream os;
+ NBOSerializer nos(os);
+ nos << a << b;
+ EXPECT_EQUAL(27u,os.size());
+ Identifiable::UP o1;
+ o1 = Identifiable::create(nos);
+ EXPECT_EQUAL(14u, os.size());
+ ASSERT_TRUE(o1->inherits(NamedObject::classId));
+ ASSERT_TRUE(o1->getClass().id() == NamedObject::classId);
+ EXPECT_TRUE(static_cast<const NamedObject &>(*o1).getName() == "first");
+ o1 = Identifiable::create(nos);
+ EXPECT_EQUAL(0u, os.size());
+ ASSERT_TRUE(o1->inherits(NamedObject::classId));
+ ASSERT_TRUE(o1->getClass().id() == NamedObject::classId);
+ EXPECT_TRUE(static_cast<const NamedObject &>(*o1).getName() == "second");
+}
+
+template <typename T>
+void IdentifiableTest::testStream(const T & a)
+{
+ nbostream s;
+ s << a;
+ T b;
+ s >> b;
+ EXPECT_TRUE(s.empty());
+ EXPECT_EQUAL(a, b);
+ EXPECT_EQUAL(nbostream::ok, s.state());
+ EXPECT_TRUE(s.good());
+}
+
+template <typename T>
+void IdentifiableTest::testSerializer(const T & a)
+{
+ nbostream t;
+ NBOSerializer s(t);
+ s << a;
+ T b;
+ s >> b;
+ EXPECT_TRUE(s.getStream().empty());
+ EXPECT_EQUAL(a, b);
+ EXPECT_EQUAL(nbostream::ok, s.getStream().state());
+}
+
+void IdentifiableTest::testNboSerializer()
+{
+ testSerializer(true);
+ testSerializer(false);
+ testSerializer(static_cast<int8_t>('a'));
+ testSerializer(static_cast<uint8_t>(156));
+ testSerializer(static_cast<int16_t>(156));
+ testSerializer(static_cast<int32_t>(156));
+ testSerializer(static_cast<int64_t>(156));
+ testSerializer(static_cast<uint16_t>(156));
+ testSerializer(static_cast<uint32_t>(156));
+ testSerializer(static_cast<uint64_t>(156));
+ testSerializer(static_cast<float>(156));
+ testSerializer(static_cast<double>(156));
+ testSerializer(vespalib::string("abcdefgh"));
+}
+
+void IdentifiableTest::testNboStream()
+{
+ testStream(true);
+ testStream(false);
+ testStream('a');
+ testStream(static_cast<unsigned char>(156));
+ testStream(static_cast<int16_t>(156));
+ testStream(static_cast<int32_t>(156));
+ testStream(static_cast<int64_t>(156));
+ testStream(static_cast<uint16_t>(156));
+ testStream(static_cast<uint32_t>(156));
+ testStream(static_cast<uint64_t>(156));
+ testStream(static_cast<float>(156));
+ testStream(static_cast<double>(156));
+ testStream(std::string("abcdefgh"));
+ testStream(vespalib::string("abcdefgh"));
+ {
+ nbostream s(4);
+ EXPECT_EQUAL(4u, s.capacity());
+ s << "abcdef";
+ EXPECT_EQUAL(nbostream::ok, s.state());
+ EXPECT_EQUAL(10u, s.size());
+ EXPECT_EQUAL(16u, s.capacity());
+ EXPECT_EQUAL(0, strncmp(s.data() + 4, "abcdef", 6));
+ }
+ {
+ nbostream s(8);
+ EXPECT_EQUAL(0u, s.size());
+ EXPECT_EQUAL(8u, s.capacity());
+ const char * prev = s.data();
+ s << "ABCD";
+ EXPECT_EQUAL(8u, s.size());
+ EXPECT_EQUAL(8u, s.capacity());
+ EXPECT_EQUAL(prev, s.data());
+ s << "A long string that will cause resizing";
+ EXPECT_EQUAL(50u, s.size());
+ EXPECT_EQUAL(64u, s.capacity());
+ EXPECT_NOT_EQUAL(prev, s.data());
+ }
+ {
+ nbostream s(8);
+ EXPECT_EQUAL(0u, s.size());
+ EXPECT_EQUAL(8u, s.capacity());
+ const char * prev = s.data();
+ s << "ABCD";
+ EXPECT_EQUAL(8u, s.size());
+ EXPECT_EQUAL(8u, s.capacity());
+ EXPECT_EQUAL(prev, s.data());
+ s.reserve(50);
+ EXPECT_NOT_EQUAL(prev, s.data());
+ EXPECT_EQUAL(8u, s.size());
+ EXPECT_EQUAL(64u, s.capacity());
+ prev = s.data();
+ s << "A long string that will cause resizing";
+ EXPECT_EQUAL(50u, s.size());
+ EXPECT_EQUAL(64u, s.capacity());
+ EXPECT_EQUAL(prev, s.data());
+ }
+ {
+ nbostream s;
+ s << int64_t(9);
+ EXPECT_EQUAL(8u, s.size());
+ EXPECT_EQUAL(0u, s.rp());
+ int64_t a(7), b(1);
+ s >> a;
+ EXPECT_EQUAL(0u, s.size());
+ EXPECT_EQUAL(8u, s.rp());
+ EXPECT_TRUE(s.empty());
+ EXPECT_TRUE(s.good());
+ EXPECT_EQUAL(9, a);
+ try {
+ s >> b;
+ EXPECT_TRUE(false);
+ } catch (const IllegalStateException & e) {
+ EXPECT_EQUAL("Stream failed bufsize(1024), readp(8), writep(8)", e.getMessage());
+ }
+ EXPECT_EQUAL(0u, s.size());
+ EXPECT_EQUAL(8u, s.rp());
+ EXPECT_TRUE(s.empty());
+ EXPECT_FALSE(s.good());
+ EXPECT_EQUAL(1, b);
+ EXPECT_EQUAL(nbostream::eof, s.state());
+ }
+}
+
+int
+IdentifiableTest::Main()
+{
+ TEST_INIT("identifiable_test");
+
+ TEST_DO(requireThatIdentifiableCastCanCastPointers());
+ TEST_DO(requireThatIdentifiableCastCanCastReferences());
+ testNamedObject();
+ testNboStream();
+ testNboSerializer();
+
+ A a;
+ B b;
+
+ const Identifiable::RuntimeClass & rtcA = a.getClass();
+ EXPECT_EQUAL(rtcA.id(), static_cast<unsigned int>(A::classId));
+ EXPECT_EQUAL(strcmp(rtcA.name(), "A"), 0);
+
+ const Identifiable::RuntimeClass & rtcB = b.getClass();
+ EXPECT_EQUAL(rtcB.id(), static_cast<unsigned int>(B::classId));
+ EXPECT_EQUAL(strcmp(rtcB.name(), "B"), 0);
+
+ const Identifiable::RuntimeClass * rt(Identifiable::classFromId(0x1ab76245));
+ ASSERT_TRUE(rt == NULL);
+ rt = Identifiable::classFromId(Abstract::classId);
+ ASSERT_TRUE(rt != NULL);
+ Identifiable * u = rt->create();
+ ASSERT_TRUE(u == NULL);
+ rt = Identifiable::classFromId(A::classId);
+ ASSERT_TRUE(rt != NULL);
+ rt = Identifiable::classFromId(B::classId);
+ ASSERT_TRUE(rt != NULL);
+
+ Identifiable * o = rt->create();
+ ASSERT_TRUE(o != NULL);
+
+ const Identifiable::RuntimeClass & rtc = o->getClass();
+ ASSERT_TRUE(rtc.id() == B::classId);
+ ASSERT_TRUE(strcmp(rtc.name(), "B") == 0);
+ ASSERT_TRUE(o->inherits(B::classId));
+ ASSERT_TRUE(o->inherits(A::classId));
+ ASSERT_TRUE(o->inherits(Abstract::classId));
+ ASSERT_TRUE(o->inherits(Identifiable::classId));
+ ASSERT_TRUE(o->getClass().id() == B::classId);
+ nbostream os;
+ NBOSerializer nos(os);
+ nos << *o;
+ EXPECT_EQUAL(os.size(), 4u);
+ Identifiable::UP o2 = Identifiable::create(nos);
+ EXPECT_TRUE(os.empty());
+ ASSERT_TRUE(o->inherits(B::classId));
+ ASSERT_TRUE(o->getClass().id() == B::classId);
+ delete o;
+
+ rt = Identifiable::classFromName("NotBNorA");
+ ASSERT_TRUE(rt == NULL);
+ rt = Identifiable::classFromName("B");
+ ASSERT_TRUE(rt != NULL);
+ o = rt->create();
+ ASSERT_TRUE(o != NULL);
+ const Identifiable::RuntimeClass & rtc2 = o->getClass();
+ ASSERT_TRUE(rtc2.id() == B::classId);
+ ASSERT_TRUE(strcmp(rtc2.name(), "B") == 0);
+ ASSERT_TRUE(o->inherits(B::classId));
+ ASSERT_TRUE(o->inherits(A::classId));
+ ASSERT_TRUE(o->inherits(Abstract::classId));
+ ASSERT_TRUE(o->inherits(Identifiable::classId));
+ ASSERT_TRUE(o->getClass().id() == B::classId);
+ delete o;
+
+ IdentifiablePtr<C> c0(NULL);
+ IdentifiablePtr<C> c1(new C(10));
+ IdentifiablePtr<C> c2(new C(20));
+
+ EXPECT_LESS(c0.cmp(c1), 0);
+ EXPECT_EQUAL(c0.cmp(c0), 0);
+ EXPECT_GREATER(c1.cmp(c0), 0);
+
+ EXPECT_LESS(c1.cmp(c2), 0);
+ EXPECT_EQUAL(c1.cmp(c1), 0);
+ EXPECT_GREATER(c2.cmp(c1), 0);
+
+ TEST_DONE();
+}
+
+void IdentifiableTest::requireThatIdentifiableCastCanCastPointers() {
+ A a;
+ B b;
+ EXPECT_TRUE(Identifiable::cast<A *>(&a));
+ EXPECT_TRUE(Identifiable::cast<A *>(&b));
+ EXPECT_TRUE(!Identifiable::cast<B *>(&a));
+ EXPECT_TRUE(Identifiable::cast<B *>(&b));
+ EXPECT_TRUE(Identifiable::cast<Abstract *>(&a));
+ EXPECT_TRUE(Identifiable::cast<Abstract *>(&b));
+}
+
+void IdentifiableTest::requireThatIdentifiableCastCanCastReferences() {
+ A a;
+ B b;
+ try {
+ // These should not throw.
+ Identifiable::cast<A &>(a);
+ Identifiable::cast<A &>(b);
+ Identifiable::cast<B &>(b);
+ Identifiable::cast<Abstract &>(a);
+ Identifiable::cast<Abstract &>(b);
+ } catch (std::bad_cast &e) {
+ TEST_FATAL(e.what());
+ }
+ EXPECT_EXCEPTION(Identifiable::cast<B &>(a), std::bad_cast, "bad_cast");
+}
+
+TEST_APPHOOK(IdentifiableTest)
diff --git a/vespalib/src/tests/objects/identifiable/namedobject.cpp b/vespalib/src/tests/objects/identifiable/namedobject.cpp
new file mode 100644
index 00000000000..3e8d3291177
--- /dev/null
+++ b/vespalib/src/tests/objects/identifiable/namedobject.cpp
@@ -0,0 +1,19 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "namedobject.h"
+
+namespace vespalib {
+
+IMPLEMENT_IDENTIFIABLE_NS(vespalib, NamedObject, Identifiable);
+
+
+Serializer & NamedObject::onSerialize(Serializer & os) const
+{
+ return os.put(_name);
+}
+
+Deserializer & NamedObject::onDeserialize(Deserializer & is)
+{
+ return is.get(_name);
+}
+
+}
diff --git a/vespalib/src/tests/objects/identifiable/namedobject.h b/vespalib/src/tests/objects/identifiable/namedobject.h
new file mode 100644
index 00000000000..784715a66f6
--- /dev/null
+++ b/vespalib/src/tests/objects/identifiable/namedobject.h
@@ -0,0 +1,23 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/vespalib/objects/identifiable.h>
+#include <string>
+
+namespace vespalib
+{
+
+class NamedObject : public Identifiable
+{
+public:
+ DECLARE_IDENTIFIABLE_NS(vespalib, NamedObject);
+ DECLARE_NBO_SERIALIZE;
+ NamedObject() : _name() { }
+ NamedObject(const string & name) : _name(name) { }
+ const string & getName() const { return _name; }
+private:
+ string _name;
+};
+
+}
+
diff --git a/vespalib/src/tests/objects/objectdump/.gitignore b/vespalib/src/tests/objects/objectdump/.gitignore
new file mode 100644
index 00000000000..6ddd515391d
--- /dev/null
+++ b/vespalib/src/tests/objects/objectdump/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+objectdump_test
+vespalib_objectdump_test_app
diff --git a/vespalib/src/tests/objects/objectdump/CMakeLists.txt b/vespalib/src/tests/objects/objectdump/CMakeLists.txt
new file mode 100644
index 00000000000..67395998b39
--- /dev/null
+++ b/vespalib/src/tests/objects/objectdump/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_objectdump_test_app TEST
+ SOURCES
+ objectdump.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_objectdump_test_app COMMAND vespalib_objectdump_test_app)
diff --git a/vespalib/src/tests/objects/objectdump/objectdump.cpp b/vespalib/src/tests/objects/objectdump/objectdump.cpp
new file mode 100644
index 00000000000..812b1e79e17
--- /dev/null
+++ b/vespalib/src/tests/objects/objectdump/objectdump.cpp
@@ -0,0 +1,115 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/objects/identifiable.h>
+#include <vespa/vespalib/objects/visit.hpp>
+
+#define CID_Base 10000000
+#define CID_Foo 10000001
+#define CID_Bar 10000002
+#define CID_Baz 10000003
+
+using vespalib::ObjectVisitor;
+using vespalib::IdentifiablePtr;
+
+struct Base : public vespalib::Identifiable
+{
+ DECLARE_IDENTIFIABLE(Base);
+ virtual Base *clone() const { return new Base(*this); }
+};
+IMPLEMENT_IDENTIFIABLE(Base, vespalib::Identifiable);
+
+struct Baz : public Base
+{
+ DECLARE_IDENTIFIABLE(Baz);
+ Baz *clone() const override { return new Baz(*this); }
+};
+IMPLEMENT_IDENTIFIABLE(Baz, Base);
+
+struct Bar : public Base
+{
+ DECLARE_IDENTIFIABLE(Bar);
+ bool _bool;
+ int8_t _int8;
+ uint8_t _uint8;
+ int16_t _int16;
+ uint16_t _uint16;
+ int32_t _int32;
+ uint32_t _uint32;
+ int64_t _int64;
+ uint64_t _uint64;
+ float _float;
+ double _double;
+ vespalib::string _string;
+ Bar() : _bool(true), _int8(-1), _uint8(1), _int16(-2), _uint16(2),
+ _int32(-4), _uint32(4), _int64(-8), _uint64(8),
+ _float(2.5), _double(2.75), _string("bla bla") {}
+
+ Bar *clone() const override { return new Bar(*this); }
+
+ void visitMembers(ObjectVisitor &v) const override {
+ visit(v, "_bool", _bool);
+ visit(v, "_int8", _int8);
+ visit(v, "_uint8", _uint8);
+ visit(v, "_int16", _int16);
+ visit(v, "_uint16", _uint16);
+ visit(v, "_int32", _int32);
+ visit(v, "_uint32", _uint32);
+ visit(v, "_int64", _int64);
+ visit(v, "_uint64", _uint64);
+ visit(v, "_float", _float);
+ visit(v, "_double", _double);
+ visit(v, "_string", _string);
+ visit(v, "info", "a dummy string");
+ visit(v, "(const char*)0", (const char*)0);
+ }
+};
+IMPLEMENT_IDENTIFIABLE(Bar, Base);
+
+struct Foo : public Base
+{
+ DECLARE_IDENTIFIABLE(Foo);
+ Bar _objMember;
+ Baz _objMember2;
+ Baz *_objPtr;
+ std::vector<Bar> _list;
+ std::vector<IdentifiablePtr<Base> > _list2;
+
+ Foo();
+ ~Foo();
+ Foo *clone() const override { return new Foo(*this); }
+ void visitMembers(ObjectVisitor &v) const override;
+};
+
+Foo::~Foo() { }
+Foo::Foo()
+ : _objMember(), _objMember2(), _objPtr(0), _list(), _list2()
+{
+ _list.push_back(Bar());
+ _list.push_back(Bar());
+ _list.push_back(Bar());
+ _list2.push_back(Bar());
+ _list2.push_back(Baz());
+}
+
+void
+Foo::visitMembers(ObjectVisitor &v) const {
+ visit(v, "_objMember", _objMember);
+ visit(v, "_objMember2", _objMember2);
+ visit(v, "_objPtr", _objPtr);
+ visit(v, "_list", _list);
+ visit(v, "_list2", _list2);
+}
+
+IMPLEMENT_IDENTIFIABLE(Foo, Base);
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("objectdump_test");
+ Foo foo;
+ fprintf(stderr, "%s", foo.asString().c_str());
+ TEST_DONE();
+}
diff --git a/vespalib/src/tests/objects/objectselection/.gitignore b/vespalib/src/tests/objects/objectselection/.gitignore
new file mode 100644
index 00000000000..f6aefd07270
--- /dev/null
+++ b/vespalib/src/tests/objects/objectselection/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+objectselection_test
+vespalib_objectselection_test_app
diff --git a/vespalib/src/tests/objects/objectselection/CMakeLists.txt b/vespalib/src/tests/objects/objectselection/CMakeLists.txt
new file mode 100644
index 00000000000..94f43078820
--- /dev/null
+++ b/vespalib/src/tests/objects/objectselection/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_objectselection_test_app TEST
+ SOURCES
+ objectselection.cpp
+ DEPENDS
+ vespalib
+)
+vespa_add_test(NAME vespalib_objectselection_test_app COMMAND vespalib_objectselection_test_app)
diff --git a/vespalib/src/tests/objects/objectselection/objectselection.cpp b/vespalib/src/tests/objects/objectselection/objectselection.cpp
new file mode 100644
index 00000000000..aa9c841f2dc
--- /dev/null
+++ b/vespalib/src/tests/objects/objectselection/objectselection.cpp
@@ -0,0 +1,94 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/testapp.h>
+#include <vespa/vespalib/objects/identifiable.hpp>
+#include <vespa/vespalib/objects/objectpredicate.h>
+#include <vespa/vespalib/objects/objectoperation.h>
+
+using namespace vespalib;
+
+#define CID_Foo 60000005
+#define CID_Bar 60000010
+
+struct Foo : public Identifiable
+{
+ typedef IdentifiablePtr<Foo> CP;
+ std::vector<CP> nodes;
+
+ DECLARE_IDENTIFIABLE(Foo);
+ virtual Foo *clone() const { return new Foo(*this); }
+ void selectMembers(const ObjectPredicate &p, ObjectOperation &o) override {
+ for (uint32_t i = 0; i < nodes.size(); ++i) {
+ nodes[i]->select(p, o);
+ }
+ }
+};
+IMPLEMENT_IDENTIFIABLE(Foo, Identifiable);
+
+struct Bar : public Foo
+{
+ int value;
+
+ DECLARE_IDENTIFIABLE(Bar);
+ Bar() : value(0) {}
+ Bar(int v) { value = v; }
+ Bar *clone() const override { return new Bar(*this); }
+};
+IMPLEMENT_IDENTIFIABLE(Bar, Identifiable);
+
+struct ObjectType : public ObjectPredicate
+{
+ uint32_t cid;
+ ObjectType(uint32_t id) : cid(id) {}
+ bool check(const Identifiable &obj) const override {
+ return (obj.getClass().id() == cid);
+ }
+};
+
+struct ObjectCollect : public ObjectOperation
+{
+ std::vector<Identifiable*> nodes;
+ ~ObjectCollect() override;
+ void execute(Identifiable &obj) override {
+ nodes.push_back(&obj);
+ }
+};
+
+ObjectCollect::~ObjectCollect() = default;
+
+TEST_SETUP(Test);
+
+int
+Test::Main()
+{
+ TEST_INIT("objectselection_test");
+ {
+ Foo f1;
+ Foo f2;
+ Foo f3;
+ Bar b1(1);
+ Bar b2(2);
+ Bar b3(3);
+ Bar b4(4);
+ f2.nodes.push_back(b1);
+ f2.nodes.push_back(b2);
+ f3.nodes.push_back(b3);
+ f3.nodes.push_back(b4);
+ f1.nodes.push_back(f2);
+ f1.nodes.push_back(f3);
+
+ ObjectType predicate(Bar::classId);
+ ObjectCollect operation;
+ f1.select(predicate, operation);
+ ASSERT_TRUE(operation.nodes.size() == 4);
+ ASSERT_TRUE(operation.nodes[0]->getClass().id() == Bar::classId);
+ ASSERT_TRUE(operation.nodes[1]->getClass().id() == Bar::classId);
+ ASSERT_TRUE(operation.nodes[2]->getClass().id() == Bar::classId);
+ ASSERT_TRUE(operation.nodes[3]->getClass().id() == Bar::classId);
+ ASSERT_TRUE(((Bar*)operation.nodes[0])->value == 1);
+ ASSERT_TRUE(((Bar*)operation.nodes[1])->value == 2);
+ ASSERT_TRUE(((Bar*)operation.nodes[2])->value == 3);
+ ASSERT_TRUE(((Bar*)operation.nodes[3])->value == 4);
+ }
+ TEST_DONE();
+}