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) 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 steam {
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 LERR_(LIFECYCLE);
54  using LERR_(INVALID_COMMAND);
55  using LERR_(DUPLICATE_COMMAND);
56  using LERR_(UNBOUND_ARGUMENTS);
57 
58 
59  namespace { // Test fixture....
60 
61  const Symbol COMMAND_PROTOTYPE = test_Dummy_command1;
62  const string INVOCATION_ID = "CommandInstanceManager_test";
63 
64  class Fixture
65  : public CommandDispatch
66  {
67  std::deque<Command> queue_;
68 
69 
70  /* == Interface: CommandDispatch == */
71 
72  void
73  clear() override
74  {
75  queue_.clear();
76  }
77 
78  void
79  enqueue (Command&& cmd) override
80  {
81  queue_.emplace_front (move (cmd));
82  }
83 
84  public:
85  bool
86  contains (Command const& ref)
87  {
88  return queue_.end()!= std::find_if (queue_.begin()
89  ,queue_.end()
90  ,[=](Command const& elm)
91  {
92  return elm == ref;
93  });
94  }
95 
96  void
97  invokeAll()
98  {
99  for (Command& cmd : queue_)
100  cmd();
101  clear();
102  }
103  };
104  }
105 
106 
107 
108  /***********************************************************************/
116  class CommandInstanceManager_test : public Test
117  {
118 
119  virtual void
120  run (Arg)
121  {
122  verify_simpleUsage();
123  verify_extendedUsage();
124  verify_instanceIdentity();
125  verify_duplicates();
126  verify_lifecycle();
127  verify_fallback();
128  }
129 
130 
141  void
143  {
144  Fixture fixture;
145  CommandInstanceManager iManager{fixture};
146  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
147 
148  int r1{rand()%1000}, r2{rand()%2000};
149  command1::check_ = 0; // commands will add to this on invocation
150 
151  iManager.bindAndDispatch (COMMAND_PROTOTYPE, Rec{r1});
152  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
153 
154  Command com{COMMAND_PROTOTYPE};
155  com.bind(r2);
156  CHECK (com.canExec());
157 
158  iManager.dispatch (COMMAND_PROTOTYPE);
159  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
160 
161  // an anonymous clone instance was dispatched,
162  // thus re-binding the arguments won't interfere with execution
163  com.bind(-1);
164 
165  CHECK (command1::check_ == 0); // nothing invoked yet
166  fixture.invokeAll();
167  CHECK (command1::check_ == r1 + r2); // both instances were invoked with their specific arguments
168 
169  // clean-up: we have bound arguments on the global prototype
170  com.unbind();
171  }
172 
173 
175  void
177  {
178  Fixture fixture;
179  CommandInstanceManager iManager{fixture};
180  Symbol instanceID = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
181  CHECK (iManager.contains (instanceID));
182 
183  Command cmd = iManager.getInstance (instanceID);
184  CHECK (cmd);
185  CHECK (not cmd.canExec());
186 
187  cmd.bind(42);
188  CHECK (cmd.canExec());
189 
190  iManager.dispatch (instanceID);
191  CHECK (fixture.contains (cmd));
192  CHECK (not iManager.contains (instanceID));
193  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
194 
195  command1::check_ = 0;
196  fixture.invokeAll();
197  CHECK (command1::check_ == 42); // the dispatched instance was executed
198  }
199 
200 
201 
219  void
221  {
222  Fixture fixture;
223  CommandInstanceManager iManager{fixture};
224  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
225  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
226 
227  Command c11 = iManager.getInstance (i1);
228  Command c12 = iManager.getInstance (i1);
229  CHECK (c11 == c12);
230  CHECK (c11.isValid());
231  CHECK (not c11.canExec());
232 
233  int r1{rand()%100}, r2{rand()%200}, r3{rand()%300};
234  command1::check_ = 0; // commands will add to this on invocation
235 
236  c11.bind (r1);
237  CHECK (c12.canExec());
238  CHECK (c11.canExec());
239 
240  Command c2 = iManager.getInstance (i2);
241  CHECK (c2 != c11);
242  CHECK (c2 != c12);
243  c2.bind (r2);
244 
245  CHECK (iManager.contains (i1));
246  CHECK (iManager.contains (i2));
247  CHECK (not fixture.contains (c11));
248  CHECK (not fixture.contains (c12));
249  CHECK (not fixture.contains (c2));
250 
251  iManager.dispatch (i1);
252  CHECK (not iManager.contains (i1));
253  CHECK ( iManager.contains (i2));
254  CHECK ( fixture.contains (c11));
255  CHECK ( fixture.contains (c12));
256  CHECK (not fixture.contains (c2));
257 
258  CHECK (command1::check_ == 0);
259 
260  Symbol i11 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
261  CHECK (i11 == i1);
262  CHECK ((const char*)i11 == (const char*) i1);
263 
264  // but the instances themselves are disjoint
265  Command c13 = iManager.getInstance (i1);
266  CHECK (c13 != c11);
267  CHECK (c13 != c12);
268  CHECK (c11.canExec());
269  CHECK (not c13.canExec());
270 
271  c13.bind (r3);
272  CHECK (c13.canExec());
273 
274  CHECK (command1::check_ == 0);
275  c12();
276  CHECK (command1::check_ == 0+r1);
277 
278  // even a command still in instance manager can be invoked
279  c2();
280  CHECK (command1::check_ == 0+r1+r2);
281 
282  CHECK ( iManager.contains (i1));
283  CHECK ( iManager.contains (i2));
284  CHECK ( fixture.contains (c11));
285  CHECK ( fixture.contains (c12));
286  CHECK (not fixture.contains (c2));
287 
288  iManager.dispatch (i2);
289  iManager.dispatch (i11);
290  CHECK (not iManager.contains (i1));
291  CHECK (not iManager.contains (i2));
292  CHECK ( fixture.contains (c11));
293  CHECK ( fixture.contains (c12));
294  CHECK ( fixture.contains (c13));
295  CHECK ( fixture.contains (c2));
296 
297  // if we continue to hold onto an instance,
298  // we can do anything with it. Like re-binding arguments.
299  c2.bind (47);
300  c2();
301  c13();
302  c13();
303  CHECK (command1::check_ == 0+r1+r2+47+r3+r3);
304 
305  c11.undo();
306  CHECK (command1::check_ == 0);
307  c2.undo();
308  CHECK (command1::check_ == 0+r1+r2); // undo() restores the value captured before second invocation of c2()
309  c12.undo(); // c11 and c12 refer to the same instance, which was invoked first
310  CHECK (command1::check_ == 0);
311  }
312 
313 
320  void
322  {
323  Fixture fixture;
324  CommandInstanceManager iManager{fixture};
325  Symbol i1 = iManager.newInstance (COMMAND_PROTOTYPE, "i1");
326  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, "i2");
327 
328  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i1"));
329  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
330 
331  iManager.bindAndDispatch (i1, {-1}); // bind and dispatch i1, thus i1 is ready for new cycle
332 
333  iManager.newInstance (COMMAND_PROTOTYPE, "i1"); // open new cycle for i1
334  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, "i2"));
335 
336  CHECK (iManager.getInstance (i1));
337  CHECK (iManager.getInstance (i2));
338  }
339 
340 
348  void
350  {
351  Fixture fixture;
352  CommandInstanceManager iManager{fixture};
353 
354  // a manually constructed ID is unknown of course
355  Symbol instanceID{COMMAND_PROTOTYPE, INVOCATION_ID};
356  VERIFY_ERROR (INVALID_COMMAND, iManager.getInstance (instanceID));
357  VERIFY_ERROR (INVALID_COMMAND, iManager.dispatch (instanceID));
358 
359  Symbol i2 = iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID);
360  CHECK (i2 == instanceID);
361  CHECK (iManager.getInstance (instanceID));
362 
363 
364  Command cmd = iManager.getInstance (instanceID);
365  CHECK (cmd);
366  CHECK (not cmd.canExec());
367 
368  VERIFY_ERROR (UNBOUND_ARGUMENTS, iManager.dispatch (instanceID));
369  VERIFY_ERROR (DUPLICATE_COMMAND, iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
370  CHECK (iManager.contains (instanceID)); // errors have not messed up anything
371 
372  cmd.bind(23);
373  CHECK (cmd.canExec());
374  iManager.dispatch (instanceID);
375 
376  CHECK (not iManager.contains (instanceID));
377  VERIFY_ERROR (LIFECYCLE, iManager.getInstance (instanceID));
378  VERIFY_ERROR (LIFECYCLE, iManager.dispatch (instanceID));
379  CHECK (instanceID == iManager.newInstance (COMMAND_PROTOTYPE, INVOCATION_ID));
380  }
381 
382 
385  void
387  {
388  Fixture fixture;
389  CommandInstanceManager iManager{fixture};
390 
391  CHECK (not iManager.contains (COMMAND_PROTOTYPE));
392  Command cmd = iManager.getInstance (COMMAND_PROTOTYPE);
393 
394  CHECK (cmd.isValid());
395  CHECK (not cmd.isAnonymous());
396  CHECK (cmd == Command::get(COMMAND_PROTOTYPE));
397  CHECK (cmd == Command{COMMAND_PROTOTYPE});
398 
399  cmd.bind(-12);
400  CHECK (cmd.canExec());
401  CHECK (not fixture.contains(cmd));
402 
403  iManager.dispatch (COMMAND_PROTOTYPE);
404  CHECK (fixture.contains(cmd)); // an equivalent clone was enqueued
405 
406  command1::check_ = 0;
407  fixture.invokeAll();
408  CHECK (command1::check_ == -12); // the clone copy was executed
409 
410  // clean-up: we have bound arguments on the global prototype
411  cmd.unbind();
412  }
413  };
414 
415 
417  LAUNCHER (CommandInstanceManager_test, "unit controller");
418 
419 
420 }}} // namespace steam::control::test
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:85
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
#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:129
Steam-Layer implementation namespace root.
A front-end for using printf-style formatting.
Token or Atom with distinct identity.
Definition: symbol.hpp:126
Simple 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:124
void bindAndDispatch(Symbol instanceID, Rec const &argSeq)
fire and forget anonymous command instance.
RET bind()
Accept dummy binding (0 Arg)