/* EntryID(Test) - proof-of-concept test for a combined hash+symbolic ID 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 "lib/idi/entry-id.hpp" #include "proc/asset/struct-scheme.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/fork.hpp" #include "lib/meta/trait-special.hpp" #include "lib/util-foreach.hpp" #include "lib/symbol.hpp" #include #include #include using lib::test::showSizeof; using lib::test::randStr; using util::isSameObject; using util::contains; using util::and_all; using lib::Literal; using std::string; using std::cout; using std::endl; namespace lib { namespace idi { namespace test{ using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; namespace { // Test definitions... struct Dummy { }; } using DummyID = EntryID; using ForkID = EntryID; using ClipID = EntryID; /***********************************************************************//** * @test proof-of-concept test for a combined symbolic and hash based ID. * - create some symbolic IDs * - check default assignment works properly * - check comparisons * - check hashing * - use the embedded hash ID (LUID) as hashtable key * * @see lib::HashIndexed::Id * @see mobject::Placement real world usage example */ class EntryID_test : public Test { virtual void run (Arg) { checkCreation(); checkBasicProperties(); checkComparisions(); checkErasure(); buildHashtable(); } void checkCreation () { DummyID dID1; DummyID dID2("strange"); DummyID dID3; CHECK (dID1.isValid()); CHECK (dID2.isValid()); CHECK (dID3.isValid()); CHECK (dID1 != dID2); CHECK (dID2 != dID1); CHECK (dID2 != dID3); CHECK (dID3 != dID2); CHECK (dID1 != dID3); CHECK (dID3 != dID1); ForkID tID1; ForkID tID2; ForkID tID3("special"); CHECK (tID1.isValid()); CHECK (tID2.isValid()); CHECK (tID3.isValid()); CHECK (tID1 != tID2); CHECK (tID2 != tID1); CHECK (tID2 != tID3); CHECK (tID3 != tID2); CHECK (tID1 != tID3); CHECK (tID3 != tID1); cout << dID1 << endl; cout << dID2 << endl; cout << dID3 << endl; cout << tID1 << endl; cout << tID2 << endl; cout << tID3 << endl; DummyID x (dID2); // copy ctor CHECK (x == dID2); CHECK (!isSameObject (x, dID2)); } void checkBasicProperties () { using proc::asset::Asset; using proc::asset::STRUCT; using proc::asset::Category; using proc::asset::idi::getAssetIdent; ForkID tID(" test ⚡ ☠ ☭ ⚡ track "); CHECK (getAssetIdent(tID) == Asset::Ident("test_track", Category(STRUCT,"forks"), "lumi", 0)); CHECK (tID.getHash() == ForkID("☢ test ☢ track ☢").getHash()); CHECK (tID.getSym() == getAssetIdent(tID).name); CHECK (getAssetIdent(ForkID()).category == Category (STRUCT,"forks")); CHECK (getAssetIdent(ClipID()).category == Category (STRUCT,"clips")); ClipID cID2,cID3; CHECK (cID2.getSym() < cID3.getSym()); CHECK (ClipID("x").getSym() == ClipID(" x ").getSym()); for (uint i=0; i<10000; ++i) { ForkID arbitrary(randStr(30)); CHECK (0 < arbitrary.getHash()); CHECK (tID.getHash() != arbitrary.getHash()); tID = arbitrary; CHECK (tID.getHash() == arbitrary.getHash()); CHECK (tID.getSym() == arbitrary.getSym()); CHECK (getAssetIdent(tID)== getAssetIdent(arbitrary)); } cout << showSizeof() << endl; cout << showSizeof() << endl; CHECK (sizeof(ForkID) == sizeof(BareEntryID)); CHECK (sizeof(ForkID) == sizeof(lumiera_uid) + sizeof(void*)); } void checkComparisions () { ForkID tID1("a1"); ForkID tID2("a1"); ForkID tID3("a2"); ForkID tID4("b"); CHECK (tID1 == tID2); CHECK (tID2 < tID3); CHECK (tID2 <= tID3); CHECK (tID3 >= tID2); CHECK (tID3 > tID2); CHECK (tID3 < tID4); CHECK (tID3 <= tID4); CHECK (tID4 >= tID3); CHECK (tID4 > tID3); ForkID trackID1, trackID2; CHECK (trackID1 < trackID2); // auto generated IDs are prefix + running counter } /** @test handling of EntryIDs through their common base class, * which means erasing the specific type information. * While this type information can't be recovered * after erasure, we can try to upcast back * to a known type; this upcast is safe, * because the embedded hash-ID * is based on the type info. */ void checkErasure () { ForkID fID("suspicious"); ClipID cID("suspicious"); CHECK (fID.getHash() != cID.getHash()); CHECK (fID.getSym() == cID.getSym()); BareEntryID bIDf (fID); BareEntryID bIDc (cID); CHECK (bIDf != bIDc); CHECK (bIDf.getHash() != bIDc.getHash()); CHECK (bIDf.getSym() == bIDc.getSym()); CHECK ("suspicious" == bIDc.getSym()); using proc::mobject::session::Fork; using proc::mobject::session::Clip; ForkID tIDnew = bIDf.recast(); ClipID cIDnew = bIDc.recast(); CHECK (tIDnew == fID); CHECK (cIDnew == cID); VERIFY_ERROR (WRONG_TYPE, bIDf.recast()); VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); VERIFY_ERROR (WRONG_TYPE, bIDf.recast()); CHECK (fID == ForkID::recast (bIDf)); // equivalent static API on typed subclass VERIFY_ERROR (WRONG_TYPE, ForkID::recast(bIDc)); VERIFY_ERROR (WRONG_TYPE, ClipID::recast(bIDf)); VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDc)); VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDf)); // mixed equality comparisons (based on the hash) BareEntryID bIDt_copy (bIDf); CHECK (bIDf == bIDt_copy); CHECK (!isSameObject (bIDf, bIDt_copy)); CHECK (fID != bIDc); CHECK (cID != bIDt_copy); CHECK (fID == bIDt_copy); CHECK (bIDf == ForkID ("suspicious")); CHECK (bIDf != ClipID ("suspicious")); CHECK (bIDc == ClipID ("suspicious")); CHECK (ForkID ("suspicious") != ClipID ("suspicious")); } //---key--+-value-+-hash-function--- typedef std::unordered_map Hashtable; /** @test build a hashtable, using EntryID as key, * thereby using the embedded hash-ID * @note there is a known weakness of the boost::hash * when used on IDs with a running number suffix. /////TICKET #587 * We use a trick to spread the numbers better. * @see HashGenerator_test#verify_Knuth_workaround */ void buildHashtable () { Hashtable tab; for (uint i=0; i<100000; ++i) { DummyID dummy; tab[dummy] = string(dummy); } CHECK (and_all (tab, verifyEntry)); CHECK (100000 == tab.size()); } static bool verifyEntry (Hashtable::value_type entry) { return checkForHashCollision(string(entry.first), entry.second); } static bool checkForHashCollision(string const& key, string const& val) { if (key != val) cout << "Hash collision: "<