Lumiera  0.pre.03
»edit your freedom«
advice-index-test.cpp
Go to the documentation of this file.
1 /*
2  AdviceIndex(Test) - cover the index datastructure used to implement Advice dispatch
3 
4  Copyright (C) Lumiera.org
5  2010, 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/test/run.hpp"
29 #include "lib/hash-value.h"
30 #include "common/advice/index.hpp"
31 
32 #include <vector>
33 
34 using lib::Literal;
35 
36 
37 
38 namespace lumiera {
39 namespace advice {
40 namespace test {
41 
42  namespace { // test support definitions
43 
52  struct TestPOA
53  {
54  TestPOA* solution_;
55  Binding::Matcher pattern_;
56 
57  explicit
58  TestPOA(Literal spec="missing")
59  : solution_(0)
60  , pattern_(Binding(spec).buildMatcher())
61  { }
62 
63  bool
64  matches (Literal refSpec) const
65  {
66  return pattern_.matches (Binding(refSpec));
67  }
68 
69  void
70  changeBinding (Literal newSpec)
71  {
72  pattern_ = Binding(newSpec).buildMatcher();
73  }
74 
75 
76  /* == Adapter interface for use within the Index == */
77 
78  void setSolution (TestPOA* p) { solution_ = p; }
79  const TestPOA* getSolution () const { return solution_;}
80  Binding::Matcher getMatcher () const { return pattern_; }
81 
82  friend HashVal
83  hash_value (TestPOA const& entry)
84  {
85  return hash_value (entry.pattern_);
86  }
87  };
88 
89 
90 
91 
92  const uint MAX_TEST_ENTRIES = 10;
93 
94  std::vector<TestPOA> testEntries(MAX_TEST_ENTRIES);
95 
96 
97 
99  inline TestPOA&
100  _entry (uint id, Literal spec)
101  {
102  REQUIRE (id < testEntries.size());
103 
104  if (!testEntries[id].matches(spec))
105  testEntries[id] = TestPOA(spec);
106 
107  return testEntries[id];
108  }
109 
111  inline bool
112  _hasSolution (uint req, uint prov)
113  {
114  REQUIRE (req < testEntries.size());
115  REQUIRE (prov < testEntries.size());
116 
117  return testEntries[req].solution_ == & testEntries[prov];
118  }
119 
121  inline bool
122  _hasDefault (uint req)
123  {
124  REQUIRE (req < testEntries.size());
125  return NULL == testEntries[req].solution_;
126  }
127  }
128 
129  typedef Index<TestPOA> Index;
130 
131 
132 
133  /**************************************************************************************/
148  class AdviceIndex_test : public Test
149  {
150 
151  virtual void
152  run (Arg)
153  {
154  Index idx;
155  buildIndex (idx);
156 
157  addRequest (idx);
158  addProvision (idx);
159  removeRequest (idx);
160  retractProvision (idx);
161  modifyRequest (idx);
162  modifyProvision (idx);
163 
164  clearIndex (idx);
165  }
166 
167 
168  void
169  buildIndex (Index& idx)
170  {
171  CHECK (idx.isValid());
172  CHECK (0 == idx.size());
173 
174  idx.addRequest (_entry (1,"cat"));
175  idx.addRequest (_entry (2,"cat"));
176  idx.addRequest (_entry (3,"dog"));
177  CHECK (3 == idx.size());
178  CHECK (3 == idx.request_count());
179  CHECK (0 == idx.provision_count());
180 
181  idx.addProvision (_entry (4,"dog"));
182  CHECK (4 == idx.size());
183  CHECK (3 == idx.request_count());
184  CHECK (1 == idx.provision_count());
185 
186  CHECK (_hasDefault (1));
187  CHECK (_hasDefault (2));
188  CHECK (_hasSolution (3,4));
189  CHECK (idx.isValid());
190  }
191 
192 
193  void
194  addRequest (Index& idx)
195  {
196  CHECK (idx.isValid());
197  uint req_cnt = idx.request_count();
198 
199  idx.addRequest (_entry (5,"dog"));
200  idx.addRequest (_entry (6,"cat"));
201 
202  CHECK (idx.hasRequest (_entry (5,"dog")));
203  CHECK (idx.hasRequest (_entry (6,"cat")));
204 
205  CHECK (_hasDefault (6));
206  CHECK (_hasSolution (5,4));
207  CHECK (idx.isValid());
208  CHECK (2 + req_cnt == idx.request_count());
209  }
210 
211 
212  void
213  addProvision (Index& idx)
214  {
215  CHECK (idx.isValid());
216  uint r_cnt = idx.request_count();
217  uint p_cnt = idx.provision_count();
218 
219  CHECK (_hasDefault (1));
220  CHECK (_hasDefault (2));
221  CHECK (_hasDefault (6));
222  CHECK (_hasSolution (3,4));
223  CHECK (_hasSolution (5,4));
224 
225  idx.addProvision (_entry (7,"cat"));
226  CHECK (idx.hasProvision (_entry (7,"cat")));
227 
228  CHECK (_hasSolution (1,7)); // all cats got the cat solution
229  CHECK (_hasSolution (2,7));
230  CHECK (_hasSolution (6,7));
231  CHECK (_hasSolution (3,4)); // dogs unaltered
232  CHECK (_hasSolution (5,4));
233  CHECK (idx.isValid());
234  CHECK (1 + p_cnt == idx.provision_count());
235  CHECK (0 + r_cnt == idx.request_count());
236 
237  idx.addProvision (_entry (8,"dog"));
238 
239  CHECK (_hasSolution (1,7)); // cats remain unaffected
240  CHECK (_hasSolution (2,7));
241  CHECK (_hasSolution (6,7));
242  CHECK (_hasSolution (3,8)); // all dogs got the new solution
243  CHECK (_hasSolution (5,8));
244  CHECK (idx.isValid());
245  CHECK (2 + p_cnt == idx.provision_count());
246  CHECK (0 + r_cnt == idx.request_count());
247  }
248 
249 
250  void
251  removeRequest (Index& idx)
252  {
253  CHECK (idx.isValid());
254  uint r_cnt = idx.request_count();
255  uint p_cnt = idx.provision_count();
256  CHECK (_hasSolution (1,7));
257  CHECK (_hasSolution (2,7));
258  CHECK (_hasSolution (6,7));
259  CHECK (_hasSolution (3,8));
260  CHECK (_hasSolution (5,8));
261 
262  CHECK ( idx.hasRequest (_entry (2,"cat")));
263 
264  idx.removeRequest (_entry (2,"cat"));
265 
266  CHECK (!idx.hasRequest (_entry (2,"cat")));
267  CHECK (p_cnt == idx.provision_count());
268  CHECK (r_cnt-1 == idx.request_count());
269 
270  CHECK (_hasSolution (1,7)); // no effect on the other requests
271  CHECK (_hasSolution (6,7));
272  CHECK (_hasSolution (3,8));
273  CHECK (_hasSolution (5,8));
274 
275  idx.removeRequest (_entry (2,"cat")); // idempotent
276  CHECK (!idx.hasRequest (_entry (2,"cat")));
277  CHECK (p_cnt == idx.provision_count());
278  CHECK (r_cnt-1 == idx.request_count());
279  CHECK (idx.isValid());
280  }
281 
282 
283  void
284  retractProvision (Index& idx)
285  {
286  CHECK (idx.isValid());
287  uint r_cnt = idx.request_count();
288  uint p_cnt = idx.provision_count();
289  CHECK (_hasSolution (1,7));
290  CHECK (_hasSolution (6,7));
291  CHECK (_hasSolution (3,8));
292  CHECK (_hasSolution (5,8));
293 
294  CHECK ( idx.hasProvision (_entry (4,"dog")));
295 
296  idx.removeProvision (_entry (4,"dog"));
297 
298  CHECK (!idx.hasProvision (_entry (4,"dog")));
299  CHECK (p_cnt-1 == idx.provision_count());
300  CHECK (r_cnt == idx.request_count());
301 
302  CHECK (_hasSolution (1,7)); // no effect on the solutions, because of the more recent dog solution 8
303  CHECK (_hasSolution (6,7));
304  CHECK (_hasSolution (3,8));
305  CHECK (_hasSolution (5,8));
306 
307  CHECK ( idx.hasProvision (_entry (8,"dog")));
308 
309  idx.removeProvision (_entry (8,"dog"));
310 
311  CHECK (!idx.hasProvision (_entry (8,"dog")));
312  CHECK (p_cnt-2 == idx.provision_count());
313  CHECK (r_cnt == idx.request_count());
314 
315  CHECK (_hasSolution (1,7)); // no effect on the cat solutions
316  CHECK (_hasSolution (6,7));
317  CHECK (!_hasSolution (3,8));
318  CHECK (!_hasSolution (5,8));
319  CHECK (_hasDefault (3)); // but all dog requests reverted to default
320  CHECK (_hasDefault (5));
321 
322  idx.removeProvision (_entry (8,"dog")); // idempotent
323  CHECK (!idx.hasProvision (_entry (8,"dog")));
324  CHECK (p_cnt-2 == idx.provision_count());
325  CHECK (r_cnt == idx.request_count());
326  CHECK (idx.isValid());
327  }
328 
329 
330  void
331  modifyRequest (Index& idx)
332  {
333  CHECK (idx.isValid());
334  uint r_cnt = idx.request_count();
335  uint p_cnt = idx.provision_count();
336  CHECK (_hasSolution (1,7));
337  CHECK (_hasSolution (6,7));
338  CHECK (_hasDefault (3));
339  CHECK (_hasDefault (5));
340 
341  HashVal dogHash (hash_value (_entry (5,"dog")));
342 
343  CHECK ( idx.hasRequest (_entry (5,"dog")));
344  _entry (5,"dog").changeBinding("cat"); // transmogrify existing request into cat-request
345  CHECK (_hasDefault (5)); // of course this didn't change the solution
346  CHECK (!idx.hasRequest (_entry (5,"cat"))); // can't find it anymore because of changed binding
347 
348  idx.modifyRequest (dogHash, _entry (5,"cat"));
349 
350  CHECK ( idx.hasRequest (_entry (5,"cat")));
351  CHECK (p_cnt == idx.provision_count());
352  CHECK (r_cnt == idx.request_count());
353  CHECK (_hasSolution (1,7));
354  CHECK (_hasSolution (6,7));
355  CHECK (_hasDefault (3));
356  CHECK (_hasSolution (5,7)); // automatically got the current cat solution
357  }
358 
359 
360  void
361  modifyProvision (Index& idx)
362  {
363  CHECK (idx.isValid());
364  uint r_cnt = idx.request_count();
365  uint p_cnt = idx.provision_count();
366  CHECK (_hasSolution (1,7));
367  CHECK (_hasSolution (5,7));
368  CHECK (_hasSolution (6,7));
369  CHECK (_hasDefault (3));
370 
371  CHECK ( idx.hasProvision (_entry (7,"cat")));
372  CHECK (!idx.hasProvision (_entry (8,"dog")));
373 
374  idx.modifyProvision (_entry (7,"cat"), _entry (8,"dog"));
375  CHECK (!idx.hasProvision (_entry (7,"cat")));
376  CHECK ( idx.hasProvision (_entry (8,"dog")));
377  CHECK (p_cnt == idx.provision_count());
378  CHECK (r_cnt == idx.request_count());
379  CHECK (_hasDefault (1));
380  CHECK (_hasDefault (5));
381  CHECK (_hasDefault (6));
382  CHECK (_hasSolution (3,8));
383 
384  idx.addProvision (_entry (7,"cat"));
385  idx.addProvision (_entry (9,"cat"));
386  CHECK (p_cnt+2 == idx.provision_count());
387  CHECK (idx.hasProvision (_entry (7,"cat")));
388  CHECK (idx.hasProvision (_entry (9,"cat")));
389  CHECK (_hasSolution (1,9)); // all cats got the second cat solution
390  CHECK (_hasSolution (5,9));
391  CHECK (_hasSolution (6,9));
392  CHECK (_hasSolution (3,8)); // the dog is unaffected
393 
394  CHECK ( idx.hasProvision (_entry (7,"cat")));
395  CHECK (!idx.hasProvision (_entry (4,"dog")));
396 
397  idx.modifyProvision (_entry (7,"cat"), _entry (4,"dog"));
398 
399  CHECK (!idx.hasProvision (_entry (7,"cat")));
400  CHECK ( idx.hasProvision (_entry (4,"dog")));
401  CHECK (_hasSolution (1,9)); // cats unaffected, because we're changing a shadowed cat provision
402  CHECK (_hasSolution (5,9));
403  CHECK (_hasSolution (6,9));
404  CHECK (_hasSolution (3,4)); // but the dog got switched to the replaced-by-dog solution,
405  // because it was added later than the existing solution 8
406 
407  // a switch within the same cluster ("cat")
408  idx.modifyProvision (_entry (9,"cat"), _entry (7,"cat"));
409  CHECK (!idx.hasProvision (_entry (9,"cat")));
410  CHECK ( idx.hasProvision (_entry (7,"cat")));
411  CHECK ( idx.hasProvision (_entry (4,"dog")));
412  CHECK (_hasSolution (1,7)); // because cat-7 is newly added, it shadows the older cat-9
413  CHECK (_hasSolution (5,7));
414  CHECK (_hasSolution (6,7));
415  CHECK (_hasSolution (3,4)); // but dog remains dog
416 
417  CHECK (p_cnt+2 == idx.provision_count());
418  CHECK (r_cnt == idx.request_count());
419  CHECK (idx.isValid());
420  }
421 
422 
423  void
424  clearIndex (Index& idx)
425  {
426  idx.clear();
427  CHECK (idx.isValid());
428  CHECK (0 == idx.size());
429  }
430  };
431 
432 
433 
435  LAUNCHER (AdviceIndex_test, "function common");
436 
437 
438 }}} // namespace lumiera::advice::test
bool _hasSolution(uint req, uint prov)
check if the given request got the denoted solution
Conjunction of predicates to be matched against a collaboration partner for establishing an Advice co...
TestPOA & _entry(uint id, Literal spec)
convenience shortcut for writing testcases inline
Functor object for matching against another Binding.
bool _hasDefault(uint req)
check if the given request holds a default solution
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:49
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:85
Implementation datastructure for use by the Advice system.
Simple test class runner.
Test dummy record, representing either a provision or an request.
Hash value types and utilities.
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:56
Lumiera public interface.
Definition: advice.cpp:113