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 "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
|
||||
|
||||
|
|
|
|||
|
|
@ -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&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
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 "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);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue