Lumiera  0.pre.03
»edit your freedom«
command-instance-manager-test.cpp
Go to the documentation of this file.
1 /*
2  CommandInstanceManager(Test) - verify helper for setup of actual command definitions
3 
4  Copyright (C)
5  2017, 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/test/run.hpp"
22 #include "lib/format-string.hpp"
23 #include "lib/format-cout.hpp"
24 #include "lib/iter-stack.hpp"
25 #include "lib/util.hpp"
26 
27 #include <algorithm>
28 #include <utility>
29 #include <string>
30 #include <deque>
31 
32 
33 namespace steam {
34 namespace control {
35 namespace test {
36 
37  using lib::Literal;
38  using std::string;
39  using util::_Fmt;
40  using std::move;
41 
42  using LERR_(LIFECYCLE);
43  using LERR_(INVALID_COMMAND);
44  using LERR_(DUPLICATE_COMMAND);
45  using LERR_(UNBOUND_ARGUMENTS);
46 
47 
48  namespace { // Test fixture....
49 
50  const Symbol COMMAND_PROTOTYPE = test_Dummy_command1;
51  const string INVOCATION_ID = "CommandInstanceManager_test";
52 
53  class Fixture
54  : public CommandDispatch
55  {
56  std::deque<Command> queue_;
57 
58 
59  /* == Interface: CommandDispatch == */
60 
61  void
62  clear() override
63  {
64  queue_.clear();
65  }
66 
67  void
68  enqueue (Command&& cmd) override
69  {
70  queue_.emplace_front (move (cmd));
71  }
72 
73  public:
74  bool
75  contains (Command const& ref)
76  {
77  return queue_.end()!= std::find_if (queue_.begin()
78  ,queue_.end()
79  ,[=](Command const& elm)
80  {
81  return elm == ref;
82  });
83  }
84 
85  void
86  invokeAll()
87  {
88  for (Command& cmd : queue_)
89  cmd();
90  clear();
91  }
92  };
93  }
94 
95 
96 
97  /***********************************************************************/
105  class CommandInstanceManager_test : public Test
106  {
107 
108  virtual void
109  run (Arg)
110  {
111  seedRand();
112  verify_simpleUsage();
113  verify_extendedUsage();
114  verify_instanceIdentity();
115  verify_duplicates();
116  verify_lifecycle();
117  verify_fallback();
118  }
119 
120 
131  void
133  {
134  Fixture fixture;
135  CommandInstanceManager iManager{fixture};
136  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
137 
138  int r1{rani(1000)}, r2{rani(2000)};
139  command1::check_ = 0; // commands will add to this on invocation
140 
141  iManager.bindAndDispatch (COMMAND_PROTOTYPE, Rec{r1});
142  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
143 
144  Command com{COMMAND_PROTOTYPE};
145  com.bind(r2);
146  CHECK (com.canExec());
147 
148  iManager.dispatch (COMMAND_PROTOTYPE);
149  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
150 
151  // an anonymous clone instance was dispatched,
152  // thus re-binding the arguments won't interfere with execution
153  com.bind(-1);
154 
155  CHECK (command1::check_ == 0); // nothing invoked yet
156  fixture.invokeAll();
157  CHECK (command1::check_ == r1 + r2); // both instances were invoked with their specific arguments
158 
159  // clean-up: we have bound arguments on the global prototype
160  com.unbind();
161  }
162 
163 
165  void
167  {
168  Fixture fixture;
169  CommandInstanceManager iManager{fixture};
170  Symbol instanceID = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
171  CHECK (iManager.contains (instanceID));
172 
173  Command cmd = iManager.getInstance (instanceID);
174  CHECK (cmd);
175  CHECK (not cmd.canExec());
176 
177  cmd.bind(42);
178  CHECK (cmd.canExec());
179 
180  iManager.dispatch (instanceID);
181  CHECK (fixture.contains (cmd));
182  CHECK (not iManager.contains (instanceID));
183  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
184 
185  command1::check_ = 0;
186  fixture.invokeAll();
187  CHECK (command1::check_ == 42); // the dispatched instance was executed
188  }
189 
190 
191 
209  void
211  {
212  Fixture fixture;
213  CommandInstanceManager iManager{fixture};
214  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
215  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
216 
217  Command c11 = iManager.getInstance (i1);
218  Command c12 = iManager.getInstance (i1);
219  CHECK (c11 == c12);
220  CHECK (c11.isValid());
221  CHECK (not c11.canExec());
222 
223  int r1{rani(100)}, r2{rani(200)}, r3{rani(300)};
224  command1::check_ = 0; // commands will add to this on invocation
225 
226  c11.bind (r1);
227  CHECK (c12.canExec());
228  CHECK (c11.canExec());
229 
230  Command c2 = iManager.getInstance (i2);
231  CHECK (c2 != c11);
232  CHECK (c2 != c12);
233  c2.bind (r2);
234 
235  CHECK (iManager.contains (i1));
236  CHECK (iManager.contains (i2));
237  CHECK (not fixture.contains (c11));
238  CHECK (not fixture.contains (c12));
239  CHECK (not fixture.contains (c2));
240 
241  iManager.dispatch (i1);
242  CHECK (not iManager.contains (i1));
243  CHECK ( iManager.contains (i2));
244  CHECK ( fixture.contains (c11));
245  CHECK ( fixture.contains (c12));
246  CHECK (not fixture.contains (c2));
247 
248  CHECK (command1::check_ == 0);
249 
250  Symbol i11 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
251  CHECK (i11 == i1);
252  CHECK ((const char*)i11 == (const char*) i1);
253 
254  // but the instances themselves are disjoint
255  Command c13 = iManager.getInstance (i1);
256  CHECK (c13 != c11);
257  CHECK (c13 != c12);
258  CHECK (c11.canExec());
259  CHECK (not c13.canExec());
260 
261  c13.bind (r3);
262  CHECK (c13.canExec());
263 
264  CHECK (command1::check_ == 0);
265  c12();
266  CHECK (command1::check_ == 0+r1);
267 
268  // even a command still in instance manager can be invoked
269  c2();
270  CHECK (command1::check_ == 0+r1+r2);
271 
272  CHECK ( iManager.contains (i1));
273  CHECK ( iManager.contains (i2));
274  CHECK ( fixture.contains (c11));
275  CHECK ( fixture.contains (c12));
276  CHECK (not fixture.contains (c2));
277 
278  iManager.dispatch (i2);
279  iManager.dispatch (i11);
280  CHECK (not iManager.contains (i1));
281  CHECK (not iManager.contains (i2));
282  CHECK ( fixture.contains (c11));
283  CHECK ( fixture.contains (c12));
284  CHECK ( fixture.contains (c13));
285  CHECK ( fixture.contains (c2));
286 
287  // if we continue to hold onto an instance,
288  // we can do anything with it. Like re-binding arguments.
289  c2.bind (47);
290  c2();
291  c13();
292  c13();
293  CHECK (command1::check_ == 0+r1+r2+47+r3+r3);
294 
295  c11.undo();
296  CHECK (command1::check_ == 0);
297  c2.undo();
298  CHECK (command1::check_ == 0+r1+r2); // undo() restores the value captured before second invocation of c2()
299  c12.undo(); // c11 and c12 refer to the same instance, which was invoked first
300  CHECK (command1::check_ == 0);
301  }
302 
303 
310  void
312  {
313  Fixture fixture;
314  CommandInstanceManager iManager{fixture};
315  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
316  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
317 
318  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i1"));
319  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
320 
321  iManager.bindAndDispatch (i1, {-1}); // bind and dispatch i1, thus i1 is ready for new cycle
322 
323  iManager.newInstance (COMMAND_PROTOTYPE, "i1"); // open new cycle for i1
324  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
325 
326  CHECK (iManager.getInstance (i1));
327  CHECK (iManager.getInstance (i2));
328  }
329 
330 
338  void
340  {
341  Fixture fixture;
342  CommandInstanceManager iManager{fixture};
343 
344  // a manually constructed ID is unknown of course
345  Symbol instanceID{COMMAND_PROTOTYPE, INVOCATION_ID};
346  VERIFY_ERROR (INVALID_COMMAND, iManager.getInstance (instanceID));
347  VERIFY_ERROR (INVALID_COMMAND, iManager.dispatch (instanceID));
348 
349  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
350  CHECK (i2 == instanceID);
351  CHECK (iManager.getInstance (instanceID));
352 
353 
354  Command cmd = iManager.getInstance (instanceID);
355  CHECK (cmd);
356  CHECK (not cmd.canExec());
357 
358  VERIFY_ERROR (UNBOUND_ARGUMENTS, iManager.dispatch (instanceID));
359  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
360  CHECK (iManager.contains (instanceID)); // errors have not messed up anything
361 
362  cmd.bind(23);
363  CHECK (cmd.canExec());
364  iManager.dispatch (instanceID);
365 
366  CHECK (not iManager.contains (instanceID));
367  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
368  VERIFY_ERROR (LIFECYCLE, iManager.dispatch (instanceID));
369  CHECK (instanceID == iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
370  }
371 
372 
375  void
377  {
378  Fixture fixture;
379  CommandInstanceManager iManager{fixture};
380 
381  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
382  Command cmd = iManager.getInstance (COMMAND_PROTOTYPE);
383 
384  CHECK (cmd.isValid());
385  CHECK (not cmd.isAnonymous());
386  CHECK (cmd == Command::get(COMMAND_PROTOTYPE));
387  CHECK (cmd == Command{COMMAND_PROTOTYPE});
388 
389  cmd.bind(-12);
390  CHECK (cmd.canExec());
391  CHECK (not fixture.contains(cmd));
392 
393  iManager.dispatch (COMMAND_PROTOTYPE);
394  CHECK (fixture.contains(cmd)); // an equivalent clone was enqueued
395 
396  command1::check_ = 0;
397  fixture.invokeAll();
398  CHECK (command1::check_ == -12); // the clone copy was executed
399 
400  // clean-up: we have bound arguments on the global prototype
401  cmd.unbind();
402  }
403  };
404 
405 
407  LAUNCHER (CommandInstanceManager_test, "unit controller");
408 
409 
410 }}} // namespace steam::control::test
Automatically use custom string conversion in C++ stream output.
Definition: run.hpp:40
Conveniently iterable stack and queue containers.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
Front-end for printf-style string template interpolation.
Maintain a current command instance for parametrisation.
Service to support forming and invocation of command instances for use by the UI. ...
CommandSetup test_Dummy_command1
test dummy command to add the argument to a global variable
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.
static Command get(Symbol cmdID)
Access existing command for use.
Definition: command.cpp:120
Steam-Layer implementation namespace root.
A front-end for using printf-style formatting.
Token or Atom with distinct identity.
Definition: symbol.hpp:117
Simplistic test class runner.
Interface of a service to perform Commands on the session.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Some dummy command functions used for building unit test cases.
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:115
RET bind()
Accept dummy binding (0 Arg)