Lumiera  0.pre.03
»edit your freedom«
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"
21 #include "lib/test/test-helper.hpp"
24 #include "lib/util.hpp"
25 
26 #include <memory>
27 
28 using std::strncpy;
29 using std::unique_ptr;
30 using lib::test::randStr;
31 using util::isSameObject;
32 using util::isnil;
33 
34 
35 namespace steam {
36 namespace engine{
37 namespace 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&
48  accessAs (metadata::Entry& entry)
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());
96  verifyBasicProperties();
98  verifyStateMachine();
99  }
100 
101 
102  bool
103  ensure_proper_fixture()
104  {
105  if (!meta_)
106  meta_.reset(new BufferMetadata("BufferMetadata_test"));
107 
108  return (SIZE_A != SIZE_B)
109  && (JUST_SOMETHING != meta_->key(SIZE_A))
110  && (JUST_SOMETHING != meta_->key(SIZE_B))
111  ;
112  }
113 
114 
115  void
116  verifyBasicProperties()
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
318  verifyStateMachine()
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 
406  LAUNCHER (BufferMetadata_test, "unit player");
407 
408 
409 
410 }}} // namespace steam::engine::test
allocated buffer, no longer in use
Mock data frame for simulated rendering.
Definition: testframe.hpp:68
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:40
int rani(uint bound=_iBOUND())
Definition: random.hpp:135
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
allocated buffer blocked by protocol failure
an opaque mark to be used by the BufferProvider implementation.
allocated buffer, returned from client
allocated buffer actively in use
Entry & mark(BufferState newState)
Buffer state machine.
Steam-Layer implementation namespace root.
string randStr(size_t len)
create garbage string of given length
Definition: test-helper.cpp:61
Metadata for managing and accessing buffers.
Description of a Buffer-"type".
Simplistic test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A collection of frequently used helper functions to support unit testing.
Lumiera error handling (C++ interface).
unique_ptr< BufferMetadata > meta_
common Metadata table to be tested
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:52
A pair of functors to maintain a datastructure within a buffer.
abstract entry, not yet allocated
Registry for managing buffer metadata.
Unit test helper to generate fake test data frames.
placeholder type for the contents of a data buffer.
Definition: streamtype.hpp:112
A complete metadata Entry, based on a Key.
TestFrame & testData(uint seqNr, uint chanNr)
Helper to access a specific frame of test data at a fixed memory location.
Definition: testframe.cpp:186
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee&#39;s memory identities. ...
Definition: util.hpp:421