lumiera_documentation/tests/core/steam/engine/buffer-provider-protocol-test.cpp
Ichthyostega bb6401aaf3 BufferProvider: reshape diagnostics into a decorator
The intention is to build the tracking functionality from scratch,
while wrapping the old implementation as delegate. Hopefully this allows
to reduce the complexity of the existing implementation step by step,
until I'm ''able to grasp what this implementation does''.

This old implementation works, but is highly problematic code.
The Git history shows that I had already problems to understand this code
while building the DiagnosticOutputSlot, one year later.

To my defense, in both cases (2011 and 2012), I had no clear idea yet
how the processing within the engine would work, and thus I considered
that Buffer Type registry a mere implementation detail, that can be
lumped into the »code behind«.
2026-02-09 03:29:06 +01:00

267 lines
9.2 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
BufferProviderProtocol(Test) - demonstration of buffer provider usage cycle
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 buffer-provider-protocol-test.cpp
** unit test \ref BufferProviderProtocol_test
*/
#include "lib/error.hpp"
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/test/tracking-dummy.hpp"
#include "lib/util-foreach.hpp"
#include "steam/engine/testframe.hpp"
#include "steam/engine/test-rand-ontology.hpp"
#include "steam/engine/diagnostic-buffer-provider.hpp"
#include "steam/engine/buffhandle-attach.hpp"
#include "steam/engine/bufftable.hpp"
using util::isSameObject;
using util::for_each;
namespace steam {
namespace engine{
namespace test {
using lib::test::Dummy;
using steam::engine::BuffHandle;
using LERR_(LIFECYCLE);
using LERR_(LOGIC);
namespace { // Test fixture
const uint TEST_ELMS = 20;
HashVal
do_some_computation (TestFrame const& src, TestFrame& work, int64_t* data)
{
REQUIRE (data);
ont::Param param = src.getChecksum();
ont::manipulateFrame (&work, &src, param);
for (uint i=0; i<TestFrame::BUFFSIZ; ++i)
*(data+i) = work.data()[i];
return work.getChecksum();
}
}
/**************************************************************************//**
* @test document the usage patterns of the »Buffer Provider Protocol«
* Notably this protocol is used by the Render Engine during recursive `pull()`
* calls to claim and release the working buffers for each step; furthermore,
* also the output buffers are packaged as `BuffHandle` and used through this
* protocol.
*
* However, the actual `BufferProvider` implementation used here is instrumented
* for diagnostics and does not actually _manage_ buffers; rather each buffer
* is allocated on the heap and never released, so that any allocation and
* all buffer contents can be verified after the fact.
*/
class BufferProviderProtocol_test : public Test
{
virtual void
run (Arg)
{
verifySimpleUsage();
verifyRenderingUsage();
verifyObjectAttachment();
verifyObjectAttachmentFailure();
}
void
verifySimpleUsage()
{
// Create Test fixture.
// In real usage, a suitable memory/frame/buffer provider
// will be preconfigured, depending on the usage context
DiagnosticBufferProvider provider;
BuffHandle buff = provider.lockBufferFor<TestFrame>();
CHECK (buff.isValid());
CHECK (sizeof(TestFrame) <= buff.size());
buff.accessAs<TestFrame>() = testData(0);
TestFrame& content = buff.accessAs<TestFrame>();
CHECK (testData(0) == content);
buff.emit();
buff.release();
CHECK (!buff.isValid());
VERIFY_ERROR (LIFECYCLE, buff.accessAs<TestFrame>() );
auto diagnostic = watch(provider);
CHECK (diagnostic.buffer_was_used (0));
CHECK (diagnostic.buffer_was_closed (0));
CHECK (testData(0) == diagnostic.accessMemory (0));
}
/** @test demonstrate the full sequence of invocations during rendering
*/
void
verifyRenderingUsage()
{
// Test fixture: allows to track/verify allocations after the fact
DiagnosticBufferProvider provider;
using DataBuff = std::array<int64_t, TestFrame::BUFFSIZ>;
constexpr auto BUFFSIZ = sizeof(DataBuff);
BuffDescr desc1 = provider.getDescriptor<size_t>();
BuffDescr desc2 = provider.getDescriptor<TestFrame>(); // note: implies also sizeof(TestFrame)
BuffDescr desc3 = provider.getDescriptorFor(BUFFSIZ); // Can also request fixed sized raw storage.
// CHECK (desc1.verifyValidity()); /////////////////////////////////////////////////////////////////////////////OOO fails (which is wrong)
// CHECK (desc2.verifyValidity());
// CHECK (desc3.verifyValidity());
uint num1 = desc1.announce(TEST_ELMS);
uint num2 = desc1.announce(TEST_ELMS + 1);
uint num3 = desc2.announce(TEST_ELMS * 2);
CHECK (num1 == TEST_ELMS);
CHECK (num2 == TEST_ELMS + 1);
CHECK (num3 == TEST_ELMS * 2);
// emulate a progressive processing
BuffHandle workBuff = desc2.lockBuffer();
workBuff.accessAs<TestFrame>() = testData(0);
for (uint i=0; i<TEST_ELMS; ++i)
{
BuffHandle srcBuff = workBuff;
workBuff = desc2.lockBuffer();
BuffHandle markBuff = desc1.lockBuffer();
BuffHandle dataBuff = desc3.lockBuffer();
CHECK (srcBuff.isValid());
CHECK (markBuff.isValid());
CHECK (workBuff.isValid());
CHECK (dataBuff.isValid());
TestFrame& src = srcBuff.accessAs<TestFrame>();
size_t& mark = markBuff.accessAs<size_t>();
TestFrame& work = workBuff.accessAs<TestFrame>();
DataBuff& data = dataBuff.accessAs<DataBuff>();
mark = do_some_computation (src,work, &data[0]);
srcBuff.emit();
dataBuff.emit();
markBuff.emit();
srcBuff.release();
dataBuff.release();
markBuff.release();
// note : workBuff passed to next iteration
}
CHECK (workBuff.isValid());
auto endSum = workBuff.accessAs<TestFrame>().getChecksum();
workBuff.release(); // note: not every buffer need be emitted.
auto diagnostic = watch(provider);
CHECK (diagnostic.buffer_was_used (0));
CHECK (diagnostic.buffer_was_closed (0));
// CHECK (diagnostic.all_buffers_released()); ///////////////OOO do we need this API?
}
void
verifyObjectAttachment()
{
DiagnosticBufferProvider provider;
BuffDescr type_A = provider.getDescriptorFor(sizeof(TestFrame));
BuffDescr type_B = provider.getDescriptorFor(sizeof(int));
BuffDescr type_C = provider.getDescriptor<int>();
BuffHandle handle_A = provider.lockBuffer(type_A);
BuffHandle handle_B = provider.lockBuffer(type_B);
BuffHandle handle_C = provider.lockBuffer(type_C);
CHECK (handle_A);
CHECK (handle_B);
CHECK (handle_C);
CHECK (sizeof(TestFrame) == handle_A.size());
CHECK (sizeof( int ) == handle_B.size());
CHECK (sizeof( int ) == handle_C.size());
TestFrame& embeddedFrame = handle_A.create<TestFrame>();
CHECK (isSameObject (*handle_A, embeddedFrame));
CHECK (embeddedFrame.isAlive());
CHECK (embeddedFrame.isSane());
VERIFY_ERROR (LOGIC, handle_B.create<TestFrame>()); // too small to hold a TestFrame
VERIFY_ERROR (LIFECYCLE, handle_C.create<int>()); // has already an attached TypeHandler (creating an int)
handle_A.release();
handle_B.release();
handle_C.release();
CHECK (embeddedFrame.isDead());
CHECK (embeddedFrame.isSane());
}
void
verifyObjectAttachmentFailure()
{
DiagnosticBufferProvider provider;
BuffDescr type_D = provider.getDescriptorFor(sizeof(Dummy));
Dummy::checksum() = 0;
BuffHandle handle_D = provider.lockBuffer(type_D);
CHECK (0 == Dummy::checksum()); // nothing created thus far
handle_D.create<Dummy>();
CHECK (0 < Dummy::checksum());
handle_D.release();
CHECK (0 == Dummy::checksum());
BuffHandle handle_DD = provider.lockBuffer(type_D);
CHECK (0 == Dummy::checksum());
Dummy::activateCtorFailure();
CHECK (handle_DD.isValid());
try
{
handle_DD.create<Dummy>();
NOTREACHED ("Dummy ctor should fail");
}
catch (int val)
{
CHECK (!handle_DD.isValid());
CHECK (0 < Dummy::checksum());
CHECK (val == Dummy::checksum());
}
VERIFY_ERROR (LIFECYCLE, handle_DD.accessAs<Dummy>() );
VERIFY_ERROR (LIFECYCLE, handle_DD.create<Dummy>() );
}
};
/** Register this test class... */
LAUNCHER (BufferProviderProtocol_test, "unit engine");
}}} // namespace steam::engine::test