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:
parent
7ba10619aa
commit
2b8ac2d071
8 changed files with 271 additions and 94 deletions
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "backend/engine/job.h"
|
#include "backend/engine/job.h"
|
||||||
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
#include <boost/functional/hash.hpp>
|
#include <boost/functional/hash.hpp>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
|
@ -55,8 +56,8 @@ namespace engine {
|
||||||
} // (END) Details...
|
} // (END) Details...
|
||||||
|
|
||||||
|
|
||||||
// using mobject::Placement;
|
using lib::HashVal;
|
||||||
// using mobject::session::Effect;
|
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.
|
/** hash value based on all relevant job data.
|
||||||
* Job records hashing to the same value shall be considered equivalent.
|
* Job records hashing to the same value shall be considered equivalent.
|
||||||
* Since the interpretation of the #InvocationInstanceID is a private detail
|
* 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
|
* concrete JobClosure. This is not considered problematic, as the normal
|
||||||
* job operation and scheduling doesn't rely on the job's hash. Only some
|
* job operation and scheduling doesn't rely on the job's hash. Only some
|
||||||
* diagnostic facilities do. */
|
* diagnostic facilities do. */
|
||||||
size_t
|
HashVal
|
||||||
hash_value (Job const& job)
|
hash_value (Job const& job)
|
||||||
{
|
{
|
||||||
size_t hash = myClosure(&job).hashOfInstance (job.parameter.invoKey);
|
return myClosure(&job).hash_value (job.parameter);
|
||||||
boost::hash_combine(hash, typeid(job.jobClosure).name());
|
|
||||||
boost::hash_combine(hash, job.parameter.nominalTime);
|
|
||||||
return hash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
}} // namespace backend::engine
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,8 @@ namespace engine {
|
||||||
virtual JobKind getJobKind() const =0;
|
virtual JobKind getJobKind() const =0;
|
||||||
virtual bool verify (Time, InvocationInstanceID) const =0;
|
virtual bool verify (Time, InvocationInstanceID) const =0;
|
||||||
virtual size_t hashOfInstance(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;
|
JobKind getKind() const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
|
bool usesClosure (JobClosure const&) const;
|
||||||
|
|
||||||
/** provide a hash based Job ID */
|
/** provide a hash based Job ID */
|
||||||
friend lib::HashVal hash_value (Job const&);
|
friend lib::HashVal hash_value (Job const&);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,23 @@ namespace util {
|
||||||
return defaultVal;
|
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
|
/** shortcut for removing all copies of an Element
|
||||||
* in any sequential collection */
|
* in any sequential collection */
|
||||||
template <typename SEQ>
|
template <typename SEQ>
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,8 @@ PLANNED "Frame Dispatcher Interface" DispatcherInterface_test <<END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
PLANNED "Render job properties" JobHash_test <<END
|
TEST "Render job properties" JobHash_test <<END
|
||||||
|
return: 0
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
190
tests/core/backend/engine/dummy-job.cpp
Normal file
190
tests/core/backend/engine/dummy-job.cpp
Normal 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
|
||||||
|
|
@ -27,94 +27,27 @@
|
||||||
|
|
||||||
|
|
||||||
#include "backend/engine/job.h"
|
#include "backend/engine/job.h"
|
||||||
#include "lib/test/test-helper.hpp"
|
#include "lib/time/timevalue.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace backend{
|
namespace backend{
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
||||||
namespace {
|
using lib::time::Time;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
struct DummyJob
|
||||||
{
|
{
|
||||||
static Job
|
static Job build(); ///< uses random job definition values
|
||||||
build()
|
static Job build (Time nominalTime, int additionalKey);
|
||||||
{
|
|
||||||
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
|
static bool was_invoked (Job const& job);
|
||||||
build (Time nominalTime, int additionalKey)
|
static Time invocationTime (Job const& job);
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
#include "backend/real-clock.hpp"
|
#include "backend/real-clock.hpp"
|
||||||
#include "backend/engine/dummy-job.hpp"
|
#include "backend/engine/dummy-job.hpp"
|
||||||
|
|
||||||
|
#include <boost/functional/hash.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace engine {
|
namespace engine {
|
||||||
|
|
@ -42,7 +44,7 @@ namespace test {
|
||||||
* especially verify that job data is passed properly back to the
|
* especially verify that job data is passed properly back to the
|
||||||
* closure and that a identity can be constructed based on a
|
* closure and that a identity can be constructed based on a
|
||||||
* hash of the job's data.
|
* hash of the job's data.
|
||||||
*
|
*
|
||||||
* @see Job
|
* @see Job
|
||||||
* @see JobClosure
|
* @see JobClosure
|
||||||
* @see SchedulerInterface_test
|
* @see SchedulerInterface_test
|
||||||
|
|
@ -90,16 +92,32 @@ namespace test {
|
||||||
CHECK (hash_value(job1) == hash_value(copy));
|
CHECK (hash_value(job1) == hash_value(copy));
|
||||||
|
|
||||||
copy.parameter.nominalTime++;
|
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 = job1;
|
||||||
copy.parameter.invoKey.metaInfo.a++;
|
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;
|
copy = job1;
|
||||||
DummyClosure dummyClosure;
|
copy.jobClosure = &someOtherClosure;
|
||||||
copy.jobClosure = &dummyClosure;
|
CHECK (hash_value(job1) != hash_value(copy)); // hash value indeed depends on the concrete job closure instance
|
||||||
CHECK (hash_value(job1) != hash_value(copy));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,6 @@ namespace test {
|
||||||
Time deadline(TEST_START_TIME + nominalTime);
|
Time deadline(TEST_START_TIME + nominalTime);
|
||||||
|
|
||||||
CHECK (monitor.has_job_scheduled_at (deadline));
|
CHECK (monitor.has_job_scheduled_at (deadline));
|
||||||
CHECK (isSameObject (dummyClosure, monitor.job_at(deadline).jobClosure));
|
|
||||||
CHECK (nominalTime == monitor.job_at(deadline).parameter.nominalTime);
|
CHECK (nominalTime == monitor.job_at(deadline).parameter.nominalTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue