Lumiera  0.pre.03
»edityourfreedom«
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) Lumiera.org
5  2017, 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"
31 #include "lib/format-string.hpp"
32 #include "lib/format-cout.hpp"
33 #include "lib/iter-stack.hpp"
34 #include "lib/util.hpp"
35 
36 #include <algorithm>
37 #include <cstdlib>
38 #include <utility>
39 #include <string>
40 #include <deque>
41 
42 
43 namespace proc {
44 namespace control {
45 namespace test {
46 
47  using lib::Literal;
48  using std::string;
49  using util::_Fmt;
50  using std::move;
51  using std::rand;
52 
53  using lumiera::error::LERR_(LIFECYCLE);
54 
55 
56 
57 
58  namespace { // Test fixture....
59 
60  const Symbol COMMAND_PROTOTYPE = test_Dummy_command1;
61  const string INVOCATION_ID = "CommandInstanceManager_test";
62 
63  class Fixture
64  : public CommandDispatch
65  {
66  std::deque<Command> queue_;
67 
68 
69  /* == Interface: CommandDispatch == */
70 
71  void
72  clear() override
73  {
74  queue_.clear();
75  }
76 
77  void
78  enqueue (Command&& cmd) override
79  {
80  queue_.emplace_front (move (cmd));
81  }
82 
83  public:
84  bool
85  contains (Command const& ref)
86  {
87  return queue_.end()!= std::find_if (queue_.begin()
88  ,queue_.end()
89  ,[=](Command const& elm)
90  {
91  return elm == ref;
92  });
93  }
94 
95  void
96  invokeAll()
97  {
98  for (Command& cmd : queue_)
99  cmd();
100  clear();
101  }
102  };
103  }
104 
105 
106 
107  /***********************************************************************/
115  class CommandInstanceManager_test : public Test
116  {
117 
118  virtual void
120  {
121  verify_simpleUsage();
122  verify_extendedUsage();
123  verify_instanceIdentity();
124  verify_duplicates();
125  verify_lifecycle();
126  verify_fallback();
127  }
128 
129 
140  void
142  {
143  Fixture fixture;
144  CommandInstanceManager iManager{fixture};
145  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
146 
147  int r1{rand()%1000}, r2{rand()%2000};
148  command1::check_ = 0; // commands will add to this on invocation
149 
150  iManager.bindAndDispatch (COMMAND_PROTOTYPE, Rec{r1});
151  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
152 
153  Command com{COMMAND_PROTOTYPE};
154  com.bind(r2);
155  CHECK (com.canExec());
156 
157  iManager.dispatch (COMMAND_PROTOTYPE);
158  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
159 
160  // an anonymous clone instance was dispatched,
161  // thus re-binding the arguments won't interfere with execution
162  com.bind(-1);
163 
164  CHECK (command1::check_ == 0); // nothing invoked yet
165  fixture.invokeAll();
166  CHECK (command1::check_ == r1 + r2); // both instances were invoked with their specific arguments
167 
168  // clean-up: we have bound arguments on the global prototype
169  com.unbind();
170  }
171 
172 
174  void
176  {
177  Fixture fixture;
178  CommandInstanceManager iManager{fixture};
179  Symbol instanceID = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
180  CHECK (iManager.contains (instanceID));
181 
182  Command cmd = iManager.getInstance (instanceID);
183  CHECK (cmd);
184  CHECK (not cmd.canExec());
185 
186  cmd.bind(42);
187  CHECK (cmd.canExec());
188 
189  iManager.dispatch (instanceID);
190  CHECK (fixture.contains (cmd));
191  CHECK (not iManager.contains (instanceID));
192  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
193 
194  command1::check_ = 0;
195  fixture.invokeAll();
196  CHECK (command1::check_ == 42); // the dispatched instance was executed
197  }
198 
199 
200 
218  void
220  {
221  Fixture fixture;
222  CommandInstanceManager iManager{fixture};
223  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
224  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
225 
226  Command c11 = iManager.getInstance (i1);
227  Command c12 = iManager.getInstance (i1);
228  CHECK (c11 == c12);
229  CHECK (c11.isValid());
230  CHECK (not c11.canExec());
231 
232  int r1{rand()%100}, r2{rand()%200}, r3{rand()%300};
233  command1::check_ = 0; // commands will add to this on invocation
234 
235  c11.bind (r1);
236  CHECK (c12.canExec());
237  CHECK (c11.canExec());
238 
239  Command c2 = iManager.getInstance (i2);
240  CHECK (c2 != c11);
241  CHECK (c2 != c12);
242  c2.bind (r2);
243 
244  CHECK (iManager.contains (i1));
245  CHECK (iManager.contains (i2));
246  CHECK (not fixture.contains (c11));
247  CHECK (not fixture.contains (c12));
248  CHECK (not fixture.contains (c2));
249 
250  iManager.dispatch (i1);
251  CHECK (not iManager.contains (i1));
252  CHECK ( iManager.contains (i2));
253  CHECK ( fixture.contains (c11));
254  CHECK ( fixture.contains (c12));
255  CHECK (not fixture.contains (c2));
256 
257  CHECK (command1::check_ == 0);
258 
259  Symbol i11 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
260  CHECK (i11 == i1);
261  CHECK ((const char*)i11 == (const char*) i1);
262 
263  // but the instances themselves are disjoint
264  Command c13 = iManager.getInstance (i1);
265  CHECK (c13 != c11);
266  CHECK (c13 != c12);
267  CHECK (c11.canExec());
268  CHECK (not c13.canExec());
269 
270  c13.bind (r3);
271  CHECK (c13.canExec());
272 
273  CHECK (command1::check_ == 0);
274  c12();
275  CHECK (command1::check_ == 0+r1);
276 
277  // even a command still in instance manager can be invoked
278  c2();
279  CHECK (command1::check_ == 0+r1+r2);
280 
281  CHECK ( iManager.contains (i1));
282  CHECK ( iManager.contains (i2));
283  CHECK ( fixture.contains (c11));
284  CHECK ( fixture.contains (c12));
285  CHECK (not fixture.contains (c2));
286 
287  iManager.dispatch (i2);
288  iManager.dispatch (i11);
289  CHECK (not iManager.contains (i1));
290  CHECK (not iManager.contains (i2));
291  CHECK ( fixture.contains (c11));
292  CHECK ( fixture.contains (c12));
293  CHECK ( fixture.contains (c13));
294  CHECK ( fixture.contains (c2));
295 
296  // if we continue to hold onto an instance,
297  // we can do anything with it. Like re-binding arguments.
298  c2.bind (47);
299  c2();
300  c13();
301  c13();
302  CHECK (command1::check_ == 0+r1+r2+47+r3+r3);
303 
304  c11.undo();
305  CHECK (command1::check_ == 0);
306  c2.undo();
307  CHECK (command1::check_ == 0+r1+r2); // undo() restores the value captured before second invocation of c2()
308  c12.undo(); // c11 and c12 refer to the same instance, which was invoked first
309  CHECK (command1::check_ == 0);
310  }
311 
312 
319  void
321  {
322  Fixture fixture;
323  CommandInstanceManager iManager{fixture};
324  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
325  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
326 
327  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i1"));
328  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
329 
330  iManager.bindAndDispatch (i1, {-1}); // bind and dispatch i1, thus i1 is ready for new cycle
331 
332  iManager.newInstance (COMMAND_PROTOTYPE, "i1"); // open new cycle for i1
333  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
334 
335  CHECK (iManager.getInstance (i1));
336  CHECK (iManager.getInstance (i2));
337  }
338 
339 
347  void
349  {
350  Fixture fixture;
351  CommandInstanceManager iManager{fixture};
352 
353  // a manually constructed ID is unknown of course
354  Symbol instanceID{COMMAND_PROTOTYPE, INVOCATION_ID};
355  VERIFY_ERROR (INVALID_COMMAND, iManager.getInstance (instanceID));
356  VERIFY_ERROR (INVALID_COMMAND, iManager.dispatch (instanceID));
357 
358  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
359  CHECK (i2 == instanceID);
360  CHECK (iManager.getInstance (instanceID));
361 
362 
363  Command cmd = iManager.getInstance (instanceID);
364  CHECK (cmd);
365  CHECK (not cmd.canExec());
366 
367  VERIFY_ERROR (UNBOUND_ARGUMENTS, iManager.dispatch (instanceID));
368  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
369  CHECK (iManager.contains (instanceID)); // errors have not messed up anything
370 
371  cmd.bind(23);
372  CHECK (cmd.canExec());
373  iManager.dispatch (instanceID);
374 
375  CHECK (not iManager.contains (instanceID));
376  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
377  VERIFY_ERROR (LIFECYCLE, iManager.dispatch (instanceID));
378  CHECK (instanceID == iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
379  }
380 
381 
384  void
386  {
387  Fixture fixture;
388  CommandInstanceManager iManager{fixture};
389 
390  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
391  Command cmd = iManager.getInstance (COMMAND_PROTOTYPE);
392 
393  CHECK (cmd.isValid());
394  CHECK (not cmd.isAnonymous());
395  CHECK (cmd == Command::get(COMMAND_PROTOTYPE));
396  CHECK (cmd == Command{COMMAND_PROTOTYPE});
397 
398  cmd.bind(-12);
399  CHECK (cmd.canExec());
400  CHECK (not fixture.contains(cmd));
401 
402  iManager.dispatch (COMMAND_PROTOTYPE);
403  CHECK (fixture.contains(cmd)); // an equivalent clone was enqueued
404 
405  command1::check_ = 0;
406  fixture.invokeAll();
407  CHECK (command1::check_ == -12); // the clone copy was executed
408 
409  // clean-up: we have bound arguments on the global prototype
410  cmd.unbind();
411  }
412  };
413 
414 
416  LAUNCHER (CommandInstanceManager_test, "unit controller");
417 
418 
419 }}} // namespace proc::control::test
CommandSetup test_Dummy_command1
test dummy command to add the argument to a global variable
int64_t check_
< test command just adding a given value
Record< GenNode > Rec
Definition: gen-node.hpp:133
Automatically use custom string conversion in C++ stream output.
Definition: run.hpp:49
Conveniently iterable stack and queue containers.
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:75
Front-end for printf-style string template interpolation.
Service to support forming and invocation of command instances for use by the UI. ...
bool canExec() const
Definition: command.cpp:357
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify a statement indeed raises an exception.
A front-end for using printf-style formatting.
Maintain a current command instance for parametrisation.
Token or Atom with distinct identity.
Definition: symbol.hpp:116
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition: util.hpp:205
RET bind()
Accept dummy binding (0 Arg)
std::vector< string > & Arg
Definition: run.hpp:54
LAUNCHER(ArgumentTupleAccept_test, "unit controller")
Register this test class...
#define LERR_(_NAME_)
Definition: error.hpp:58
Simple test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
bool isValid() const
Definition: handle.hpp:104
static Command get(Symbol cmdID)
Access existing command for use.
Definition: command.cpp:127
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:125
Proc-Layer implementation namespace root.
Definition: id-scheme.hpp:63
ExecResult undo()
Definition: command.hpp:246