Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
buffer-metadata-test.cpp
Go to the documentation of this file.
1/*
2 BufferMetadata(Test) - properties of internal data buffer metadata
3
4 Copyright (C)
5 2011, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
19#include "lib/error.hpp"
20#include "lib/test/run.hpp"
24#include "lib/util.hpp"
25
26#include <memory>
27
28using std::strncpy;
29using std::unique_ptr;
32using util::isnil;
33
34
35namespace steam {
36namespace engine{
37namespace test {
38
39 using LERR_(FATAL);
40 using LERR_(INVALID);
41 using LERR_(LIFECYCLE);
42
43
44 namespace { // Test fixture
45
46 template<typename TY>
47 TY&
49 {
50 TY* ptr = reinterpret_cast<TY*> (entry.access());
51 ASSERT (ptr);
52 return *ptr;
53 }
54
55 template<typename X>
57 mark_as_Buffer(X& something)
58 {
59 return reinterpret_cast<metadata::Buff*> (std::addressof(something));
60 }
61
62
63 const size_t TEST_MAX_SIZE = 1024 * 1024;
64
65 HashVal JUST_SOMETHING = 123;
66 auto SOME_POINTER = mark_as_Buffer(JUST_SOMETHING);
67
68 }//(End) Test fixture and helpers
69
70
71
72
73
74
75 /***************************************************************/
80 class BufferMetadata_test : public Test
81 {
82 size_t SIZE_A{0};
83 size_t SIZE_B{0};
84
86 unique_ptr<BufferMetadata> meta_;
87
88 virtual void
89 run (Arg)
90 {
91 seedRand();
92 SIZE_A = 1 + rani(TEST_MAX_SIZE);
93 SIZE_B = 1 + rani(TEST_MAX_SIZE);
94
95 CHECK (ensure_proper_fixture());
99 }
100
101
102 bool
104 {
105 if (!meta_)
106 meta_.reset(new BufferMetadata("BufferMetadata_test"));
107
108 return (SIZE_A != SIZE_B)
109 and (JUST_SOMETHING != meta_->key(SIZE_A))
110 and (JUST_SOMETHING != meta_->key(SIZE_B))
111 ;
112 }
113
114
115 void
117 {
118 // retrieve some type keys
119 metadata::Key key = meta_->key(SIZE_A);
120 CHECK (key);
121
122 metadata::Key key1 = meta_->key(SIZE_A);
123 metadata::Key key2 = meta_->key(SIZE_B);
124 CHECK (key1);
125 CHECK (key2);
126 CHECK (key == key1);
127 CHECK (key != key2);
128
129 // access metadata entries
130 VERIFY_ERROR (INVALID, meta_->get(0));
131 VERIFY_ERROR (INVALID, meta_->get(JUST_SOMETHING));
132 CHECK ( & meta_->get(key));
133 CHECK ( & meta_->get(key1));
134 CHECK ( & meta_->get(key2));
135
136 CHECK ( isSameObject (meta_->get(key), meta_->get(key)));
137 CHECK ( isSameObject (meta_->get(key), meta_->get(key1)));
138 CHECK (!isSameObject (meta_->get(key), meta_->get(key2)));
139
140 // entries retrieved thus far were inactive (type only) entries
141 metadata::Entry& m1 = meta_->get(key);
142 CHECK (NIL == m1.state());
143 CHECK (!meta_->isLocked(key));
144
145 VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED));
146 VERIFY_ERROR (LIFECYCLE, m1.mark(FREE) );
147
148 // now create an active (buffer) entry
149 metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
150 CHECK (!isSameObject (m1,m2));
151 CHECK (NIL == m1.state());
152 CHECK (LOCKED == m2.state());
153 CHECK (SOME_POINTER == m2.access()); // buffer pointer associated
154
155 // entries are unique and identifiable
156 HashVal keyX = meta_->key(key1, SOME_POINTER);
157 CHECK (meta_->isLocked(keyX));
158 CHECK (keyX != key1);
159 CHECK (keyX);
160
161 CHECK ( isSameObject (m1, meta_->get(key)));
162 CHECK ( isSameObject (m1, meta_->get(key1)));
163 CHECK ( isSameObject (m2, meta_->get(keyX)));
164 CHECK ( key1 == m2.parentKey());
165
166 // now able to do state transitions
167 CHECK (LOCKED == m2.state());
168 m2.mark(EMITTED);
169 CHECK (EMITTED == m2.state());
170 CHECK (SOME_POINTER == m2.access());
171 CHECK ( meta_->isLocked(keyX));
172 CHECK ( meta_->isKnown(keyX));
173
174 // but the FREE state is a dead end
175 m2.mark(FREE);
176 CHECK (!meta_->isLocked(keyX));
177 CHECK ( meta_->isKnown(keyX));
178 CHECK ( meta_->isKnown(key1));
179 VERIFY_ERROR (LIFECYCLE, m2.access());
180 VERIFY_ERROR (FATAL, m2.mark(LOCKED)); // buffer missing
181 CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible
182
183 // release buffer...
184 meta_->release(keyX);
185 CHECK (!meta_->isLocked(keyX));
186 CHECK (!meta_->isKnown(keyX));
187 CHECK ( meta_->isKnown(key1));
188 VERIFY_ERROR (INVALID, meta_->get(keyX)); // now unaccessible
189 }
190
191
199 void
201 {
202 // to build a descriptor for a buffer holding a TestFrame
203 TypeHandler attachTestFrame = TypeHandler::create<TestFrame>();
204 metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame);
205
206 // to build a descriptor for a raw buffer of size SIZE_B
207 metadata::Key rawBuffType = meta_->key(SIZE_B);
208
209 // to announce using a number of buffers of this type
210 LocalTag transaction1(1);
211 LocalTag transaction2(2);
212 bufferType1 = meta_->key(bufferType1, transaction1);
213 rawBuffType = meta_->key(rawBuffType, transaction2);
214 // these type keys are now handed over to the client,
215 // embedded into a Buffer Descriptor...
216
217 // later, when it comes to actually *locking* those buffers...
218 using RawBuffer = std::byte;
219 void* storage = malloc (2*SIZE_B);
220
221 // do the necessary memory allocations behind the scenes...
222 RawBuffer* rawbuf = (RawBuffer*)storage; // coding explicit allocations here for sake of clarity;
223 TestFrame* frames = new TestFrame[3]; // a real-world BufferProvider would use some kind of allocator
224
225 // track individual buffers by metadata entries
226 metadata::Entry& f0 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[0]));
227 metadata::Entry& f1 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[1]));
228 metadata::Entry& f2 = meta_->markLocked(bufferType1, mark_as_Buffer(frames[2]));
229
230 metadata::Entry& r0 = meta_->markLocked(rawBuffType, mark_as_Buffer(rawbuf[ 0]));
231 metadata::Entry& r1 = meta_->markLocked(rawBuffType, mark_as_Buffer(rawbuf[SIZE_B]));
232
233 CHECK (LOCKED == f0.state());
234 CHECK (LOCKED == f1.state());
235 CHECK (LOCKED == f2.state());
236 CHECK (LOCKED == r0.state());
237 CHECK (LOCKED == r1.state());
238
239 CHECK (transaction1 == f0.localTag());
240 CHECK (transaction1 == f1.localTag());
241 CHECK (transaction1 == f2.localTag());
242 CHECK (transaction2 == r0.localTag());
243 CHECK (transaction2 == r1.localTag());
244
245 auto adr =[](auto* x){ return reinterpret_cast<size_t>(x); };
246
247 CHECK (adr(f0.access()) == adr(frames+0));
248 CHECK (adr(f1.access()) == adr(frames+1));
249 CHECK (adr(f2.access()) == adr(frames+2));
250 CHECK (adr(r0.access()) == adr(rawbuf+0 ));
251 CHECK (adr(r1.access()) == adr(rawbuf+SIZE_B));
252
253 TestFrame defaultFrame;
254 CHECK (defaultFrame == f0.access());
255 CHECK (defaultFrame == f1.access());
256 CHECK (defaultFrame == f2.access());
257
258 // at that point, we'd return BuffHandles to the client
259 HashVal handle_f0(f0);
260 HashVal handle_f1(f1);
261 HashVal handle_f2(f2);
262 HashVal handle_r0(r0);
263 HashVal handle_r1(r1);
264
265 // client uses the buffers---------------------(Start)
266 accessAs<TestFrame> (f0) = testData(1);
267 accessAs<TestFrame> (f1) = testData(2);
268 accessAs<TestFrame> (f2) = testData(3);
269
270 CHECK (testData(1) == frames[0]);
271 CHECK (testData(2) == frames[1]);
272 CHECK (testData(3) == frames[2]);
273
274 CHECK (TestFrame::isAlive (f0.access()));
275 CHECK (TestFrame::isAlive (f1.access()));
276 CHECK (TestFrame::isAlive (f2.access()));
277
278 strncpy (& accessAs<char> (r0), randStr(SIZE_B - 1).c_str(), SIZE_B);
279 strncpy (& accessAs<char> (r1), randStr(SIZE_B - 1).c_str(), SIZE_B);
280
281 // client might trigger some state transitions
282 f0.mark(EMITTED);
283 f1.mark(EMITTED);
284 f1.mark(BLOCKED);
285 // client uses the buffers---------------------(End)
286
287
288 f0.mark(FREE); // note: implicitly invoking the embedded dtor
289 f1.mark(FREE);
290 f2.mark(FREE);
291 r0.mark(FREE);
292 r1.mark(FREE);
293
294
295 meta_->release(handle_f0);
296 meta_->release(handle_f1);
297 meta_->release(handle_f2);
298 meta_->release(handle_r0);
299 meta_->release(handle_r1);
300
301 CHECK (TestFrame::isDead (&frames[0])); // was destroyed implicitly
302 CHECK (TestFrame::isDead (&frames[1]));
303 CHECK (TestFrame::isDead (&frames[2]));
304
305 // manual cleanup of test allocations
306 delete[] frames;
307 free(storage);
308
309 CHECK (!meta_->isLocked(handle_f0));
310 CHECK (!meta_->isLocked(handle_f1));
311 CHECK (!meta_->isLocked(handle_f2));
312 CHECK (!meta_->isLocked(handle_r0));
313 CHECK (!meta_->isLocked(handle_r1));
314 }
315
316
317 void
319 {
320 // start with building a type key....
321 metadata::Key key = meta_->key(SIZE_A);
322 CHECK (NIL == meta_->get(key).state());
323 CHECK (meta_->get(key).isTypeKey());
324 CHECK (!meta_->isLocked(key));
325
326 VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(LOCKED) );
327 VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(EMITTED));
328 VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(BLOCKED));
329 VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(FREE) );
330 VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(NIL) );
331
332 // now build a concrete buffer entry
333 metadata::Entry& entry = meta_->markLocked(key, SOME_POINTER);
334 CHECK (LOCKED == entry.state());
335 CHECK (!entry.isTypeKey());
336
337 CHECK (SOME_POINTER == entry.access());
338
339 VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); // invalid state transition
340 VERIFY_ERROR (FATAL, entry.mark(NIL) );
341
342 entry.mark (EMITTED); // valid transition
343 CHECK (EMITTED == entry.state());
344 CHECK (entry.isLocked());
345
346 VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
347 VERIFY_ERROR (FATAL, entry.mark(EMITTED));
348 VERIFY_ERROR (FATAL, entry.mark(NIL) );
349 CHECK (EMITTED == entry.state());
350
351 entry.mark (FREE);
352 CHECK (FREE == entry.state());
353 CHECK (!entry.isLocked());
354 CHECK (!entry.isTypeKey());
355
356 VERIFY_ERROR (LIFECYCLE, entry.access() );
357 VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
358 VERIFY_ERROR (FATAL, entry.mark(EMITTED));
359 VERIFY_ERROR (FATAL, entry.mark(BLOCKED));
360 VERIFY_ERROR (FATAL, entry.mark(FREE) );
361 VERIFY_ERROR (FATAL, entry.mark(NIL) );
362
363 // re-use buffer slot, start new lifecycle
364 auto* SOME_OTHER_LOCATION = mark_as_Buffer(*this);
365 entry.lock (SOME_OTHER_LOCATION);
366 CHECK (LOCKED == entry.state());
367 CHECK (entry.isLocked());
368
369 VERIFY_ERROR (LIFECYCLE, entry.lock(SOME_POINTER));
370
371 entry.mark (BLOCKED); // go directly to the blocked state
372 CHECK (BLOCKED == entry.state());
373 VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
374 VERIFY_ERROR (FATAL, entry.mark(EMITTED) );
375 VERIFY_ERROR (FATAL, entry.mark(BLOCKED) );
376 VERIFY_ERROR (FATAL, entry.mark(NIL) );
377
378 CHECK (SOME_OTHER_LOCATION == entry.access());
379
380 entry.mark (FREE);
381 CHECK (!entry.isLocked());
382 VERIFY_ERROR (LIFECYCLE, entry.access() );
383
384 meta_->lock(key, SOME_POINTER);
385 CHECK (entry.isLocked());
386
387 entry.mark (EMITTED);
388 entry.mark (BLOCKED);
389 CHECK (BLOCKED == entry.state());
390 CHECK (SOME_POINTER == entry.access());
391
392 // can't discard metadata, need to free first
393 VERIFY_ERROR (LIFECYCLE, meta_->release(entry) );
394 CHECK (meta_->isKnown(entry));
395 CHECK (entry.isLocked());
396
397 entry.mark (FREE);
398 meta_->release(entry);
399 CHECK (!meta_->isKnown(entry));
400 CHECK ( meta_->isKnown(key));
401 }
402 };
403
404
407
408
409
410}}} // namespace steam::engine::test
Metadata for managing and accessing buffers.
placeholder type for the contents of a data buffer.
Registry for managing buffer metadata.
an opaque mark to be used by the BufferProvider implementation.
A complete metadata Entry, based on a Key.
Entry & mark(BufferState newState)
Buffer state machine.
Description of a Buffer-"type".
LocalTag const & localTag() const
unique_ptr< BufferMetadata > meta_
common Metadata table to be tested
Mock data frame for simulated rendering.
Definition testframe.hpp:69
Lumiera error handling (C++ interface).
#define LERR_(_NAME_)
Definition error.hpp:45
string randStr(size_t len)
create garbage string of given length
TestFrame & testData(uint seqNr, uint chanNr)
Helper to access a specific frame of test data at a fixed memory location.
@ BLOCKED
allocated buffer blocked by protocol failure
@ NIL
abstract entry, not yet allocated
@ LOCKED
allocated buffer actively in use
@ FREE
allocated buffer, no longer in use
@ EMITTED
allocated buffer, returned from client
Steam-Layer implementation namespace root.
Test runner and basic definitions for tests.
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities.
Definition util.hpp:421
bool isnil(lib::time::Duration const &dur)
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
A pair of functors to maintain a datastructure within a buffer.
A collection of frequently used helper functions to support unit testing.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Unit test helper to generate fake test data frames.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...