Lumiera  0.pre.03
»edit your freedom«
multifact-test.cpp
Go to the documentation of this file.
1 /*
2  MultiFact(Test) - cover the configurable object-family creating factory
3 
4  Copyright (C) Lumiera.org
5  2014, 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/test/test-helper.hpp"
30 #include "lib/multifact.hpp"
31 #include "lib/util.hpp"
32 
33 #include <boost/lexical_cast.hpp>
34 #include <functional>
35 #include <string>
36 
37 
38 
39 namespace lib {
40 namespace test{
41 
42  using boost::lexical_cast;
43  using util::isSameObject;
44  using util::isnil;
45 
46  using std::function;
47  using std::string;
48  using std::bind;
49 
50  using lumiera::error::LUMIERA_ERROR_INVALID;
51 
52 
53  namespace { // hierarchy of test dummy objects
54 
55  struct Interface
56  {
57  virtual ~Interface() {};
58  virtual operator string () =0;
59  };
60 
61 
62 
63  enum theID
64  { ONE = 1
65  , TWO
66  , THR
67  , FOU
68  };
69 
70 
71 
72  template<theID ii>
74  : public Interface
75  {
76  string instanceID_;
77 
78  operator string()
79  {
80  return instanceID_ + lexical_cast<string> (ii);
81  }
82 
83  public:
84  Implementation(string id = "Impl-")
85  : instanceID_(id)
86  { }
87  };
88 
89 
90 
91  template<typename X>
92  string
93  buildSome (X rawVal)
94  {
95  return lexical_cast<string> (rawVal);
96  }
97 
98  string
99  buildOne()
100  {
101  return buildSome(ONE);
102  }
103  }
104 
105 
106 
107 
108 
109  /******************************************************************************/
121  class MultiFact_test : public Test
122  {
123  void
124  run (Arg)
125  {
126  produce_simple_values();
127  produce_smart_pointers();
128  pass_additional_arguments();
129  fed_a_custom_finishing_functor();
130  }
131 
132  string
133  callMe (string val)
134  {
135  ++invocations_;
136  return val;
137  }
138  uint invocations_ = 0;
139 
140 
141  void
142  produce_simple_values()
143  {
145 
147 
148  // the first "production line" is wired to a free function
149  theFact.defineProduction (ONE, buildOne);
150 
151  // second "production line" uses a explicit partial closure
152  theFact.defineProduction (TWO, bind (buildSome<theID>, TWO));
153 
154  // for the third "production line" we set up a function object
155  auto memberFunction = bind (&MultiFact_test::callMe, this, "lalü");
156  theFact.defineProduction (THR, memberFunction);
157 
158  // and the fourth "production line" uses a lambda, closed with a local reference
159  string backdoor("backdoor");
160  theFact.defineProduction (FOU, [&] {
161  return backdoor;
162  });
163 
164  CHECK (!isnil (theFact));
165  CHECK (theFact(ONE) == "1");
166  CHECK (theFact(TWO) == "2");
167 
168  CHECK (theFact(THR) == "lalü");
169  CHECK (invocations_ == 1);
170 
171  CHECK (theFact(FOU) == "backdoor");
172  backdoor = "I am " + backdoor.substr(0,4);
173  CHECK (theFact(FOU) == "I am back");
174 
175 
176  TestFactory anotherFact;
177  CHECK (isnil (anotherFact));
178  VERIFY_ERROR (INVALID, anotherFact(ONE) );
179 
180  anotherFact.defineProduction (ONE, memberFunction);
181  CHECK (anotherFact(ONE) == "lalü");
182  CHECK (invocations_ == 2);
183 
184  CHECK (theFact(THR) == "lalü");
185  CHECK (invocations_ == 3);
186 
187 
188  CHECK ( theFact.contains (FOU));
189  CHECK (!anotherFact.contains (FOU));
190 
191  anotherFact = theFact;
192  CHECK (anotherFact.contains (FOU));
193  CHECK (!isSameObject(theFact, anotherFact));
194 
195  CHECK (anotherFact(ONE) == "1");
196  CHECK (anotherFact(TWO) == "2");
197  CHECK (anotherFact(THR) == "lalü");
198  CHECK (anotherFact(FOU) == "I am back");
199  CHECK (invocations_ == 4);
200  }
201 
202 
203  void
204  produce_smart_pointers()
205  {
207  using PIfa = shared_ptr<Interface>;
208 
210 
211  // set up the "production lines" by lambda
212  theFact.defineProduction (ONE, [] { return new Implementation<ONE>; });
213  theFact.defineProduction (TWO, [] { return new Implementation<TWO>; });
214  theFact.defineProduction (THR, [] { return new Implementation<THR>; });
215  theFact.defineProduction (FOU, [] { return new Implementation<FOU>; });
216  CHECK (!isnil (theFact));
217 
218  PIfa p1 = theFact(ONE);
219  PIfa p2 = theFact(TWO);
220  PIfa p3 = theFact(THR);
221  PIfa p4 = theFact(FOU);
222 
223  PIfa p11 = theFact(ONE);
224 
225  CHECK ("Impl-1" == string(*p1));
226  CHECK ("Impl-2" == string(*p2));
227  CHECK ("Impl-3" == string(*p3));
228  CHECK ("Impl-4" == string(*p4));
229 
230  CHECK ("Impl-1" == string(*p11));
231  CHECK (!isSameObject(*p1, *p11));
232 
233  PIfa p12(p11);
234  CHECK (isSameObject(*p11, *p12));
235  CHECK ("Impl-1" == string(*p12));
236  CHECK (1 == p1.use_count());
237  CHECK (2 == p11.use_count());
238  CHECK (2 == p12.use_count());
239  }
240 
241 
242  void
243  pass_additional_arguments()
244  {
246 
248 
249  // set up the "production lines"
250  theFact.defineProduction (ONE, [](string ) { return new Implementation<ONE>; });
251  theFact.defineProduction (TWO, [](string ) { return new Implementation<TWO>("X"); });
252  theFact.defineProduction (THR, [](string id) { return new Implementation<THR>(id); });
253  theFact.defineProduction (FOU, [](string id) { return new Implementation<FOU>("Z"+id);});
254 
255  Interface *p1 = theFact(ONE, "irrelevant"),
256  *p2 = theFact(TWO, "ignored"),
257  *p3 = theFact(THR, "idiocy"),
258  *p4 = theFact(FOU, "omg"),
259  *p5 = theFact(FOU, "z");
260 
261  // does not compile...
262  // theFact(ONE);
263  // theFact(ONE, "foo", bar);
264 
265  CHECK ("Impl-1" == string(*p1));
266  CHECK ("X2" == string(*p2));
267  CHECK ("idiocy3"== string(*p3));
268  CHECK ("Zomg4" == string(*p4));
269  CHECK ("Zz4" == string(*p5));
270 
271  CHECK (!isSameObject(*p4, *p5));
272  CHECK (INSTANCEOF(Implementation<ONE>, p1));
273  CHECK (INSTANCEOF(Implementation<TWO>, p2));
274  CHECK (INSTANCEOF(Implementation<THR>, p3));
275  CHECK (INSTANCEOF(Implementation<FOU>, p4));
276  CHECK (INSTANCEOF(Implementation<FOU>, p5));
277 
278  delete p1;
279  delete p2;
280  delete p3;
281  delete p4;
282  delete p5;
283  }
284 
285 
286  void
287  fed_a_custom_finishing_functor()
288  {
290 
292 
293  // Setup(1): each "production line" does a distinct calculation
294  theFact.defineProduction (ONE, [](int par) { return par; });
295  theFact.defineProduction (TWO, [](int par) { return 2 * par; });
296  theFact.defineProduction (THR, [](int par) { return par*par; });
297  theFact.defineProduction (FOU, [](int par) { return 1 << par;});
298 
299  // Setup(2): and a common "wrapper functor" finishes
300  // the output of the chosen "production line"
301  theFact.defineFinalWrapper([](int raw) { return raw + 1; });
302 
303  CHECK (long(1 + 1) == theFact(ONE, 1));
304  CHECK (long(1 + 2) == theFact(ONE, 2));
305  CHECK (long(1 + 3) == theFact(ONE, 3));
306 
307  CHECK (long(1 + 2) == theFact(TWO, 1));
308  CHECK (long(1 + 4) == theFact(TWO, 2));
309  CHECK (long(1 + 6) == theFact(TWO, 3));
310 
311  CHECK (long(1 + 1) == theFact(THR, 1));
312  CHECK (long(1 + 4) == theFact(THR, 2));
313  CHECK (long(1 + 9) == theFact(THR, 3));
314 
315  CHECK (long(1 + 2) == theFact(FOU, 1));
316  CHECK (long(1 + 4) == theFact(FOU, 2));
317  CHECK (long(1 + 8) == theFact(FOU, 3));
318  }
319  };
320 
321 
323  LAUNCHER (MultiFact_test, "unit common");
324 
325 
326 
327 }} // namespace lib::test
factory::MultiFact< Num(int), prodID, factory::BuildRefcountPtr > TestFactory
the factory instantiation used for this test
Definition: run.hpp:49
Framework for building a configurable factory, to generate families of related objects.
#define INSTANCEOF(CLASS, EXPR)
shortcut for subclass test, intended for assertions only.
Definition: util.hpp:492
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Implementation namespace for support and library code.
Factory for creating a family of objects by ID.
Definition: multifact.hpp:262
Target object to be instantiated as Singleton Allocates a variable amount of additional heap memory a...
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.
void defineProduction(ID id, FUNC &&fun)
to set up a production line, associated with a specific ID
Definition: multifact.hpp:320
bool isSameObject(A const &a, B const &b)
compare plain object identity, bypassing any custom comparison operators.
Definition: util.hpp:372