Lumiera  0.pre.03
»edit your freedom«
dispatcher-looper-test.cpp
1 /*
2  DispatcherLooper(Test) - verify loop control and timing functionality of SteamDispatcher
3 
4  Copyright (C)
5  2016, 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 
14 
15 #include "lib/test/run.hpp"
16 #include "steam/control/looper.hpp"
17 
18 #include <chrono>
19 #include <thread>
20 
21 
22 namespace steam {
23 namespace control {
24 namespace test {
25 
26  using std::this_thread::sleep_for;
27  using namespace std::chrono_literals;
28 
29 
30  namespace { // test fixture...
31 
32  using Dur = std::chrono::duration<double, std::milli>;
33 
38  const uint EXPECTED_BUILDER_DELAY_ms = 50;
39 
40  bool
41  isFast (milliseconds timeoutDelay)
42  {
43  return timeoutDelay < Dur{1.2 * EXPECTED_BUILDER_DELAY_ms}
44  and 0ms < timeoutDelay;
45  }
46 
47  bool
48  isSlow (milliseconds timeoutDelay)
49  {
50  return timeoutDelay >= Dur{1.2 * EXPECTED_BUILDER_DELAY_ms};
51  }
52 
53  bool
54  isDisabled (milliseconds timeoutDelay)
55  {
56  return 0ms == timeoutDelay;
57  }
58 
59 
66  struct Setup
67  {
68  bool has_commands_in_queue = false;
69 
70  Looper
71  install()
72  {
73  return Looper([&](){ return has_commands_in_queue; });
74  }
75  };
76 
77  }//(End) test fixture
78 
79 
80 
81 
82 
83 
84 
85  /******************************************************************************/
95  class DispatcherLooper_test : public Test
96  {
97 
98 
99  virtual void
100  run (Arg)
101  {
102  verifyBasics();
103  verifyShutdown();
104  verifyWakeupActivity();
105  verifyShutdown_stops_processing();
106  verifyDisabling_stops_processing();
107  verifyBuilderStart();
108  }
109 
110 
111  void
112  verifyBasics()
113  {
114  Setup setup;
115  Looper looper = setup.install();
116 
117  CHECK (not looper.isDying());
118  CHECK (looper.shallLoop());
119  CHECK (not looper.runBuild());
120  CHECK (isDisabled (looper.getTimeout()));
121 
122  setup.has_commands_in_queue = true;
123  CHECK (looper.requireAction());
124 
125  milliseconds timeout = looper.getTimeout();
126  CHECK (10ms < timeout, "configured idle timeout %2l to short", timeout.count());
127  CHECK (timeout < 800ms, "configured idle timeout %3l to long", timeout.count());
128  }
129 
130 
131  void
132  verifyShutdown()
133  {
134  Setup setup;
135  Looper looper = setup.install();
136 
137  CHECK (not looper.isDying());
138  CHECK (looper.shallLoop());
139 
140  looper.triggerShutdown();
141  CHECK (looper.isDying());
142  CHECK (not looper.shallLoop());
143  }
144 
145 
146  void
147  verifyWakeupActivity()
148  {
149  Setup setup;
150  Looper looper = setup.install();
151 
152  CHECK (not looper.isDying());
153  CHECK (looper.shallLoop());
154 
155  CHECK (not looper.requireAction());
156  CHECK (not looper.isWorking());
157  CHECK ( looper.isIdle());
158 
159  setup.has_commands_in_queue = true;
160 
161  CHECK ( looper.requireAction());
162  CHECK ( looper.isWorking());
163  CHECK (not looper.isIdle());
164  CHECK (looper.shallLoop());
165 
166  setup.has_commands_in_queue = false;
167  looper.markStateProcessed(); // after command processing
168  CHECK (not looper.requireAction()); // stops immediate work state
169  CHECK ( looper.useTimeout()); // but still performs timeout
170  CHECK (not looper.isWorking());
171  CHECK (not looper.isIdle()); // still need to run the builder
172 
173  looper.markStateProcessed(); // second round-trip, after builder run
174 
175  CHECK (not looper.requireAction());
176  CHECK (not looper.isWorking());
177  CHECK ( looper.isIdle());
178  CHECK (looper.shallLoop());
179 
180  looper.triggerShutdown();
181 
182  CHECK (not looper.shallLoop());
183 
184  CHECK ( looper.requireAction());
185  CHECK (not looper.isWorking());
186  CHECK (not looper.isIdle());
187  }
188 
189 
190  void
191  verifyShutdown_stops_processing()
192  {
193  Setup setup;
194  Looper looper = setup.install();
195 
196  CHECK (not looper.isDying());
197  CHECK (looper.shallLoop());
198 
199  CHECK (not looper.requireAction());
200  CHECK (not looper.isWorking());
201  CHECK ( looper.isIdle());
202 
203  setup.has_commands_in_queue = true;
204 
205  CHECK ( looper.requireAction());
206  CHECK ( looper.isWorking());
207  CHECK (not looper.isIdle());
208  CHECK ( looper.shallLoop());
209  CHECK (not looper.isDying());
210 
211  looper.triggerShutdown();
212 
213  CHECK ( looper.requireAction());
214  CHECK (not looper.isWorking());
215  CHECK (not looper.isIdle());
216  CHECK (not looper.shallLoop());
217  CHECK ( looper.isDying());
218 
219  setup.has_commands_in_queue = false;
220 
221  CHECK ( looper.requireAction());
222  CHECK (not looper.isWorking());
223  CHECK (not looper.isIdle());
224  CHECK (not looper.shallLoop());
225  CHECK ( looper.isDying());
226 
227  setup.has_commands_in_queue = true;
228 
229  CHECK ( looper.requireAction());
230  CHECK (not looper.isWorking());
231  CHECK (not looper.isIdle());
232  CHECK (not looper.shallLoop());
233  CHECK ( looper.isDying());
234  }
235 
236 
237  void
238  verifyDisabling_stops_processing()
239  {
240  Setup setup;
241  Looper looper = setup.install();
242 
243  CHECK (not looper.requireAction());
244  CHECK (not looper.isDisabled());
245  CHECK (not looper.isWorking());
246  CHECK ( looper.isIdle());
247  CHECK ( looper.shallLoop());
248  CHECK (not looper.isDying());
249 
250  setup.has_commands_in_queue = true; // normal operation: pending commands will be processed
251 
252  CHECK ( looper.requireAction()); // ..causes wake-up
253  CHECK (not looper.isDisabled());
254  CHECK ( looper.isWorking());
255  CHECK (not looper.isIdle());
256  CHECK ( looper.shallLoop());
257  CHECK (not looper.isDying());
258 
259  looper.enableProcessing(false); // disable processing
260 
261  CHECK (not looper.requireAction());
262  CHECK ( looper.isDisabled());
263  CHECK (not looper.isWorking());
264  CHECK (not looper.isIdle());
265  CHECK ( looper.shallLoop());
266  CHECK (not looper.isDying());
267 
268  setup.has_commands_in_queue = false; // while disabled, state of the command queue has no effect
269 
270  CHECK (not looper.requireAction());
271  CHECK ( looper.isDisabled());
272  CHECK (not looper.isWorking());
273  CHECK (not looper.isIdle());
274  CHECK ( looper.shallLoop());
275  CHECK (not looper.isDying());
276 
277  setup.has_commands_in_queue = true;
278 
279  CHECK (not looper.requireAction());
280  CHECK ( looper.isDisabled());
281  CHECK (not looper.isWorking());
282  CHECK (not looper.isIdle());
283  CHECK ( looper.shallLoop());
284  CHECK (not looper.isDying());
285 
286  looper.enableProcessing(); // resume normal operation
287 
288  CHECK ( looper.requireAction());
289  CHECK (not looper.isDisabled());
290  CHECK ( looper.isWorking());
291  CHECK (not looper.isIdle());
292  CHECK ( looper.shallLoop());
293  CHECK (not looper.isDying());
294 
295  looper.enableProcessing(false); // disable again
296 
297  CHECK (not looper.requireAction());
298  CHECK ( looper.isDisabled());
299  CHECK (not looper.isWorking());
300  CHECK (not looper.isIdle());
301  CHECK ( looper.shallLoop());
302  CHECK (not looper.isDying());
303 
304  looper.triggerShutdown(); // wake-up for shutdown even from disabled state
305 
306  CHECK ( looper.requireAction());
307  CHECK ( looper.isDisabled());
308  CHECK (not looper.isWorking());
309  CHECK (not looper.isIdle());
310  CHECK (not looper.shallLoop());
311  CHECK ( looper.isDying());
312  }
313 
314 
324  void
326  {
327  Setup setup;
328  Looper looper = setup.install();
329 
330  CHECK (not looper.requireAction());
331  CHECK (not looper.isDisabled());
332  CHECK (not looper.isWorking());
333  CHECK (not looper.runBuild());
334  CHECK ( looper.isIdle());
335 
336  setup.has_commands_in_queue = true; // regular command processing
337 
338  CHECK ( looper.requireAction());
339  CHECK (not looper.isDisabled());
340  CHECK ( looper.isWorking());
341  CHECK (not looper.runBuild());
342  CHECK (not looper.isIdle());
343 
344  looper.markStateProcessed(); // at least one command has been handled
345 
346  CHECK ( looper.requireAction());
347  CHECK (not looper.isDisabled());
348  CHECK ( looper.isWorking());
349  CHECK (not looper.runBuild()); // ...note: build not yet triggered
350  CHECK (not looper.isIdle());
351 
352  CHECK (isSlow (looper.getTimeout()));
353 
354 
355  looper.markStateProcessed(); // next processing round: further command(s) processed,
356  // yet still more commands pending...
357  CHECK ( looper.requireAction());
358  CHECK (not looper.isDisabled());
359  CHECK ( looper.isWorking());
360  CHECK (not looper.runBuild()); // ...build still postponed
361  CHECK (not looper.isIdle());
362 
363  sleep_for (800ms); // let's assume we did command processing for a long time...
364 
365  CHECK ( looper.requireAction());
366  CHECK (not looper.isDisabled());
367  CHECK ( looper.isWorking());
368  CHECK ( looper.runBuild()); // ...after some time of command processing, a build run is forced
369  CHECK (not looper.isIdle());
370 
371  looper.markStateProcessed(); // and when the builder run is confirmed...
372 
373  CHECK ( looper.requireAction());
374  CHECK (not looper.isDisabled());
375  CHECK ( looper.isWorking());
376  CHECK (not looper.runBuild()); // ...we are back to normal working state (build postponed)
377  CHECK (not looper.isIdle());
378 
379 
380  setup.has_commands_in_queue = false; // now emptied our queue
381 
382  CHECK (not looper.requireAction());
383  CHECK (not looper.isDisabled());
384  CHECK (not looper.isWorking());
385  CHECK ( looper.runBuild()); // ...note: now build will be triggered
386  CHECK (not looper.isIdle());
387 
388  CHECK (isFast (looper.getTimeout())); // ...but only after a short wait period,
389  // since not looper.requireAction()
390 
391 
392  looper.markStateProcessed(); // next processing round: invoked builder,
393  // and no more commands commands pending...
394  CHECK (not looper.requireAction());
395  CHECK (not looper.isDisabled());
396  CHECK (not looper.isWorking());
397  CHECK (not looper.runBuild()); // ...note: now done with building
398  CHECK ( looper.isIdle());
399 
400  CHECK (isDisabled(looper.getTimeout())); // ...now Dispatcher is idle and goes to sleep
401 
402 
403  setup.has_commands_in_queue = true; // next command pending
404 
405  CHECK ( looper.requireAction()); // return to work mode
406  CHECK (not looper.isDisabled());
407  CHECK ( looper.isWorking());
408  CHECK (not looper.runBuild());
409  CHECK (not looper.isIdle());
410 
411  setup.has_commands_in_queue = false; // now let's assume command has been processed
412  looper.markStateProcessed(); // and queue is empty again
413 
414  CHECK (not looper.requireAction());
415  CHECK (not looper.isDisabled());
416  CHECK (not looper.isWorking());
417  CHECK ( looper.runBuild());
418  CHECK (not looper.isIdle());
419 
420  CHECK (isFast (looper.getTimeout())); // now build *would* be triggered after short timeout, but..
421 
422 
423  looper.enableProcessing(false); // disable processing
424 
425  CHECK (not looper.requireAction());
426  CHECK ( looper.isDisabled());
427  CHECK (not looper.isWorking());
428  CHECK (not looper.runBuild()); // ...note: dirty state hidden by disabled state
429  CHECK (not looper.isIdle());
430 
431  CHECK (isDisabled (looper.getTimeout()));
432 
433 
434  looper.enableProcessing(true); // enable back
435 
436  CHECK (not looper.requireAction());
437  CHECK (not looper.isDisabled());
438  CHECK (not looper.isWorking());
439  CHECK ( looper.runBuild()); // ...note: dirty state revealed again
440  CHECK (not looper.isIdle());
441 
442  CHECK (isFast (looper.getTimeout()));
443 
444  looper.enableProcessing(false); // disable processing
445  looper.markStateProcessed(); // let's assume builder was running and is now finished
446 
447  CHECK (not looper.requireAction());
448  CHECK ( looper.isDisabled());
449  CHECK (not looper.isWorking());
450  CHECK (not looper.runBuild()); // ...note: dirty state not obvious
451  CHECK (not looper.isIdle());
452 
453  CHECK (isDisabled (looper.getTimeout()));
454 
455 
456  looper.enableProcessing(true); // enable back
457  // NOTE special twist: it's unclear, if builder was triggered before the disabled state...
458  CHECK (isFast (looper.getTimeout())); // ...and thus we remain in dirty state
459 
460  CHECK (not looper.requireAction());
461  CHECK (not looper.isDisabled());
462  CHECK (not looper.isWorking());
463  CHECK ( looper.runBuild()); // so the builder will be triggered (possibly a second time) after a short timeout
464  CHECK (not looper.isIdle());
465 
466  looper.markStateProcessed(); // and after one round-trip the builder was running and is now finished
467 
468  CHECK (not looper.requireAction());
469  CHECK (not looper.isDisabled());
470  CHECK (not looper.isWorking());
471  CHECK (not looper.runBuild());
472  CHECK ( looper.isIdle()); // ...system is in idle state now and waits until triggered externally
473 
474  CHECK (isDisabled (looper.getTimeout()));
475 
476 
477  setup.has_commands_in_queue = true; // more commands again -> wake up
478  looper.markStateProcessed(); // ...and let's assume one command has already been processed
479 
480  CHECK ( looper.requireAction());
481  CHECK (not looper.isDisabled());
482  CHECK ( looper.isWorking());
483  CHECK (not looper.runBuild());
484  CHECK (not looper.isIdle());
485 
486  looper.triggerShutdown(); // request shutdown...
487 
488  CHECK ( looper.requireAction());
489  CHECK ( looper.isDisabled());
490  CHECK (not looper.isWorking());
491  CHECK (not looper.runBuild());
492  CHECK (not looper.isIdle());
493 
494  CHECK (isDisabled (looper.getTimeout()));
495 
496 
497  setup.has_commands_in_queue = false; // and even when done with all commands...
498  looper.markStateProcessed();
499 
500  CHECK (isDisabled (looper.getTimeout()));
501  CHECK (not looper.shallLoop()); // we remain disabled and break out of the loop
502 
503  CHECK ( looper.requireAction());
504  CHECK ( looper.isDisabled());
505  CHECK (not looper.isWorking());
506  CHECK (not looper.runBuild()); // ...note: still no need for builder run, since in shutdown
507  CHECK (not looper.isIdle());
508 
509  }
510  };
511 
512 
514  LAUNCHER (DispatcherLooper_test, "unit controller");
515 
516 
517 }}} // namespace steam::control::test
Definition: Setup.py:1
Definition: run.hpp:40
Steam-Layer implementation namespace root.
bool shallLoop() const
state fusion to control looping
Definition: looper.hpp:197
bool requireAction()
state fusion to control (timed) wait
Definition: looper.hpp:180
Implementation building block of SteamDispatcher to control waiting and timing.
Simplistic test class runner.
void markStateProcessed()
invoking this function signals that all consequences of past state changes have been processed and ar...
Definition: looper.hpp:165
auto setup(FUN &&workFun)
Helper: setup a Worker-Pool configuration for the test.
Encapsulated control logic for the session thread loop.
Definition: looper.hpp:105