/* TimelineSequenceHandling(Test) - managing the top level session facade objects Copyright (C) Lumiera.org 2008-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 "proc/mobject/session.hpp" #include "proc/mobject/mobject-ref.hpp" #include "proc/mobject/session/binding.hpp" //#include "proc/mobject/session/fixture.hpp" // TODO only temporarily needed #include "proc/assetmanager.hpp" #include "proc/asset/timeline.hpp" #include "proc/asset/sequence.hpp" #include "proc/asset/pipe.hpp" #include "lib/lumitime.hpp" #include "lib/query.hpp" #include "lib/util.hpp" #include using util::isSameObject; using util::contains; using std::string; using std::cout; namespace mobject { namespace session { namespace test { using proc_interface::AssetManager; using proc_interface::PAsset; using asset::PTimeline; using asset::PSequence; using asset::RBinding; using asset::RTrack; using asset::Pipe; using lumiera::Query; using lumiera::Time; /******************************************************************************** * @test verify retrieval and instance management of the top level facade objects * as integrated with the session and high-level model. Both sequences and * timelines are at the same time structural assets and act as facades * on the session API. Thus we can query specific instances from the * struct factory or alternatively access them through the session. * Moreover we can create new top level elements in the session * just by querying the respective asset. * * @todo specify how deletion is handled * @todo specify how to \em move objects by placement * * @see session-structure-test.cpp * @see Timeline * @see Sequence * @see Session */ class TimelineSequenceHandling_test : public Test { virtual void run (Arg) { Session::current.reset(); CHECK (Session::current.isUp()); verify_retrieval(); verify_creation(); verify_removalTimeline(); verify_removalBinding(); verify_removalSequence(); indirect_SequenceHandling(); } void verify_retrieval() { PSess sess = Session::current; CHECK (sess->isValid()); CHECK (0 < sess->timelines.size()); PTimeline defaultTimeline = sess->defaults (Query ()); //////////////////////TICKET #549 Query query1 = "id("+defaultTimeline->getNameID()+")."; PTimeline queriedTimeline = asset::Struct::create (query1); CHECK (queriedTimeline); CHECK (queriedTimeline == defaultTimeline); // retrieved the existing timeline asset again CHECK (queriedTimeline == sess->timelines[0]); Query query2 = "id("+defaultTimeline->getSequence()->getNameID()+")."; PSequence queriedSequence = asset::Struct::create (query2); CHECK (queriedSequence); CHECK (queriedSequence == sess->sequences[0]); CHECK (queriedSequence == sess->timelines[0]->getSequence()); CHECK (queriedSequence == defaultTimeline->getSequence()); } void verify_creation() { PSess sess = Session::current; CHECK (sess->isValid()); uint num_timelines = sess->timelines.size(); CHECK (0 < num_timelines); Query special = "id(aSillyName), sequence(" + sess->sequences[0]->getNameID() + "), pipe(ambiance)."; PTimeline specialTimeline (asset::Struct::create (special)); CHECK (specialTimeline); CHECK (num_timelines + 1 == sess->timelines.size()); CHECK (specialTimeline == session->timelines[num_timelines]); // new one got appended at the end // verify the properties CHECK (specialTimeline->getSequence() == sess->sequences[0]); // the already existing sequence got bound into that timeline too CHECK (contains (specialTimeline->pipes, Pipe::query("pipe(ambiance)"))); CHECK (specialTimeline.use_count() == 3); // we, the AssetManager and the session } void verify_removalTimeline() { PSess sess (Session::current); AssetManager& assetM (AssetManager::instance()); CHECK (sess->isValid()); uint num_timelines = sess->timelines.size(); CHECK (2 <= num_timelines); PTimeline specialTimeline = sess->timelines[num_timelines-1]; CHECK ("aSillyName" == specialTimeline->getNameID()); RBinding binding = specialTimeline->getBinding(); CHECK (binding); PSequence theSeq = binding->getSequence(); CHECK (theSeq == sess->sequences[0]); CHECK (theSeq == specialTimeline->getSequence()); CHECK (assetM.known (theSeq->getID())); // cause removal of the timeline assetM.remove (specialTimeline->getID()); //////////////TICKET #550 CHECK (!assetM.known (specialTimeline->getID())); CHECK (1 == specialTimeline.use_count()); // we hold the only remaining ref CHECK ( assetM.known (theSeq->getID())); // bound sequence isn't affected CHECK (theSeq == sess->sequences[0]); CHECK (num_timelines - 1 == sess->timelines.size()); CHECK (!binding); // got purged from the model } void verify_removalBinding() { PSess sess (Session::current); AssetManager& assetM (AssetManager::instance()); CHECK (sess->isValid()); uint num_timelines = sess->timelines.size(); CHECK (0 < num_timelines); // create a new Timeline to play with, using the default sequence... PTimeline aTimeline (asset::Struct::create (Query ("sequence("+ + sess->sequences[0]->getNameID() + ")."))); CHECK (num_timelines + 1 == sess->timelines.size()); RBinding binding = aTimeline->getBinding(); CHECK (binding); PSequence theSeq = binding->getSequence(); CHECK (theSeq == sess->sequences[0]); CHECK (theSeq == aTimeline->getSequence()); CHECK (assetM.known (aTimeline->getID())); CHECK (assetM.known (theSeq->getID())); // indirectly cause removal of the timeline by dropping the binding sess->remove(binding); CHECK (!binding); CHECK (!assetM.known (aTimeline->getID())); CHECK (1 == aTimeline.use_count()); CHECK ( assetM.known (theSeq->getID())); CHECK (num_timelines == sess->timelines.size()); CHECK (!contains (sess->timelines, aTimeline)); CHECK ( contains (sess->sequences, theSeq)); // unaffected } void verify_removalSequence() { PSess sess (Session::current); AssetManager& assetM (AssetManager::instance()); CHECK (sess->isValid()); uint num_timelines = sess->timelines.size(); uint num_sequences = sess->sequences.size(); // create a new timeline, bound to a new sequence... PTimeline aTimeline (asset::Struct::create (Query ())); PSequence aSequence (aTimeline->getSequence()); CHECK (num_timelines + 1 == sess->timelines.size()); CHECK (num_sequences + 1 == sess->sequences.size()); RBinding binding = aTimeline->getBinding(); RTrack rootTrack = aSequence->getTracks(); CHECK (rootTrack); CHECK (binding); CHECK (aSequence == binding->getSequence()); CHECK (assetM.known (aTimeline->getID())); CHECK (assetM.known (aSequence->getID())); // purging the sequence cascades to all linked entities assetM.remove (aSequence->getID()); //////////////TICKET #550 CHECK (!assetM.known (aTimeline->getID())); CHECK (!assetM.known (aSequence->getID())); CHECK (!rootTrack); CHECK (!binding); CHECK (num_timelines == sess->timelines.size()); CHECK (num_sequences == sess->sequences.size()); CHECK (!contains (sess->timelines, aTimeline)); CHECK (!contains (sess->sequences, aSequence)); CHECK (1 == aTimeline.use_count()); CHECK (1 == aSequence.use_count()); } void indirect_SequenceHandling() { PSess sess (Session::current); AssetManager& assetM (AssetManager::instance()); CHECK (sess->isValid()); uint num_sequences = sess->sequences.size(); RTrack someTrack = sess->sequences[0]->getTracks(); // indirectly cause a new sequence to come to life... RTrack newTrack = sess->getRoot().attach (someTrack); // attach new Placement to root scope CHECK (newTrack != someTrack); // it's a new placement CHECK (num_sequences + 1 == sess->sequences.size()); // this root-attachment created a new sequence by sideeffect PSequence aSequence = sess->sequences[num_sequences]; CHECK (newTrack == aSequence->getTracks()); CHECK (newTrack); CHECK (someTrack); CHECK (assetM.known (aSequence->getID())); //TODO maybe even bind it into a new timeline. Then verify this new timeline gets removed alongside with the sequence below! // just moving the new track away from root position // causes the sequence to disappear newTrack.getPlacement().chain (someTrack, Time(20)); /////////////////TICKET #555 does moving by placement really work this way?? //TODO how to verify the changed placement?? CHECK (!assetM.known (aSequence->getID())); CHECK (num_sequences == sess->sequences.size()); CHECK (!contains (sess->sequences, aSequence)); CHECK (someTrack); CHECK (newTrack); } }; /** Register this test class... */ LAUNCHER (TimelineSequenceHandling_test, "unit session"); }}} // namespace mobject::session::test