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) Lumiera.org
5  2011, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/error.hpp"
29 #include "lib/test/run.hpp"
30 #include "lib/test/test-helper.hpp"
31 #include "lib/util.hpp"
34 
35 #include <cstdlib>
36 #include <cstring>
37 #include <memory>
38 
39 using std::strncpy;
40 using std::unique_ptr;
41 using lib::test::randStr;
42 using util::isSameObject;
43 using util::isnil;
44 
45 
46 namespace steam {
47 namespace engine{
48 namespace test {
49 
50  using LERR_(FATAL);
51  using LERR_(INVALID);
52  using LERR_(LIFECYCLE);
53 
54 
55  namespace { // Test fixture
56 
57  const size_t TEST_MAX_SIZE = 1024 * 1024;
58 
59  const size_t SIZE_A = 1 + rand() % TEST_MAX_SIZE;
60  const size_t SIZE_B = 1 + rand() % TEST_MAX_SIZE;
61 
62  HashVal JUST_SOMETHING = 123;
63  void* const SOME_POINTER = &JUST_SOMETHING;
64 
65 
66  template<typename TY>
67  TY&
68  accessAs (metadata::Entry& entry)
69  {
70  TY* ptr = reinterpret_cast<TY*> (entry.access());
71  ASSERT (ptr);
72  return *ptr;
73  }
74  }//(End) Test fixture and helpers
75 
76 
77 
78 
79 
80 
81  /***************************************************************/
86  class BufferMetadata_test : public Test
87  {
89  unique_ptr<BufferMetadata> meta_;
90 
91  virtual void
92  run (Arg)
93  {
94  CHECK (ensure_proper_fixture());
95  verifyBasicProperties();
97  verifyStateMachine();
98  }
99 
100 
101  bool
102  ensure_proper_fixture()
103  {
104  if (!meta_)
105  meta_.reset(new BufferMetadata("BufferMetadata_test"));
106 
107  return (SIZE_A != SIZE_B)
108  && (JUST_SOMETHING != meta_->key(SIZE_A))
109  && (JUST_SOMETHING != meta_->key(SIZE_B))
110  ;
111  }
112 
113 
114  void
115  verifyBasicProperties()
116  {
117  // retrieve some type keys
118  metadata::Key key = meta_->key(SIZE_A);
119  CHECK (key);
120 
121  metadata::Key key1 = meta_->key(SIZE_A);
122  metadata::Key key2 = meta_->key(SIZE_B);
123  CHECK (key1);
124  CHECK (key2);
125  CHECK (key == key1);
126  CHECK (key != key2);
127 
128  // access metadata entries
129  VERIFY_ERROR (INVALID, meta_->get(0));
130  VERIFY_ERROR (INVALID, meta_->get(JUST_SOMETHING));
131  CHECK ( & meta_->get(key));
132  CHECK ( & meta_->get(key1));
133  CHECK ( & meta_->get(key2));
134 
135  CHECK ( isSameObject (meta_->get(key), meta_->get(key)));
136  CHECK ( isSameObject (meta_->get(key), meta_->get(key1)));
137  CHECK (!isSameObject (meta_->get(key), meta_->get(key2)));
138 
139  // entries retrieved thus far were inactive (type only) entries
140  metadata::Entry& m1 = meta_->get(key);
141  CHECK (NIL == m1.state());
142  CHECK (!meta_->isLocked(key));
143 
144  VERIFY_ERROR (LIFECYCLE, m1.mark(EMITTED));
145  VERIFY_ERROR (LIFECYCLE, m1.mark(FREE) );
146 
147  // now create an active (buffer) entry
148  metadata::Entry& m2 = meta_->markLocked (key, SOME_POINTER);
149  CHECK (!isSameObject (m1,m2));
150  CHECK (NIL == m1.state());
151  CHECK (LOCKED == m2.state());
152  CHECK (SOME_POINTER == m2.access()); // buffer pointer associated
153 
154  // entries are unique and identifiable
155  HashVal keyX = meta_->key(key1, SOME_POINTER);
156  CHECK (meta_->isLocked(keyX));
157  CHECK (keyX != key1);
158  CHECK (keyX);
159 
160  CHECK ( isSameObject (m1, meta_->get(key)));
161  CHECK ( isSameObject (m1, meta_->get(key1)));
162  CHECK ( isSameObject (m2, meta_->get(keyX)));
163  CHECK ( key1 == m2.parentKey());
164 
165  // now able to do state transitions
166  CHECK (LOCKED == m2.state());
167  m2.mark(EMITTED);
168  CHECK (EMITTED == m2.state());
169  CHECK (SOME_POINTER == m2.access());
170  CHECK ( meta_->isLocked(keyX));
171  CHECK ( meta_->isKnown(keyX));
172 
173  // but the FREE state is a dead end
174  m2.mark(FREE);
175  CHECK (!meta_->isLocked(keyX));
176  CHECK ( meta_->isKnown(keyX));
177  CHECK ( meta_->isKnown(key1));
178  VERIFY_ERROR (LIFECYCLE, m2.access());
179  VERIFY_ERROR (FATAL, m2.mark(LOCKED)); // buffer missing
180  CHECK ( isSameObject (m2, meta_->get(keyX))); // still accessible
181 
182  // release buffer...
183  meta_->release(keyX);
184  CHECK (!meta_->isLocked(keyX));
185  CHECK (!meta_->isKnown(keyX));
186  CHECK ( meta_->isKnown(key1));
187  VERIFY_ERROR (INVALID, meta_->get(keyX)); // now unaccessible
188  }
189 
190 
198  void
200  {
201  // to build a descriptor for a buffer holding a TestFrame
202  TypeHandler attachTestFrame = TypeHandler::create<TestFrame>();
203  metadata::Key bufferType1 = meta_->key(sizeof(TestFrame), attachTestFrame);
204 
205  // to build a descriptor for a raw buffer of size SIZE_B
206  metadata::Key rawBuffType = meta_->key(SIZE_B);
207 
208  // to announce using a number of buffers of this type
209  LocalKey transaction1(1);
210  LocalKey transaction2(2);
211  bufferType1 = meta_->key(bufferType1, transaction1);
212  rawBuffType = meta_->key(rawBuffType, transaction2);
213  // these type keys are now handed over to the client,
214  // embedded into a BufferDescriptor...
215 
216  // later, when it comes to actually *locking* those buffers...
217  typedef char RawBuffer[SIZE_B];
218  void* storage = malloc (2*SIZE_B);
219 
220  // do the necessary memory allocations behind the scenes...
221  RawBuffer* rawbuf = (RawBuffer*)storage; // coding explicit allocations here for sake of clarity;
222  TestFrame* frames = new TestFrame[3]; // a real-world BufferProvider would use some kind of allocator
223 
224  // track individual buffers by metadata entries
225  metadata::Entry& f0 = meta_->markLocked(bufferType1, &frames[0]);
226  metadata::Entry& f1 = meta_->markLocked(bufferType1, &frames[1]);
227  metadata::Entry& f2 = meta_->markLocked(bufferType1, &frames[2]);
228 
229  metadata::Entry& r0 = meta_->markLocked(rawBuffType, &rawbuf[0]);
230  metadata::Entry& r1 = meta_->markLocked(rawBuffType, &rawbuf[1]);
231 
232  CHECK (LOCKED == f0.state());
233  CHECK (LOCKED == f1.state());
234  CHECK (LOCKED == f2.state());
235  CHECK (LOCKED == r0.state());
236  CHECK (LOCKED == r1.state());
237 
238  CHECK (transaction1 == f0.localKey());
239  CHECK (transaction1 == f1.localKey());
240  CHECK (transaction1 == f2.localKey());
241  CHECK (transaction2 == r0.localKey());
242  CHECK (transaction2 == r1.localKey());
243 
244 
245  CHECK (f0.access() == frames+0);
246  CHECK (f1.access() == frames+1);
247  CHECK (f2.access() == frames+2);
248  CHECK (r0.access() == rawbuf+0);
249  CHECK (r1.access() == rawbuf+1);
250 
251  TestFrame defaultFrame;
252  CHECK (defaultFrame == f0.access());
253  CHECK (defaultFrame == f1.access());
254  CHECK (defaultFrame == f2.access());
255 
256  // at that point, we'd return BuffHandles to the client
257  HashVal handle_f0(f0);
258  HashVal handle_f1(f1);
259  HashVal handle_f2(f2);
260  HashVal handle_r0(r0);
261  HashVal handle_r1(r1);
262 
263  // client uses the buffers---------------------(Start)
264  accessAs<TestFrame> (f0) = testData(1);
265  accessAs<TestFrame> (f1) = testData(2);
266  accessAs<TestFrame> (f2) = testData(3);
267 
268  CHECK (testData(1) == frames[0]);
269  CHECK (testData(2) == frames[1]);
270  CHECK (testData(3) == frames[2]);
271 
272  CHECK (TestFrame::isAlive (f0.access()));
273  CHECK (TestFrame::isAlive (f1.access()));
274  CHECK (TestFrame::isAlive (f2.access()));
275 
276  strncpy (& accessAs<char> (r0), randStr(SIZE_B - 1).c_str(), SIZE_B);
277  strncpy (& accessAs<char> (r1), randStr(SIZE_B - 1).c_str(), SIZE_B);
278 
279  // client might trigger some state transitions
280  f0.mark(EMITTED);
281  f1.mark(EMITTED);
282  f1.mark(BLOCKED);
283  // client uses the buffers---------------------(End)
284 
285 
286  f0.mark(FREE); // note: implicitly invoking the embedded dtor
287  f1.mark(FREE);
288  f2.mark(FREE);
289  r0.mark(FREE);
290  r1.mark(FREE);
291 
292 
293  meta_->release(handle_f0);
294  meta_->release(handle_f1);
295  meta_->release(handle_f2);
296  meta_->release(handle_r0);
297  meta_->release(handle_r1);
298 
299  CHECK (TestFrame::isDead (&frames[0])); // was destroyed implicitly
300  CHECK (TestFrame::isDead (&frames[1]));
301  CHECK (TestFrame::isDead (&frames[2]));
302 
303  // manual cleanup of test allocations
304  delete[] frames;
305  free(storage);
306 
307  CHECK (!meta_->isLocked(handle_f0));
308  CHECK (!meta_->isLocked(handle_f1));
309  CHECK (!meta_->isLocked(handle_f2));
310  CHECK (!meta_->isLocked(handle_r0));
311  CHECK (!meta_->isLocked(handle_r1));
312  }
313 
314 
315  void
316  verifyStateMachine()
317  {
318  // start with building a type key....
319  metadata::Key key = meta_->key(SIZE_A);
320  CHECK (NIL == meta_->get(key).state());
321  CHECK (meta_->get(key).isTypeKey());
322  CHECK (!meta_->isLocked(key));
323 
324  VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(LOCKED) );
325  VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(EMITTED));
326  VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(BLOCKED));
327  VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(FREE) );
328  VERIFY_ERROR (LIFECYCLE, meta_->get(key).mark(NIL) );
329 
330  // now build a concrete buffer entry
331  metadata::Entry& entry = meta_->markLocked(key, SOME_POINTER);
332  CHECK (LOCKED == entry.state());
333  CHECK (!entry.isTypeKey());
334 
335  CHECK (SOME_POINTER == entry.access());
336 
337  VERIFY_ERROR (FATAL, entry.mark(LOCKED) ); // invalid state transition
338  VERIFY_ERROR (FATAL, entry.mark(NIL) );
339 
340  entry.mark (EMITTED); // valid transition
341  CHECK (EMITTED == entry.state());
342  CHECK (entry.isLocked());
343 
344  VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
345  VERIFY_ERROR (FATAL, entry.mark(EMITTED));
346  VERIFY_ERROR (FATAL, entry.mark(NIL) );
347  CHECK (EMITTED == entry.state());
348 
349  entry.mark (FREE);
350  CHECK (FREE == entry.state());
351  CHECK (!entry.isLocked());
352  CHECK (!entry.isTypeKey());
353 
354  VERIFY_ERROR (LIFECYCLE, entry.access() );
355  VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
356  VERIFY_ERROR (FATAL, entry.mark(EMITTED));
357  VERIFY_ERROR (FATAL, entry.mark(BLOCKED));
358  VERIFY_ERROR (FATAL, entry.mark(FREE) );
359  VERIFY_ERROR (FATAL, entry.mark(NIL) );
360 
361  // re-use buffer slot, start new lifecycle
362  void* OTHER_LOCATION = this;
363  entry.lock (OTHER_LOCATION);
364  CHECK (LOCKED == entry.state());
365  CHECK (entry.isLocked());
366 
367  VERIFY_ERROR (LIFECYCLE, entry.lock(SOME_POINTER));
368 
369  entry.mark (BLOCKED); // go directly to the blocked state
370  CHECK (BLOCKED == entry.state());
371  VERIFY_ERROR (FATAL, entry.mark(LOCKED) );
372  VERIFY_ERROR (FATAL, entry.mark(EMITTED) );
373  VERIFY_ERROR (FATAL, entry.mark(BLOCKED) );
374  VERIFY_ERROR (FATAL, entry.mark(NIL) );
375 
376  CHECK (OTHER_LOCATION == entry.access());
377 
378  entry.mark (FREE);
379  CHECK (!entry.isLocked());
380  VERIFY_ERROR (LIFECYCLE, entry.access() );
381 
382  meta_->lock(key, SOME_POINTER);
383  CHECK (entry.isLocked());
384 
385  entry.mark (EMITTED);
386  entry.mark (BLOCKED);
387  CHECK (BLOCKED == entry.state());
388  CHECK (SOME_POINTER == entry.access());
389 
390  // can't discard metadata, need to free first
391  VERIFY_ERROR (LIFECYCLE, meta_->release(entry) );
392  CHECK (meta_->isKnown(entry));
393  CHECK (entry.isLocked());
394 
395  entry.mark (FREE);
396  meta_->release(entry);
397  CHECK (!meta_->isKnown(entry));
398  CHECK ( meta_->isKnown(key));
399  }
400  };
401 
402 
404  LAUNCHER (BufferMetadata_test, "unit player");
405 
406 
407 
408 }}} // namespace steam::engine::test
allocated buffer, no longer in use
Mock data frame for simulated rendering.
Definition: testframe.hpp:60
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:49
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
allocated buffer blocked by protocol failure
TestFrame & testData(uint seqNr)
Helper to access a specific frame of test data at a fixed memory location.
Definition: testframe.cpp:155
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:69
an opaque ID to be used by the BufferProvider implementation.
Metadata for managing and accessing buffers.
Description of a Buffer-"type".
Simple 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:56
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.
A complete metadata Entry, based on a Key.
bool isSameObject(A const &a, B const &b)
compare plain object identity, bypassing any custom comparison operators.
Definition: util.hpp:372