The prototype implementations of BuferProvider and OutputSlot are confusing; in-depth analysis shows that this is related to intermingling use of test instrumentation and features of a prototypical default. To get anywhere with the existing code, it seems we'd need to split off the test instrumentation (i.e. `DiagnosticBufferProvider`) altogether, even if this causes duplication of implementation code. Many years ago, when first implementing this prototype, I had no clear idea what the usage in the Render Engine entails, yet I noticed that some basic buffer »tracking« could be used as implementation primitive to simplify the prototyping work. This might be one of the reasons, why the "code behind" in the protocol implementations both in `BufferProvider` and `OutputSlot` became so strongly tangled, so that I way unable to make both protocols orthogonally composable.
217 lines
7.2 KiB
C++
217 lines
7.2 KiB
C++
/*
|
||
DiagnosticBufferProvider(Test) - verify a support facility for diagnostic/test purposes
|
||
|
||
Copyright (C)
|
||
2011, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
/** @file tracking-heap-block-provider-test.cpp
|
||
** unit test \ref DiagnosticBufferProvider_test
|
||
*/
|
||
|
||
|
||
#include "lib/error.hpp"
|
||
#include "lib/test/run.hpp"
|
||
#include "steam/engine/heap-mem-provider.hpp"
|
||
#include "steam/engine/buffhandle-attach.hpp"
|
||
#include "steam/engine/testframe.hpp"
|
||
|
||
#include <vector>
|
||
|
||
|
||
namespace steam {
|
||
namespace engine{
|
||
namespace test {
|
||
|
||
|
||
|
||
namespace { // Test fixture
|
||
|
||
const size_t TEST_ELM_SIZE = sizeof(uint);
|
||
const uint MAX_ELMS = 50;
|
||
|
||
std::vector<uint> testNumbers(MAX_ELMS);
|
||
|
||
|
||
bool
|
||
has_expectedContent (uint nr, diagn::Block& memoryBlock)
|
||
{
|
||
void* mem = memoryBlock.accessMemory();
|
||
uint data = *static_cast<uint*> (mem);
|
||
|
||
return data == testNumbers[nr];
|
||
}
|
||
|
||
bool
|
||
verifyUsedBlock (uint nr, diagn::Block& memoryBlock)
|
||
{
|
||
return memoryBlock.was_used()
|
||
and memoryBlock.was_closed()
|
||
and has_expectedContent (nr, memoryBlock);
|
||
}
|
||
}
|
||
|
||
|
||
/******************************************************************//**
|
||
* @test verify support to write mock components for buffer management.
|
||
* - HeapMemProvider is a „braindead“ implementation of the BufferProvider
|
||
* interface: it just claims new heap blocks and never de-allocates them.
|
||
* - adding a layer of instrumentation, the DiagnosticBufferProvider allows
|
||
* to count and verify allocated buffers and their content after the fact.
|
||
*/
|
||
class DiagnosticBufferProvider_test : public Test
|
||
{
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
seedRand();
|
||
|
||
simpleExample();
|
||
verifyStandardCase();
|
||
verifyTestProtocol();
|
||
}
|
||
|
||
|
||
void
|
||
simpleExample()
|
||
{
|
||
HeapMemProvider provider; /////////////////////////////////////OOO should be DiagnosticBufferProvider
|
||
|
||
BuffHandle testBuff = provider.lockBufferFor<TestFrame>();
|
||
CHECK (testBuff);
|
||
CHECK (testBuff.accessAs<TestFrame>().isSane());
|
||
|
||
uint dataID = 1 + rani(29);
|
||
testBuff.accessAs<TestFrame>() = testData(dataID);
|
||
|
||
provider.emitBuffer (testBuff);
|
||
provider.releaseBuffer(testBuff);
|
||
|
||
diagn::Block& block0 = provider.access_emitted(0);
|
||
CHECK (testData(dataID) == block0.accessMemory());
|
||
}
|
||
|
||
|
||
void
|
||
verifyStandardCase()
|
||
{
|
||
HeapMemProvider provider; /////////////////////////////////////OOO should be DiagnosticBufferProvider
|
||
|
||
BuffDescr buffType = provider.getDescriptorFor(TEST_ELM_SIZE);
|
||
uint numElms = provider.announce(MAX_ELMS, buffType);
|
||
CHECK (0 < numElms);
|
||
CHECK (numElms <= MAX_ELMS);
|
||
|
||
for (uint i=0; i<numElms; ++i)
|
||
{
|
||
BuffHandle buff = provider.lockBuffer(buffType);
|
||
buff.accessAs<uint>() = testNumbers[i] = rani(100000);
|
||
provider.emitBuffer (buff);
|
||
provider.releaseBuffer(buff);
|
||
}
|
||
|
||
for (uint nr=0; nr<numElms; ++nr)
|
||
{
|
||
CHECK (verifyUsedBlock (nr, provider.access_emitted(nr)));
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
verifyTestProtocol()
|
||
{
|
||
HeapMemProvider provider; /////////////////////////////////////OOO should be DiagnosticBufferProvider
|
||
|
||
BuffDescr buffType = provider.getDescriptorFor(TEST_ELM_SIZE);
|
||
|
||
BuffHandle bu1 = provider.lockBuffer (buffType);
|
||
BuffHandle bu2 = provider.lockBuffer (buffType);
|
||
BuffHandle bu3 = provider.lockBuffer (buffType);
|
||
BuffHandle bu4 = provider.lockBuffer (buffType);
|
||
BuffHandle bu5 = provider.lockBuffer (buffType);
|
||
|
||
// buffers are locked,
|
||
// but still within the per-type allocation pool
|
||
// while the output sequence is still empty
|
||
CHECK (!provider.access_emitted(0).was_used());
|
||
CHECK (!provider.access_emitted(1).was_used());
|
||
CHECK (!provider.access_emitted(2).was_used());
|
||
CHECK (!provider.access_emitted(3).was_used());
|
||
CHECK (!provider.access_emitted(4).was_used());
|
||
|
||
// can use the buffers for real
|
||
bu1.accessAs<uint>() = 1;
|
||
bu2.accessAs<uint>() = 2;
|
||
bu3.accessAs<uint>() = 3;
|
||
bu4.accessAs<uint>() = 4;
|
||
bu5.accessAs<uint>() = 5;
|
||
|
||
CHECK (0 == provider.emittedCnt());
|
||
|
||
// now emit buffers in shuffled order
|
||
provider.emitBuffer (bu3);
|
||
provider.emitBuffer (bu1);
|
||
provider.emitBuffer (bu5);
|
||
provider.emitBuffer (bu4);
|
||
provider.emitBuffer (bu2);
|
||
|
||
CHECK (5 == provider.emittedCnt());
|
||
|
||
CHECK (3 == provider.accessAs<uint>(0));
|
||
CHECK (1 == provider.accessAs<uint>(1));
|
||
CHECK (5 == provider.accessAs<uint>(2));
|
||
CHECK (4 == provider.accessAs<uint>(3));
|
||
CHECK (2 == provider.accessAs<uint>(4));
|
||
|
||
CHECK ( provider.access_emitted(0).was_used());
|
||
CHECK ( provider.access_emitted(1).was_used());
|
||
CHECK ( provider.access_emitted(2).was_used());
|
||
CHECK ( provider.access_emitted(3).was_used());
|
||
CHECK ( provider.access_emitted(4).was_used());
|
||
|
||
CHECK (!provider.access_emitted(0).was_closed());
|
||
CHECK (!provider.access_emitted(1).was_closed());
|
||
CHECK (!provider.access_emitted(2).was_closed());
|
||
CHECK (!provider.access_emitted(3).was_closed());
|
||
CHECK (!provider.access_emitted(4).was_closed());
|
||
|
||
bu5.release();
|
||
CHECK (!provider.access_emitted(0).was_closed());
|
||
CHECK (!provider.access_emitted(1).was_closed());
|
||
CHECK ( provider.access_emitted(2).was_closed());
|
||
CHECK (!provider.access_emitted(3).was_closed());
|
||
CHECK (!provider.access_emitted(4).was_closed());
|
||
|
||
bu2.release();
|
||
bu2.release();
|
||
bu5.release();
|
||
CHECK (!provider.access_emitted(0).was_closed());
|
||
CHECK (!provider.access_emitted(1).was_closed());
|
||
CHECK ( provider.access_emitted(2).was_closed());
|
||
CHECK (!provider.access_emitted(3).was_closed());
|
||
CHECK ( provider.access_emitted(4).was_closed());
|
||
|
||
CHECK (!bu2);
|
||
CHECK (bu3);
|
||
|
||
bu1.release();
|
||
bu3.release();
|
||
bu4.release();
|
||
|
||
CHECK (5 == provider.emittedCnt());
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (DiagnosticBufferProvider_test, "unit player");
|
||
|
||
|
||
|
||
}}} // namespace steam::engine::test
|