render job dummy passes unit test

the basic job and job closure interface is mostly settled now.
We can define and invoke render jobs, and distinguish jobs
through a hash ID
This commit is contained in:
Fischlurch 2013-09-07 02:37:17 +02:00
parent 7ba10619aa
commit 2b8ac2d071
8 changed files with 271 additions and 94 deletions

View file

@ -34,6 +34,7 @@
#include "backend/engine/job.h"
#include "lib/util.hpp"
#include <boost/functional/hash.hpp>
#include <typeinfo>
@ -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

View file

@ -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&);
};

View file

@ -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 <typename MAP>
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 <typename SEQ>

View file

@ -43,7 +43,8 @@ PLANNED "Frame Dispatcher Interface" DispatcherInterface_test <<END
END
PLANNED "Render job properties" JobHash_test <<END
TEST "Render job properties" JobHash_test <<END
return: 0
END

View file

@ -0,0 +1,190 @@
/*
DummyJob - diagnostic job for unit tests
Copyright (C) Lumiera.org
2013, Hermann Vosseler <Ichthyostega@web.de>
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 <cstdlib>
#include <tr1/unordered_map>
#include <boost/functional/hash.hpp>
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<HashVal,Invocation> invocationLog_;
public:
Invocation const&
queryInvocation (JobParameter param) const
{
return access_or_default (invocationLog_, hash_value(param)
,NullValue<Invocation>::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

View file

@ -27,94 +27,27 @@
#include "backend/engine/job.h"
#include "lib/test/test-helper.hpp"
#include <cstdlib>
#include <boost/functional/hash.hpp>
#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);
};

View file

@ -27,6 +27,8 @@
#include "backend/real-clock.hpp"
#include "backend/engine/dummy-job.hpp"
#include <boost/functional/hash.hpp>
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
}
};

View file

@ -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);
}
}