Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
ui-coord-resolver-test.cpp
Go to the documentation of this file.
1/*
2 UICoordResolver(Test) - resolve UI coordinates against actual topology
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"
24#include "lib/diff/gen-node.hpp"
25#include "lib/format-util.hpp"
26#include "lib/util.hpp"
27
28#include <string>
29
30
31using std::string;
33using lib::diff::Rec;
34using lib::Symbol;
35using util::isnil;
36using util::join;
37
38
39
40namespace stage {
41namespace interact{
42namespace test {
43
44 using LERR_(INVALID);
45 using LERR_(STATE);
46
47
48
49 /******************************************************************************/
63 class UICoordResolver_test : public Test
64 {
65
66 virtual void
77
78
85 void
87 {
88 // a Test dummy placeholder for the real UI structure
89 Rec dummyUiStructure = MakeRec()
90 .set("window-1"
91 , MakeRec()
92 .type("perspective-A")
93 )
94 .set("window-2"
95 , MakeRec()
96 .type("perspective-B")
97 .set("panelX", MakeRec())
98 .set("panelXX", MakeRec())
99 );
100
101 // helper to answer "location queries" backed by this structure
102 GenNodeLocationQuery locationQuery{dummyUiStructure};
103
104 UICoord uic{"window-2","*","panelX","someView"};
105 UICoordResolver resolver{uic, locationQuery};
106
107 CHECK (not resolver.isCovered());
108 CHECK ( resolver.canCover());
109
110 UICoord uic2 = resolver.cover()
111 .extend("otherView");
112
113 CHECK ("UI:window-2[perspective-B]-panelX.otherView" == string(uic2));
114 }
115
116
141 void
143 {
144 GenNodeLocationQuery queryAPI{MakeRec()
145 .set("window-1"
146 , MakeRec()
147 .type("perspective-A")
148 .set("panelX"
149 , MakeRec()
150 .set("firstView", MakeRec())
151 .set("secondView", MakeRec())
152 )
153 )
154 .set("window-2"
155 , MakeRec()
156 .type("perspective-B")
157 .set("panelY", MakeRec())
158 )
159 .set("window-3"
160 , MakeRec()
161 .type("perspective-C")
162 .set("panelZ"
163 , MakeRec()
164 .set("thirdView", MakeRec())
165 )
166 .set("panelZZ", MakeRec())
167 )
168 };
169
170 // the LocationQuery API works by matching a UICoord spec against the "real" structure
171 UICoord uic1 = UICoord::window("window-2").persp("perspective-B");
172 UICoord uic2 = UICoord::window("windows");
173 UICoord uic3 = UICoord::firstWindow().persp("perspective-A").panel("panelX").view("secondView");
174 UICoord uic4 = UICoord::currentWindow().persp("perspective-B");
175 UICoord uic5 = UICoord::currentWindow().persp("perspective-C").panel("panelZ").view("someOtherView");
176
177 CHECK ("window-2" == queryAPI.determineAnchor(uic1));
178 CHECK (Symbol::BOTTOM == queryAPI.determineAnchor(uic2));
179 CHECK ("window-1" == queryAPI.determineAnchor(uic3));
180 CHECK ("window-3" == queryAPI.determineAnchor(uic4));
181 CHECK ("window-3" == queryAPI.determineAnchor(uic5));
182
183 CHECK (2 == queryAPI.determineCoverage(uic1));
184 CHECK (0 == queryAPI.determineCoverage(uic2));
185 CHECK (4 == queryAPI.determineCoverage(uic3));
186 CHECK (1 == queryAPI.determineCoverage(uic4));
187 CHECK (3 == queryAPI.determineCoverage(uic5));
188
189 LocationQuery::ChildIter cii = queryAPI.getChildren(uic3, 3);
190 CHECK (not isnil(cii));
191 CHECK ("firstView" == *cii);
192 ++cii;
193 CHECK ("secondView" == *cii);
194 CHECK (not isnil(cii));
195 ++cii;
196 CHECK (isnil(cii));
197
198 CHECK ("window-1, window-2, window-3" == join (queryAPI.getChildren (uic3, 0)));
199 CHECK ("perspective-A" == join (queryAPI.getChildren (uic3, 1)));
200 CHECK ("panelX" == join (queryAPI.getChildren (uic3, 2)));
201 CHECK ("firstView, secondView" == join (queryAPI.getChildren (uic3, 3)));
202 CHECK (isnil ( queryAPI.getChildren (uic3, 4))); // "firstView" has no children
203
204 CHECK ("window-1, window-2, window-3" == join (queryAPI.getChildren (uic2, 0)));
205 VERIFY_ERROR (STATE, queryAPI.getChildren (uic2, 1) ); // "windows" at pos==0 is not covered by real UI
206
207 CHECK ("window-1, window-2, window-3" == join (queryAPI.getChildren (uic5, 0)));
208 CHECK ("perspective-C" == join (queryAPI.getChildren (uic5, 1)));
209 CHECK ("panelZ, panelZZ" == join (queryAPI.getChildren (uic5, 2)));
210 CHECK ("thirdView" == join (queryAPI.getChildren (uic5, 3)));
211 VERIFY_ERROR (STATE, queryAPI.getChildren (uic5, 4) ); // "someOtherView" at level 4 does not exist
212
213
214 // verify "child exploration" via iterator interface
215 cii = queryAPI.getChildren (uic3, 0); // enter at root level...
216 CHECK ("window-1" == *cii); // first child of root to appear is "window-1"
217 CHECK (0 == cii.depth()); // (note depth just happens to coincide with absolute tree depth here)
218 cii.expandChildren(); // drill down into current element's children
219 CHECK (1 == cii.depth());
220 CHECK ("perspective-A" == *cii); // which is just one, the perspective
221 cii.expandChildren(); // drill down into the (formal, logical) children of "perspective-A"
222 CHECK (2 == cii.depth());
223 CHECK ("panelX" == *cii); // ..and find the "panelX" at level 2
224 cii.expandChildren(); // drill down one level further
225 CHECK (3 == cii.depth());
226 CHECK ("firstView" == *cii); // and then just continue iteration, which first explores that scope...
227 CHECK ("firstView, secondView, window-2, window-3" == join (cii)); // ...followed by returning to the enclosing scopes, finally top level.
228 }
229
230
237 void
239 {
240 GenNodeLocationQuery tree{MakeRec()
241 .set("window-1"
242 , MakeRec()
243 .type("perspective-A")
244 )
245 .set("window-2"
246 , MakeRec()
247 .type("perspective-B")
248 .set("panelX"
249 , MakeRec()
250 .set("someView", MakeRec())
251 )
252 )
253 };
254 UICoord uic1 = UICoord::window("window-1").persp("perspective-A");
255 UICoord uic2 = UICoord::window("windows");
257 UICoord uic4 = UICoord::currentWindow().persp("perspective-B");
258 UICoord uic5 = UICoord::currentWindow().panel("panelY");
259 UICoord uic6 = UICoord().view("someView");
260
261 UICoordResolver r1{uic1, tree};
262 UICoordResolver r2{uic2, tree};
263 UICoordResolver r3{uic3, tree};
264 UICoordResolver r4{uic4, tree};
265 UICoordResolver r5{uic5, tree};
266 UICoordResolver r6{uic6, tree};
267
268 CHECK ( r1.isAnchored());
269 CHECK (not r2.isAnchored());
270 CHECK ( r3.isAnchored());
271 CHECK ( r4.isAnchored());
272 CHECK ( r5.isAnchored());
273 CHECK (not r6.isAnchored());
274
275 CHECK ( r1.canAnchor());
276 CHECK (not r2.canAnchor());
277 CHECK ( r3.canAnchor());
278 CHECK ( r4.canAnchor());
279 CHECK ( r5.canAnchor());
280 CHECK ( r6.canAnchor());
281 }
282
283
284
319 void
321 {
322 GenNodeLocationQuery tree{MakeRec()
323 .set("window-1"
324 , MakeRec()
325 .type("persp-A")
326 .set("panelX"
327 , MakeRec()
328 .set("firstView", MakeRec())
329 .set("secondView", MakeRec())
330 )
331 .set("panelZ"
332 , MakeRec()
333 .set("thirdView"
334 , MakeRec()
335 .set("#1", MakeRec())
336 .set("#2", MakeRec())
337 .set("tab", MakeRec())
338 )
339 )
340 )
341 .set("window-2"
342 , MakeRec()
343 .type("persp-B")
344 .set("panelY", MakeRec())
345 )
346 .set("window-3"
347 , MakeRec()
348 .type("persp-C")
349 .set("panelZ"
350 , MakeRec()
351 .set("thirdView"
352 , MakeRec()
353 .set("tab"
354 , MakeRec()
355 .set("sub", MakeRec())
356 )
357 .set("#1", MakeRec())
358 )
359 )
360 .set("panelZZ", MakeRec())
361 )
362 };
363
364 /* === trivial cases === */
365 UICoordResolver r11 {UICoord::window("window-1")
366 .persp("persp-A")
367 .panel("panelX"), tree};
368 CHECK (r11.isCovered());
369 CHECK (3 == r11.coverDepth());
370
371
372 UICoordResolver r12 {UICoord::window("window-1")
373 .persp("persp-A")
374 .panel("panelX")
375 .view("thirdView"), tree};
376 CHECK (not r12.isCovered());
377 CHECK ( r12.isCoveredPartially());
378 CHECK (3 ==r12.coverDepth());
379 CHECK ("UI:window-1[persp-A]-panelX.thirdView" == string(r12));
380
381 r12.cover();
382 CHECK (r12.isCovered());
383 CHECK (r12.isCoveredPartially());
384 CHECK (3 ==r12.coverDepth());
385 CHECK ("UI:window-1[persp-A]-panelX" == string(r12));
386
387
388 /* === expand anchor === */
389 UICoordResolver r21 {UICoord::firstWindow().persp("persp-A"), tree};
390 CHECK ("UI:firstWindow[persp-A]" == string(r21));
391 r21.cover();
392 CHECK ("UI:window-1[persp-A]" == string(r21));
393
394 /* === expand anchor alone === */
396 CHECK ("UI:window-3" == string(r22.cover()));
397
398
399 /* === interpolate a single gap === */
400 UICoordResolver r31 {UICoord::window("window-1").view("secondView"), tree};
401 CHECK ("UI:window-1[*]-*.secondView" == string(r31));
402 CHECK (0 ==r31.coverDepth());
403 CHECK (not r31.isCovered());
404 CHECK (r31.canCover());
405 r31.cover();
406 CHECK (r31.isCovered());
407 CHECK (4 == r31.coverDepth());
408 CHECK ("UI:window-1[persp-A]-panelX.secondView" == string(r31));
409
410 /* === interpolate several gaps === */
411 UICoordResolver r32 {UICoord().view("thirdView").path("sub"), tree};
412 CHECK ("UI:window-3[persp-C]-panelZ.thirdView.tab/sub" == string(r32.cover()));
413
414 /* === interpolate anchor and consecutive wildcards === */
416 CHECK ("UI:window-1[persp-A]-panelZ.thirdView.#2" == string(r33.cover()));
417
418 /* === discriminate by anchor and fill second gap === */
419 UICoordResolver r34 {UICoord::currentWindow().panel("panelZ").tab("tab"), tree};
420 CHECK ("UI:currentWindow[*]-panelZ.*.tab" == string(r34));
421 CHECK ("UI:window-3[persp-C]-panelZ.thirdView.tab" == string(r34.cover())); // Note: rest of the path would also match on window-1, but currentWindow == window-3
422
423 UICoordResolver r35 {UICoord::currentWindow().persp(UIC_ELIDED).panel("panelZ").tab("tab"), tree};
424 CHECK ("UI:currentWindow[.]-panelZ.*.tab" == string(r35));
425 CHECK ("UI:window-3[persp-C]-panelZ.thirdView.tab" == string(r35.cover())); // elided (existentially quantified) element interpolated similar to a wildcard
426
427 UICoordResolver r36 {UICoord::currentWindow().panel(UIC_ELIDED).view("nonexisting"), tree};
428 CHECK ("UI:currentWindow[*]-..nonexisting" == string(r36));
429 CHECK ("UI:window-3[persp-C]-panelZ" == string(r36.cover())); // ...but elided counts as existing element and matches arbitrarily (-> contrast this to r44)
430
431
432 /* === trailing wildcards stripped automatically === */
433 UICoordResolver r41 {UICoord::window("window-2").append("*/*"), tree};
434 CHECK ("UI:window-2" == string(r41)); // Note: trailing wildcards are already discarded by PathArray / UICoord
435
436 r41.extend("*/*"); // if we now attempt to "sneak in" trailing wildcards...
437 CHECK ("UI:window-2[*]-*" == string(r41));
438 CHECK (not r41.canCover()); // ...then the algorithm rejects any solution
439 CHECK ("UI:window-2" == string(r41.cover())); // Note: but cover() will act on the previous coverage and just strip the extraneous suffix
440
441 /* === reject gap beyond existing real UI tree === */
442 UICoordResolver r42 {UICoord::window("window-2").append("*/*/*/some/path"), tree};
443 CHECK (not r42.canCover());
444
445 /* === reject gap ending at real UI tree boundary === */
446 UICoordResolver r43 {UICoord::currentWindow().view("firstView").tab("nonexisting"), tree};
447 CHECK (not r43.canCover());
448
449 /* === reject interpolated gap on mismatch right behind === */
450 UICoordResolver r44 {UICoord().view("otherView"), tree}; // Note: will be checked on all four existing views, but never matches
451 CHECK (not r44.canCover());
452
453 /* === reject mismatch immediately behind second gap === */
454 UICoordResolver r45 {UICoord().panel("panelZ").tab(3), tree}; // Note: we have two "panelZ", but none has a tab #3
455 CHECK (not r45.canCover());
456
457 /* === mismatch of tree level === */
458 UICoordResolver r46 {UICoord::currentWindow().append("*/*/panelZ/thirdView"), tree}; // Note: one '*' too much, thus 'panelZ' is matched on view level
459 CHECK (not r46.canCover());
460
461 /* === impossible to anchor === */
463 CHECK (not r47.canCover());
464
465
466 /* === the solution with maximum covered depth wins === */
467 UICoordResolver r51 {UICoord().tab("tab").path("sub"), tree};
468 CHECK ("UI:window-3[persp-C]-panelZ.thirdView.tab/sub" == string(r51.cover())); // the second solution found covers to maximum depth
469
470 /* === when two solutions are equivalent, pick the fist one === */
471 UICoordResolver r52 {UICoord().tab("tab"), tree};
472 CHECK ("UI:window-1[persp-A]-panelZ.thirdView.tab" == string(r52.cover())); // "UI:window-3[persp-C]-panelZ.thirdView.tab" would match too
473
474 /* === best solution will be picked, irrespective of discovery order === */
475 UICoordResolver r531 {UICoord().persp("persp-A").tab(1), tree};
476 CHECK ("UI:window-1[persp-A]-panelZ.thirdView.#1" == string(r531.cover())); // best solution discovered as first one
477
478 UICoordResolver r532 {UICoord().view("thirdView").tab("tab"), tree};
479 CHECK ("UI:window-1[persp-A]-panelZ.thirdView.tab" == string(r532.cover())); // best solution is 3rd of five possible ones
480
481 UICoordResolver r533 {UICoord().persp("persp-C").tab(1), tree};
482 CHECK ("UI:window-3[persp-C]-panelZ.thirdView.#1" == string(r533.cover())); // best solution is found as last one
483 }
484
485
486
494 void
496 {
497 GenNodeLocationQuery tree{MakeRec()
498 .set("window-2"
499 , MakeRec()
500 .type("persp-B")
501 .set("panelY"
502 , MakeRec()
503 .set("someView"
504 , MakeRec()
505 .set("#1", MakeRec())
506 .set("#2", MakeRec())
507 )
508 )
509 )
510 };
511
512 /* === explicitly given spec partially covered === */
513 UICoordResolver r1 {UICoord{"window-2","persp-B","panelY","otherView","tab"}, tree};
514 CHECK (3 == r1.coverDepth());
515 r1.coverPartially();
516 CHECK (not r1.isCovered());
517 CHECK (3 == r1.coverDepth());
518 CHECK (r1.isCoveredPartially()); // is covered down to the "panelY"
519 CHECK ("UI:window-2[persp-B]-panelY.otherView.tab" == string(r1));
520 r1.cover();
521 CHECK (r1.isCovered()); // cover() retains the covered part only
522 CHECK ("UI:window-2[persp-B]-panelY" == string(r1));
523
524 /* === fill wildcard gap but retain uncovered extension === */
525 UICoordResolver r2 {UICoord::currentWindow().view("someView").tab(3).path("sub"), tree};
526 CHECK (0 == r2.coverDepth());
527 r2.coverPartially();
528 CHECK (not r2.isCovered());
529 CHECK (4 == r2.coverDepth());
530 CHECK (r2.isCoveredPartially());
531 CHECK ("UI:window-2[persp-B]-panelY.someView.#3/sub" == string(r2));
532 r2.cover();
533 CHECK ("UI:window-2[persp-B]-panelY.someView" == string(r2));
534
535 /* === reject when gap can not be closed unambiguously === */
536 UICoordResolver r3 {UICoord::currentWindow().view("someView").path("sub"), tree};
537 CHECK (not r3.canCover()); // NOTE: second gap here, tab info missing
538 r3.coverPartially();
539 CHECK (isnil (r3));
540
541 /* === reject when some wildcards remain after partial coverage === */
542 UICoordResolver r4 {UICoord::currentWindow().tab(3).path("sub"), tree};
543 r4.coverPartially();
544 CHECK (isnil (r4));
545
546 /* === existentially quantified (elided) element constitutes partial coverage === */
547 UICoordResolver r5 {UICoord::currentWindow().persp(UIC_ELIDED).panel("fantasy").view("fantomas"), tree};
548 CHECK ("UI:currentWindow[.]-fantasy.fantomas" == string(r5));
549 CHECK (1 == r5.coverDepth());
550 r5.coverPartially();
551 CHECK (not r5.isCovered());
552 CHECK (2 == r5.coverDepth()); // Note side-effect of computing the coverage...
553 CHECK (r5.isCoveredPartially()); // it is known to be covered including "the" perspective
554 CHECK ("UI:window-2[persp-B]-fantasy.fantomas" == string(r5));
555 r5.cover();
556 CHECK ("UI:window-2[persp-B]" == string(r5));
557 CHECK (2 == r5.coverDepth());
558 }
559
560
561
562
574 void
576 {
577 GenNodeLocationQuery tree{MakeRec()
578 .set("window-1"
579 , MakeRec()
580 .type("persp-A")
581 .set("panelX"
582 , MakeRec()
583 .set("firstView", MakeRec())
584 .set("secondView", MakeRec())
585 )
586 )
587 .set("window-2"
588 , MakeRec()
589 .type("persp-B")
590 .set("panelY"
591 , MakeRec()
592 .set("thirdView"
593 , MakeRec()
594 .set("#1", MakeRec())
595 .set("#2", MakeRec())
596 )
597 )
598 )
599 .set("window-3"
600 , MakeRec()
601 .type("persp-C")
602 .set("panelZ"
603 , MakeRec()
604 .set("thirdView", MakeRec())
605 )
606 )
607 };
608
609 /* === explicitly given window spec remains unchanged === */
610 UICoordResolver r1 {UICoord{"window-2","persp-B","panelY"}, tree};
611 CHECK (3 == r1.coverDepth());
612 r1.anchor();
613 CHECK ("UI:window-2[persp-B]-panelY" == string(r1));
614
615 /* === `firstWindow` meta spec is resolved === */
616 UICoordResolver r2 {UICoord::firstWindow().view("blah"), tree};
617 CHECK (0 == r2.coverDepth());
618 CHECK (r2.isAnchored()); // can obviously be anchored, since there is always a first window
619 CHECK (not r2.canCover()); // yet this path is impossible to cover in the current UI
620 CHECK ("UI:firstWindow[*]-*.blah" == string(r2));
621 r2.anchor();
622 CHECK ("UI:window-1[*]-*.blah" == string(r2));
623 CHECK (0 == r2.coverDepth());
624 CHECK (not r2.canCover());
625
626 /* === `currentWindow` meta spec is resolved === */
627 UICoordResolver r3 {UICoord::currentWindow().view("thirdView"), tree};
628 CHECK (0 == r3.coverDepth());
629 CHECK (r3.isAnchored());
630 CHECK (not r3.isCovered());
631 CHECK (r3.canCover());
632 r3.anchor();
633 CHECK (not r3.isCovered());
634 CHECK (r3.isCoveredPartially());
635 CHECK (1 == r3.coverDepth()); // anchoring also picks the second of two possible solutions
636 CHECK ("UI:window-3[*]-*.thirdView" == string(r3)); // thereby covering the "thirdView"
637
638 /* === coverage solution is calculated on demand === */
639 UICoordResolver r4 {UICoord().view("thirdView").append("#2/sub"), tree};
640 CHECK ("UI:?.thirdView.#2/sub" == string(r4)); // an incomplete path is not automatically resolved
641 CHECK (not r4.isAnchored());
642 CHECK (0 == r4.coverDepth());
643 r4.anchor(); // but if we anchor, we force search for a coverage solution
644 CHECK (1 == r4.coverDepth()); // which is actually found starting from the second window,
645 CHECK (r4.isCoveredPartially()); // and kept in the internal cache for future use,
646 CHECK ("UI:window-2[*]-*.thirdView.#2/sub" == string(r4)); // but not made explicit, since we only requested anchorage
647
648 /* === already calculated coverage solution is used === */
649 UICoordResolver r5 {UICoord::currentWindow().view("thirdView"), tree};
650 CHECK (not r5.isCovered());
651 CHECK (not r5.isCoveredPartially());
652 CHECK (0 == r5.coverDepth());
653 CHECK (r5.canCover()); // this triggers search for a coverage solution
654 CHECK (1 == r5.coverDepth());
655 CHECK (not r5.isCovered());
656 CHECK (r5.isCoveredPartially());
657 CHECK ("UI:currentWindow[*]-*.thirdView" == string(r5));
658 r5.anchor(); // and this (cached) solution is also used to make anchorage explicit
659 CHECK ("UI:window-3[*]-*.thirdView" == string(r5));
660 CHECK (1 == r5.coverDepth());
661 CHECK (not r5.isCovered());
662 r5.cover(); // ...now also the coverage solution was made explicit
663 CHECK (r5.isCovered());
664 CHECK (4 == r5.coverDepth());
665 CHECK ("UI:window-3[persp-C]-panelZ.thirdView" == string(r5));
666
667 /* === impossible to cover and can not be anchored === */
668 UICoordResolver r6 {UICoord::window("windows").path("to/hell"), tree};
669 CHECK (not r6.isAnchored());
670 CHECK (not r6.canCover());
671 r6.anchor();
672 CHECK (not r6.isAnchored());
673 CHECK (0 == r6.coverDepth());
674 CHECK ("UI:windows[*]-*.*.*/to/hell" == string(r6));
675 }
676
677
688 void
690 {
691 GenNodeLocationQuery tree{MakeRec()
692 .set("window-2"
693 , MakeRec()
694 .type("persp-B")
695 .set("panelY"
696 , MakeRec()
697 .set("thirdView"
698 , MakeRec()
699 .set("#1", MakeRec())
700 .set("#2", MakeRec())
701 )
702 )
703 )
704 };
705
706 /* === extend fully covered explicit path === */
707 UICoordResolver r1 {UICoord{"window-2","persp-B","panelY"}, tree};
708 CHECK ("UI:window-2[persp-B]-panelY" == string(r1));
709 CHECK (r1.isCovered());
710 r1.extend (UICoord().path("gappy").tab(2)); // can extend with partially defined UI coordinates
711 CHECK ("UI:window-2[persp-B]-panelY.*.#2/gappy" == string(r1)); // ...the resulting UI path is unresolved, yet can be partially covered
712 r1.extend ("seamless"); // ...and this partial coverage is used as base for further extension
713 CHECK ("UI:window-2[persp-B]-panelY.thirdView.#2/seamless" ==string(r1));
714
715 /* === extend partially covered path === */
716 UICoordResolver r2 {UICoord().view("thirdView").append("some/where"), tree};
717 CHECK ("UI:?.thirdView.some/where" ==string(r2)); // "thirdView" is covered, "some/where" is not
718 r2.extend ("no/where");
719 CHECK ("UI:window-2[persp-B]-panelY.thirdView.no/where" ==string(r2)); // ...and thus the extension is attached behind "thirdView"
720 CHECK (r2.isCoveredPartially());
721
722 /* === impossible extensions rejected === */ // since r2 already specifies a perspective ("persp-B")....
723 VERIFY_ERROR (INVALID, r2.extend(UICoord().persp("fisheye"))); // ...overwriting with another perspective is rejected as extension
724 CHECK ("UI:window-2[persp-B]-panelY.thirdView.no/where" ==string(r2)); // ...and the existing state is unaffected from this error
725 VERIFY_ERROR (INVALID, r2.extend(UICoord().view("alternative"))); // Likewise, extending with a conflicting view spec is rejected
726 r2.extend(UICoord().tab("nada")); // But a tab is not yet covered and thus acceptable as extension
727 CHECK ("UI:window-2[persp-B]-panelY.thirdView.nada" ==string(r2));
728 r2.extend(UICoord());
729 CHECK ("UI:window-2[persp-B]-panelY.thirdView" ==string(r2)); // empty coordinates implicitly attached behind the covered part
730
731 /* === unsolvable: truncate, extend, recalculate coverage === */
732 UICoordResolver r3 {UICoord().persp("awesome"), tree};
733 CHECK (not r3.canCover());
734 CHECK (0 == r3.coverDepth());
735 r3.extend (UICoord::currentWindow().tab(1)); // Extension implies covering, which effectively truncates the path
736 CHECK (1 == r3.coverDepth()); // ...and "currentWindow" can even be covered, thus the coverage increases
737 CHECK ("UI:currentWindow[*]-*.*.#1" ==string(r3)); // note coverage calculated internally, not made explicit
738 }
739 };
740
741
744
745
746}}} // namespace stage::interact::test
Token or Atom with distinct identity.
Definition symbol.hpp:120
static Symbol BOTTOM
Definition symbol.hpp:124
Mutator && set(string const &key, X &&content)
Definition record.hpp:462
object-like record of data.
Definition record.hpp:142
Test/Diagnostics: implementation of the LocationQuery-API based on a abstract topological structure g...
decltype(TreeStructureNavigator::buildIterator(0)) ChildIter
Query and mutate UICoord specifications in relation to actual UI topology.
Builder && view(Literal viewID)
augment UI coordinates to indicate a specific view to be used
Definition ui-coord.hpp:555
Builder && tab(Literal tabID)
augment UI coordinates to indicate a specific tab within the view"
Definition ui-coord.hpp:563
Builder && path(Literal pathDef)
augment UI coordinates to define a complete local path
Definition ui-coord.hpp:617
Builder && panel(Literal panelID)
augment UI coordinates to indicate a specific view to be used
Definition ui-coord.hpp:547
Builder && persp(Literal perspectiveID)
augment UI coordinates to mandate a specific perspective to be active within the window
Definition ui-coord.hpp:539
Builder && append(Literal elm)
augment UI coordinates by appending a further component at the end.
Definition ui-coord.hpp:592
Describe a location within the UI through structural/topological coordinates.
Definition ui-coord.hpp:131
static Builder window(Literal windowID)
Builder: start definition of UI-Coordinates rooted in given window.
Definition ui-coord.hpp:683
static Builder firstWindow()
Builder: start definition of UI-Coordinates rooted in the firstWindow
Definition ui-coord.hpp:676
Builder tab(Literal tabID) const
Definition ui-coord.hpp:714
Builder persp(Literal perspectiveID) const
Definition ui-coord.hpp:696
Builder panel(Literal panelID) const
Definition ui-coord.hpp:702
Builder view(Literal viewID) const
Definition ui-coord.hpp:708
static Builder currentWindow()
Builder: start definition of UI-Coordinates rooted in the currentWindow
Definition ui-coord.hpp:670
#define LERR_(_NAME_)
Definition error.hpp:45
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Implementation of the stage::interact::LocationQuery interface to work on a GenNode tree.
Generic building block for tree shaped (meta)data structures.
const Symbol UIC_ELIDED
indicate that a component is elided or irrelevant here
Lumiera GTK UI implementation root.
Definition guifacade.cpp:37
Test runner and basic definitions for tests.
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
A collection of frequently used helper functions to support unit testing.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Evaluation of UI coordinates against a concrete window topology.
A topological addressing scheme to designate structural locations within the UI.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...