/* ModelPortRegistry(Test) - verify handling of model ports Copyright (C) Lumiera.org 2010, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "proc/mobject/builder/model-port-registry.hpp" #include "proc/asset/timeline.hpp" #include "proc/asset/pipe.hpp" #include "lib/query.hpp" #include "lib/util.hpp" namespace mobject { namespace builder { namespace test { using util::isSameObject; using util::isnil; using asset::Pipe; using asset::PPipe; using asset::Struct; using asset::Timeline; using asset::PTimeline; using lumiera::Query; typedef asset::ID PID; typedef asset::ID TID; typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; namespace { // test environment inline PID getPipe (string id) { return Pipe::query("id("+id+")"); } inline TID getTimeline (string id) { return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); } struct TestContext { ModelPortRegistry registry_; ModelPortRegistry* previous_; /** setup */ TestContext() : registry_() , previous_(ModelPortRegistry::setActiveInstance (registry_)) { } /** tear-down */ ~TestContext() { if (previous_) ModelPortRegistry::setActiveInstance (*previous_); else ModelPortRegistry::shutdown(); } }; } /********************************************************************************* * @test create a standalone model port registry to verify the behaviour of * model ports, accessed through reference handles. This test provides an * example setup detached from the real usage situation within the builder. * The ModelPortRegistry management interface is used to create and track a * set of model ports, to be made visible by an atomic, transactional switch. * The access for client code through the ModelPort frontend is then verified. * * @see mobject::ModelPort * @see mobject::builder::ModelPortRegistry */ class ModelPortRegistry_test : public Test { virtual void run (Arg) { TestContext ctx; fabricating_ModelPorts (ctx.registry_); accessing_ModelPorts(); transactionalSwitch (ctx.registry_); } void fabricating_ModelPorts (ModelPortRegistry& registry) { /* == some Assets to play with == */ PID pipeA = getPipe ("pipeA"); PID pipeB = getPipe ("pipeB"); TID someTimeline = getTimeline ("some_test_Timeline"); // start out with defining some new model ports...... MPDescriptor p1 = registry.definePort (pipeA, someTimeline); MPDescriptor p2 = registry.definePort (pipeB, someTimeline); CHECK (registry.contains (pipeA)); CHECK (registry.contains (pipeB)); VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) ); CHECK (registry.contains (pipeB)); CHECK (pipeA == p1.id()); CHECK (pipeB == p2.id()); CHECK (someTimeline == p1.holder()); CHECK (someTimeline == p2.holder()); registry.commit(); } void accessing_ModelPorts () { PID pipeA = getPipe ("pipeA"); PID pipeB = getPipe ("pipeB"); PID pipeWC = getPipe ("WCpipe"); ModelPort mp1(pipeA); ModelPort mp2(pipeB); VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort unbefitting(pipeWC) ); ModelPort mp1x(pipeA); // can be created multiple times ModelPort mp2x(mp1x); // can be copied at will ModelPort mpNull; // can be default constructed (->unconnected) CHECK (mp1); CHECK (mp2); CHECK (mp1x); CHECK (!mpNull); // bool check verifies setup and connected state CHECK ( ModelPort::exists (pipeA)); // this is the same check, but invoked just with an pipe-ID CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); CHECK (mp1 == mp1x); CHECK (!isSameObject (mp1, mp1x)); CHECK (mp1 != mp2); CHECK (mp2 != mp1); CHECK (mp1 != mpNull); CHECK (mp2 != mpNull); CHECK (mp1.pipe() == pipeA); CHECK (mp2.pipe() == pipeB); CHECK (mp1x.pipe() == pipeA); VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); // any further operations on an unconnected port will throw VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.holder()); CHECK (mp1.streamType() == pipeA.streamType()); } void transactionalSwitch (ModelPortRegistry& registry) { PID pipeA = getPipe ("pipeA"); PID pipeB = getPipe ("pipeB"); PID pipeWC = getPipe ("WCpipe"); CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); CHECK (ModelPort::exists (pipeA)); CHECK (registry.contains (pipeA)); registry.remove (pipeA); CHECK (!registry.contains (pipeA)); // removed from the current (pending) transaction CHECK ( ModelPort::exists (pipeA)); // but not yet publicly visible // now create a new and differing definition of port A TID anotherTimeline = getTimeline ("another_test_Timeline"); MPDescriptor p1 = registry.definePort (pipeA, anotherTimeline); CHECK (registry.contains (pipeA)); CHECK (anotherTimeline == p1.holder()); CHECK (ModelPort(pipeA).holder() != anotherTimeline); registry.remove (pipeB); // some more wired definitions registry.definePort (pipeWC, anotherTimeline); CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); CHECK ( registry.isRegistered (pipeB)); // this is the same as ModelPort::exists CHECK (!registry.isRegistered (pipeWC)); // // Note: pending transaction not yet committed ModelPort portA(pipeA); // ...... thus the changes aren't reflected to client code ModelPort portB(pipeB); VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort ineptly(pipeWC)); CHECK (portA); CHECK (portB); CHECK (portA.pipe() == pipeA); CHECK (portB.pipe() == pipeB); CHECK (portA.holder() != anotherTimeline); registry.commit(); CHECK ( ModelPort::exists (pipeA)); // now all our changes got publicly visible CHECK (!ModelPort::exists (pipeB)); CHECK ( ModelPort::exists (pipeWC)); CHECK ( portA); CHECK (!portB); CHECK (portA.holder() == anotherTimeline); CHECK (portA.pipe() == pipeA); VERIFY_ERROR (INVALID_MODEL_PORT, portB.pipe()); ModelPort pwc(pipeWC); // now clients may also use the now officially promoted new port CHECK (pwc); CHECK (pwc.pipe() == pipeWC); CHECK (pwc.holder() == anotherTimeline); // Next: doing several changes, // but finally *not* committing them... CHECK ( registry.contains (pipeA)); CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); registry.remove (pipeA); registry.clear(); // remove everything from the pending transaction CHECK (!registry.contains (pipeA)); CHECK (!registry.contains (pipeB)); CHECK (!registry.contains (pipeWC)); registry.definePort (pipeB, anotherTimeline); CHECK ( registry.contains (pipeB)); CHECK (!portB); // not committed and thus not visible CHECK (portA); CHECK (pwc); registry.rollback(); CHECK ( registry.contains (pipeA)); // no effect to the officialy visible state CHECK (!registry.contains (pipeB)); CHECK ( registry.contains (pipeWC)); VERIFY_ERROR(INVALID_MODEL_PORT, registry.get(pipeB) ); CHECK (!portB); } }; /** Register this test class... */ LAUNCHER (ModelPortRegistry_test, "unit session builder"); }}} // namespace mobject::builder::test