diff --git a/src/backend/engine/job.cpp b/src/backend/engine/job.cpp index 34776ae33..bacfadc09 100644 --- a/src/backend/engine/job.cpp +++ b/src/backend/engine/job.cpp @@ -34,6 +34,7 @@ #include "backend/engine/job.h" +#include "lib/util.hpp" #include #include @@ -55,8 +56,8 @@ namespace engine { } // (END) Details... -// using mobject::Placement; -// using mobject::session::Effect; + using lib::HashVal; + using util::isSameObject; @@ -111,6 +112,14 @@ namespace engine { } + bool + Job::usesClosure (JobClosure const& otherClosure) const + { + return isSameObject (myClosure(this), otherClosure); + } + + + /** hash value based on all relevant job data. * Job records hashing to the same value shall be considered equivalent. * Since the interpretation of the #InvocationInstanceID is a private detail @@ -118,15 +127,21 @@ namespace engine { * concrete JobClosure. This is not considered problematic, as the normal * job operation and scheduling doesn't rely on the job's hash. Only some * diagnostic facilities do. */ - size_t + HashVal hash_value (Job const& job) { - size_t hash = myClosure(&job).hashOfInstance (job.parameter.invoKey); - boost::hash_combine(hash, typeid(job.jobClosure).name()); - boost::hash_combine(hash, job.parameter.nominalTime); - return hash; + return myClosure(&job).hash_value (job.parameter); } + HashVal + JobClosure::hash_value (JobParameter parameter) const + { + HashVal hash = this->hashOfInstance (parameter.invoKey); + boost::hash_combine(hash, typeid(*this).name()); + boost::hash_combine(hash, parameter.nominalTime); + return hash; + } + }} // namespace backend::engine diff --git a/src/backend/engine/job.h b/src/backend/engine/job.h index 41dda27e6..6de6e03ff 100644 --- a/src/backend/engine/job.h +++ b/src/backend/engine/job.h @@ -215,6 +215,8 @@ namespace engine { virtual JobKind getJobKind() const =0; virtual bool verify (Time, InvocationInstanceID) const =0; virtual size_t hashOfInstance(InvocationInstanceID) const =0; + + lib::HashVal hash_value (JobParameter) const; }; @@ -270,6 +272,8 @@ namespace engine { JobKind getKind() const; bool isValid() const; + bool usesClosure (JobClosure const&) const; + /** provide a hash based Job ID */ friend lib::HashVal hash_value (Job const&); }; diff --git a/src/lib/util.hpp b/src/lib/util.hpp index 55a04c443..a55ff3aed 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -190,6 +190,23 @@ namespace util { return defaultVal; } + /** expose a reference to a map entry, with fall-back to some default object + * @note exposing a const reference; especially the default needs to reside + * somewhere at a safe storage location. + * @see lib::NullValue + */ + template + inline typename MAP::mapped_type const & + access_or_default (MAP& map, typename MAP::key_type const& key + , typename MAP::mapped_type const& refDefault) + { + typename MAP::const_iterator pos = map.find (key); + if (pos != map.end()) + return pos->second; + else + return refDefault; + } + /** shortcut for removing all copies of an Element * in any sequential collection */ template diff --git a/tests/46engine.tests b/tests/46engine.tests index f63f41317..fad64e4a5 100644 --- a/tests/46engine.tests +++ b/tests/46engine.tests @@ -43,7 +43,8 @@ PLANNED "Frame Dispatcher Interface" DispatcherInterface_test < + + 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 "backend/engine/dummy-job.hpp" + +#include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" +#include "backend/real-clock.hpp" +#include "lib/null-value.hpp" +#include "lib/hash-value.h" +#include "lib/util.hpp" + +#include +#include +#include + + +namespace backend{ +namespace engine { + + namespace { // DummyJob implementation details... + + using lib::HashVal; + using lib::NullValue; + using lib::time::TimeVar; + using std::tr1::unordered_map; + using util::access_or_default; + + + const int MAX_PARAM_A(1000); ///< random test values 0...1000 + const int MAX_PARAM_B(10); ///< random test values -10...+10 + + + /** + * test dummy jobs are backed by this closure. + * DummyJob invocations are recorded in a hashtable + * @note as of 9/2013, we use a very simplistic implementation, + * causing a consecutive invocation of the same job instance + * to overwrite the previous log entry. + */ + class DummyClosure + : public JobClosure + { + void + invokeJobOperation (JobParameter parameter) + { + invocationLog_[hash_value (parameter)] = Invocation(parameter); + } + + void + signalFailure (JobParameter,JobFailureReason) + { + NOTREACHED ("Job failure is not subject of this test"); + } + + JobKind + getJobKind() const + { + return META_JOB; + } + + bool + verify (Time nominalJobTime, InvocationInstanceID invoKey) const + { + return Time::NEVER < nominalJobTime + && 0 <= invoKey.metaInfo.a + && invoKey.metaInfo.a < MAX_PARAM_A + && -MAX_PARAM_B < invoKey.metaInfo.b + && invoKey.metaInfo.b < MAX_PARAM_B; + } + + size_t + hashOfInstance(InvocationInstanceID invoKey) const + { + return boost::hash_value (invoKey.metaInfo.a); + } + + + /* === Logging/Reporting of job invocation === */ + + struct Invocation + { + TimeVar nominal; + TimeVar real; + int a,b; + + Invocation(JobParameter param) + : nominal(TimeValue(param.nominalTime)) + , real(RealClock::now()) + , a(param.invoKey.metaInfo.a) + , b(param.invoKey.metaInfo.b) + { } + + Invocation() + : nominal(Time::NEVER) + , real(Time::ANYTIME) + , a(MAX_PARAM_A), b(0) + { } + }; + + /** recording DummyJob invocations */ + unordered_map invocationLog_; + + + public: + Invocation const& + queryInvocation (JobParameter param) const + { + return access_or_default (invocationLog_, hash_value(param) + ,NullValue::get()); + } + }; + + + + /** actual instance of the test dummy job operation */ + DummyClosure dummyClosure; + + }// (End)Implementation details + + + + + + + + + Job + DummyJob::build() + { + InvocationInstanceID invoKey; + invoKey.metaInfo.a = rand() % MAX_PARAM_A; + invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + + Time nominalTime = lib::test::randTime(); + + return Job(dummyClosure, invoKey, nominalTime); + } + + + Job + DummyJob::build (Time nominalTime, int additionalKey) + { + InvocationInstanceID invoKey; + invoKey.metaInfo.a = additionalKey; + invoKey.metaInfo.b = rand() % (2*MAX_PARAM_B) - MAX_PARAM_B; + + return Job(dummyClosure, invoKey, nominalTime); + } + + + bool + DummyJob::was_invoked (Job const& job) + { + REQUIRE (job.usesClosure (dummyClosure)); + + return Time::NEVER != dummyClosure.queryInvocation(job.parameter).nominal; + } + + + Time + DummyJob::invocationTime (Job const& job) + { + REQUIRE (job.usesClosure (dummyClosure)); + + return dummyClosure.queryInvocation(job.parameter).real; + } + + +}} // namespace backend::engine diff --git a/tests/core/backend/engine/dummy-job.hpp b/tests/core/backend/engine/dummy-job.hpp index bb46c4690..102085926 100644 --- a/tests/core/backend/engine/dummy-job.hpp +++ b/tests/core/backend/engine/dummy-job.hpp @@ -27,94 +27,27 @@ #include "backend/engine/job.h" -#include "lib/test/test-helper.hpp" - -#include -#include +#include "lib/time/timevalue.hpp" namespace backend{ namespace engine { - namespace { - - class DummyClosure - : public JobClosure - { - void - invokeJobOperation (JobParameter parameter) - { - UNIMPLEMENTED ("conjure a verifiable dummy job operation"); - } - - void - signalFailure (JobParameter,JobFailureReason) - { - NOTREACHED ("Job failure is not subject of this test"); - } - - JobKind - getJobKind() const - { - return META_JOB; - } - - bool - verify (Time nominalJobTime, InvocationInstanceID invoKey) const - { - UNIMPLEMENTED ("what the hell do we need to mock for this operation????"); - } - - size_t - hashOfInstance(InvocationInstanceID invoKey) const - { - return boost::hash_value (invoKey.metaInfo.a); - } - }; - - /** actual instance of the test dummy job operation */ - DummyClosure dummyClosure; - - const int MAX_PARAM_A(1000); - const int MAX_PARAM_B(10); - } + using lib::time::Time; + /** + * Test helper: generate test dummy jobs with built-in diagnostics. + * Each invocation of such a dummy job will be logged internally + * and can be investigated and verified afterwards. + */ struct DummyJob { - static Job - build() - { - InvocationInstanceID invoKey; - invoKey.metaInfo.a = rand() % MAX_PARAM_A; - invoKey.metaInfo.b = rand() % MAX_PARAM_B; - - Time nominalTime = lib::test::randTime(); - - return Job(dummyClosure, invoKey, nominalTime); - } + static Job build(); ///< uses random job definition values + static Job build (Time nominalTime, int additionalKey); - static Job - build (Time nominalTime, int additionalKey) - { - InvocationInstanceID invoKey; - invoKey.metaInfo.a = additionalKey; - invoKey.metaInfo.b = rand() % MAX_PARAM_B; - - return Job(dummyClosure, invoKey, nominalTime); - } - - static bool - was_invoked (Job const& job) - { - UNIMPLEMENTED ("look up invocation from logging hashtable"); - } - - static Time - invocationTime (Job const& job) - { - UNIMPLEMENTED ("look up invocation from logging hashtable"); - } + static bool was_invoked (Job const& job); + static Time invocationTime (Job const& job); }; diff --git a/tests/core/backend/engine/job-hash-test.cpp b/tests/core/backend/engine/job-hash-test.cpp index 350ae0888..5318728b1 100644 --- a/tests/core/backend/engine/job-hash-test.cpp +++ b/tests/core/backend/engine/job-hash-test.cpp @@ -27,6 +27,8 @@ #include "backend/real-clock.hpp" #include "backend/engine/dummy-job.hpp" +#include + namespace backend { namespace engine { @@ -42,7 +44,7 @@ namespace test { * especially verify that job data is passed properly back to the * closure and that a identity can be constructed based on a * hash of the job's data. - * + * * @see Job * @see JobClosure * @see SchedulerInterface_test @@ -90,16 +92,32 @@ namespace test { CHECK (hash_value(job1) == hash_value(copy)); copy.parameter.nominalTime++; - CHECK (hash_value(job1) != hash_value(copy)); + CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the concrete nominal job time copy = job1; copy.parameter.invoKey.metaInfo.a++; - CHECK (hash_value(job1) != hash_value(copy)); - + CHECK (hash_value(job1) != hash_value(copy)); // hash value depends on the internal interpretation of the invocation key + + + class OtherClosure + : public JobClosure + { + void invokeJobOperation (JobParameter) { /* irrelevant */ } + void signalFailure (JobParameter,JobFailureReason) { /* irrelevant */ } + JobKind getJobKind() const { return META_JOB; } + bool verify (Time, InvocationInstanceID) const { return false; } + + size_t + hashOfInstance(InvocationInstanceID invoKey) const + { + return boost::hash_value (invoKey.metaInfo.a); + } + }; + OtherClosure someOtherClosure; + copy = job1; - DummyClosure dummyClosure; - copy.jobClosure = &dummyClosure; - CHECK (hash_value(job1) != hash_value(copy)); + copy.jobClosure = &someOtherClosure; + CHECK (hash_value(job1) != hash_value(copy)); // hash value indeed depends on the concrete job closure instance } }; diff --git a/tests/core/backend/engine/scheduler-interface-test.cpp b/tests/core/backend/engine/scheduler-interface-test.cpp index d1fcc3518..af250a669 100644 --- a/tests/core/backend/engine/scheduler-interface-test.cpp +++ b/tests/core/backend/engine/scheduler-interface-test.cpp @@ -158,7 +158,6 @@ namespace test { Time deadline(TEST_START_TIME + nominalTime); CHECK (monitor.has_job_scheduled_at (deadline)); - CHECK (isSameObject (dummyClosure, monitor.job_at(deadline).jobClosure)); CHECK (nominalTime == monitor.job_at(deadline).parameter.nominalTime); } }