Branch data Line data Source code
1 : : #include "test_librational_all.h"
2 : :
3 : : /* Counts side-effect helper calls in SKIP macro tests */
4 : : static unsigned int counted_return_calls = 0U;
5 : :
6 : : /**
7 : : * @brief Return a positive check-function answer
8 : : *
9 : : * @return SUCCESS with YES marked as pending by provide()
10 : : */
11 : 8 : static Return return_yes(void)
12 : : {
13 : : /* Status returned by this function through provide()
14 : : Default value assumes successful completion */
15 : 8 : Return status = SUCCESS;
16 : :
17 : 8 : status |= YES;
18 : :
19 : 8 : provide(status);
20 : : }
21 : :
22 : : /**
23 : : * @brief Return a negative check-function answer
24 : : *
25 : : * @return SUCCESS with NO marked as pending by provide()
26 : : */
27 : 3 : static Return return_no(void)
28 : : {
29 : : /* Status returned by this function through provide()
30 : : Default value assumes successful completion */
31 : 3 : Return status = SUCCESS;
32 : :
33 : 3 : status |= NO;
34 : :
35 : 3 : provide(status);
36 : : }
37 : :
38 : : /**
39 : : * @brief Return conflicting binary answers
40 : : *
41 : : * Used to verify that normalization keeps NO stronger than YES
42 : : *
43 : : * @return SUCCESS with normalized NO and AWAITING flags
44 : : */
45 : 1 : static Return return_yes_and_no(void)
46 : : {
47 : : /* Status returned by this function through provide()
48 : : Default value assumes successful completion */
49 : 1 : Return status = SUCCESS;
50 : :
51 : 1 : status |= YES | NO;
52 : :
53 : 1 : provide(status);
54 : : }
55 : :
56 : : /**
57 : : * @brief Return a technical failure mixed with a positive answer
58 : : *
59 : : * @return FAILURE with YES marked as pending by provide()
60 : : */
61 : 1 : static Return return_failure_with_yes(void)
62 : : {
63 : : /* Status returned by this function through provide()
64 : : Default value assumes successful completion */
65 : 1 : Return status = SUCCESS;
66 : :
67 : 1 : status = FAILURE | YES;
68 : :
69 : 1 : provide(status);
70 : : }
71 : :
72 : : /**
73 : : * @brief Return success while global_return_status contains conflicting flags
74 : : *
75 : : * @return SUCCESS merged with allowed normalized global flags
76 : : */
77 : 1 : static Return return_success_with_conflicting_global_status(void)
78 : : {
79 : : /* Status returned by this function through provide()
80 : : Default value assumes successful completion */
81 : 1 : Return status = SUCCESS;
82 : :
83 : 1 : atomic_store(&global_return_status,SUCCESS | FAILURE | YES | NO | INFO | HALTED);
84 : :
85 : 1 : provide(status);
86 : : }
87 : :
88 : : /**
89 : : * @brief Try to exit through provide() with an already pending answer
90 : : *
91 : : * @return FAILURE because provide() must reject unhandled pending answers
92 : : */
93 : 1 : static Return return_unhandled_provide(void)
94 : : {
95 : 1 : provide(SUCCESS | YES | AWAITING);
96 : : }
97 : :
98 : : /**
99 : : * @brief Try to exit through deliver() with an already pending answer
100 : : *
101 : : * @return FAILURE because deliver() must reject unhandled pending answers
102 : : */
103 : 1 : static Return return_unhandled_deliver(void)
104 : : {
105 : 1 : deliver(SUCCESS | YES | AWAITING);
106 : : }
107 : :
108 : : /**
109 : : * @brief Return success and record that the expression was evaluated
110 : : *
111 : : * @return SUCCESS after incrementing counted_return_calls
112 : : */
113 : 1 : static Return return_success_and_count_call(void)
114 : : {
115 : : /* Status returned by this function through provide()
116 : : Default value assumes successful completion */
117 : 1 : Return status = SUCCESS;
118 : :
119 : 1 : counted_return_calls++;
120 : :
121 : 1 : provide(status);
122 : : }
123 : :
124 : : /**
125 : : * @brief Return YES and record that the expression was evaluated
126 : : *
127 : : * @return SUCCESS with YES marked as pending after incrementing counted_return_calls
128 : : */
129 : 1 : static Return return_yes_and_count_call(void)
130 : : {
131 : : /* Status returned by this function through provide()
132 : : Default value assumes successful completion */
133 : 1 : Return status = SUCCESS;
134 : :
135 : 1 : counted_return_calls++;
136 : 1 : status |= YES;
137 : :
138 : 1 : provide(status);
139 : : }
140 : :
141 : : /**
142 : : * @brief Check that provide() marks fresh YES and NO answers as pending
143 : : *
144 : : * @return Return describing success or failure
145 : : */
146 : 1 : static Return test_librational_0005_1(void)
147 : : {
148 : 1 : INITTEST;
149 : :
150 : 1 : atomic_store(&global_return_status,OK);
151 : :
152 : : /* Fresh yes/no answers must be marked pending so callers cannot ignore them accidentally */
153 : 1 : Return yes_status = return_yes();
154 : 1 : ASSERT(SUCCESS & yes_status);
155 : 1 : ASSERT(YES & yes_status);
156 : 1 : ASSERT((NO & yes_status) == 0);
157 : 1 : ASSERT(AWAITING & yes_status);
158 : :
159 : 1 : Return no_status = return_no();
160 : 1 : ASSERT(SUCCESS & no_status);
161 : 1 : ASSERT((YES & no_status) == 0);
162 : 1 : ASSERT(NO & no_status);
163 : 1 : ASSERT(AWAITING & no_status);
164 : :
165 : 1 : Return normalized_answer = return_yes_and_no();
166 : 1 : ASSERT(SUCCESS & normalized_answer);
167 : 1 : ASSERT((YES & normalized_answer) == 0);
168 : 1 : ASSERT(NO & normalized_answer);
169 : 1 : ASSERT(AWAITING & normalized_answer);
170 : :
171 : 1 : RETURN_STATUS;
172 : : }
173 : :
174 : : /**
175 : : * @brief Check that ask() consumes direct yes/no check calls
176 : : *
177 : : * @return Return describing success or failure
178 : : */
179 : 1 : static Return test_librational_0005_2(void)
180 : : {
181 : 1 : INITTEST;
182 : :
183 : 1 : atomic_store(&global_return_status,OK);
184 : :
185 : : /* ask() converts the binary answer to bool and clears the pending answer from status */
186 : 1 : bool answer = ask(return_yes());
187 : 1 : ASSERT(answer == true);
188 : 1 : ASSERT(status == SUCCESS);
189 : :
190 : 1 : answer = ask(return_no());
191 : 1 : ASSERT(answer == false);
192 : 1 : ASSERT(status == SUCCESS);
193 : :
194 : 1 : RETURN_STATUS;
195 : : }
196 : :
197 : : /**
198 : : * @brief Check that ask() consumes stored and assigned pending answers
199 : : *
200 : : * @return Return describing success or failure
201 : : */
202 : 1 : static Return test_librational_0005_3(void)
203 : : {
204 : 1 : INITTEST;
205 : :
206 : 1 : atomic_store(&global_return_status,OK);
207 : :
208 : : /* A pending answer can be consumed after it has already been stored in status */
209 : 1 : status = return_yes();
210 : :
211 : 1 : bool answer = ask(status);
212 : 1 : ASSERT(answer == true);
213 : 1 : ASSERT(status == SUCCESS);
214 : :
215 : 1 : answer = ask(status = return_no());
216 : 1 : ASSERT(answer == false);
217 : 1 : ASSERT(status == SUCCESS);
218 : :
219 : 1 : RETURN_STATUS;
220 : : }
221 : :
222 : : /**
223 : : * @brief Check that technical failure stays stronger than a YES answer
224 : : *
225 : : * @return Return describing success or failure
226 : : */
227 : 1 : static Return test_librational_0005_4(void)
228 : : {
229 : 1 : INITTEST;
230 : :
231 : 1 : atomic_store(&global_return_status,OK);
232 : :
233 : : /* Preserve the critical status produced by ask(), then restore the test runner status */
234 : 1 : bool answer = ask(return_failure_with_yes());
235 : 1 : Return observed_status = status;
236 : 1 : status = SUCCESS;
237 : :
238 : 1 : ASSERT(answer == false);
239 : 1 : ASSERT(FAILURE & observed_status);
240 : 1 : ASSERT((SUCCESS & observed_status) == 0);
241 : :
242 : 1 : RETURN_STATUS;
243 : : }
244 : :
245 : : /**
246 : : * @brief Trigger run() and call() with yes/no functions for stderr capture
247 : : *
248 : : * @return Return describing whether both contract violations were detected
249 : : */
250 : 1 : static Return capture_librational_0005_5(void)
251 : : {
252 : 1 : Return status = SUCCESS;
253 : 1 : Return test_status = SUCCESS;
254 : :
255 : 1 : atomic_store(&global_return_status,OK);
256 : :
257 : : /* run() is for regular work functions, not for yes/no check functions */
258 : 1 : run(return_yes());
259 : :
260 [ - + ]: 1 : if((CRITICAL & status) == 0)
261 : : {
262 : 0 : test_status = FAILURE;
263 : : }
264 : :
265 : 1 : status = SUCCESS;
266 : :
267 : : /* call() is also required to reject a pending yes/no answer from its callee */
268 : 1 : call(return_yes());
269 : :
270 [ - + ]: 1 : if((CRITICAL & status) == 0)
271 : : {
272 : 0 : test_status = FAILURE;
273 : : }
274 : :
275 : 1 : deliver(test_status);
276 : : }
277 : :
278 : : /**
279 : : * @brief Check diagnostics when run() and call() receive yes/no functions
280 : : *
281 : : * @return Return describing success or failure
282 : : */
283 : 1 : static Return test_librational_0005_5(void)
284 : : {
285 : 1 : INITTEST;
286 : :
287 : : /* The exact stderr text is part of the developer-facing contract */
288 : : static const char expected_stderr_pattern[] =
289 : : "\\A"
290 : : "ERROR: run\\(\\) received an unhandled yes/no Return answer\\. Use ask\\(\\)\n"
291 : : "ERROR: call\\(\\) received an unhandled yes/no Return answer\\. Use ask\\(\\)\\Z";
292 : :
293 : 1 : ASSERT(SUCCESS == match_function_output(
294 : : expected_stderr_pattern,
295 : : NULL,
296 : : capture_librational_0005_5));
297 : :
298 : 1 : RETURN_STATUS;
299 : : }
300 : :
301 : : /**
302 : : * @brief Trigger run() and call() while local status already has a pending answer
303 : : *
304 : : * @return Return describing whether both contract violations were detected
305 : : */
306 : 1 : static Return capture_librational_0005_6(void)
307 : : {
308 : 1 : Return status = SUCCESS;
309 : 1 : Return test_status = SUCCESS;
310 : :
311 : 1 : atomic_store(&global_return_status,OK);
312 : :
313 : : /* A pending answer in status must stop run() before its argument is evaluated */
314 : 1 : status = return_yes();
315 : 1 : run(return_yes());
316 : :
317 [ - + ]: 1 : if((CRITICAL & status) == 0)
318 : : {
319 : 0 : test_status = FAILURE;
320 : : }
321 : :
322 : : /* call() has the same pending-answer guard, but it does not use the SKIP gate */
323 : 1 : status = return_yes();
324 : 1 : call(return_yes());
325 : :
326 [ - + ]: 1 : if((CRITICAL & status) == 0)
327 : : {
328 : 0 : test_status = FAILURE;
329 : : }
330 : :
331 : 1 : deliver(test_status);
332 : : }
333 : :
334 : : /**
335 : : * @brief Check diagnostics for previous unhandled yes/no answers
336 : : *
337 : : * @return Return describing success or failure
338 : : */
339 : 1 : static Return test_librational_0005_6(void)
340 : : {
341 : 1 : INITTEST;
342 : :
343 : : /* A previous pending answer must be consumed before regular flow continues */
344 : : static const char expected_stderr_pattern[] =
345 : : "\\A"
346 : : "ERROR: Unhandled yes/no Return answer before run\\(\\)\n"
347 : : "ERROR: Unhandled yes/no Return answer before call\\(\\)\\Z";
348 : :
349 : 1 : ASSERT(SUCCESS == match_function_output(
350 : : expected_stderr_pattern,
351 : : NULL,
352 : : capture_librational_0005_6));
353 : :
354 : 1 : RETURN_STATUS;
355 : : }
356 : :
357 : : /**
358 : : * @brief Trigger provide() and deliver() with an already pending answer
359 : : *
360 : : * @return Return describing whether both exits reject the pending answer
361 : : */
362 : 1 : static Return capture_librational_0005_7(void)
363 : : {
364 : 1 : INITTEST;
365 : :
366 : 1 : atomic_store(&global_return_status,OK);
367 : :
368 : : /* Function exits must not leak an unconsumed yes/no answer to their caller */
369 : 1 : ASSERT(FAILURE == return_unhandled_provide());
370 : 1 : ASSERT(FAILURE == return_unhandled_deliver());
371 : :
372 : 1 : deliver(status);
373 : : }
374 : :
375 : : /**
376 : : * @brief Check diagnostics for unhandled pending answers at function exit
377 : : *
378 : : * @return Return describing success or failure
379 : : */
380 : 1 : static Return test_librational_0005_7(void)
381 : : {
382 : 1 : INITTEST;
383 : :
384 : : /* provide() and deliver() intentionally reject the same misuse with different macro names */
385 : : static const char expected_stderr_pattern[] =
386 : : "\\A"
387 : : "ERROR: Unhandled yes/no Return answer before provide\\(\\)\n"
388 : : "ERROR: Unhandled yes/no Return answer before deliver\\(\\)\\Z";
389 : :
390 : 1 : ASSERT(SUCCESS == match_function_output(
391 : : expected_stderr_pattern,
392 : : NULL,
393 : : capture_librational_0005_7));
394 : :
395 : 1 : RETURN_STATUS;
396 : : }
397 : :
398 : : /**
399 : : * @brief Trigger ask() with raw YES flags that were not returned by provide()
400 : : *
401 : : * @return Return describing whether the misuse was detected
402 : : */
403 : 1 : static Return capture_librational_0005_8(void)
404 : : {
405 : 1 : Return status = SUCCESS;
406 : 1 : Return test_status = SUCCESS;
407 : :
408 : 1 : atomic_store(&global_return_status,OK);
409 : :
410 : : /* A raw YES flag is not enough because ask() requires the AWAITING marker */
411 : 1 : bool answer = ask(SUCCESS | YES);
412 : :
413 [ - + ]: 1 : if(answer == true)
414 : : {
415 : 0 : test_status = FAILURE;
416 : : }
417 : :
418 [ - + ]: 1 : if((CRITICAL & status) == 0)
419 : : {
420 : 0 : test_status = FAILURE;
421 : : }
422 : :
423 : 1 : deliver(test_status);
424 : : }
425 : :
426 : : /**
427 : : * @brief Check that ask() rejects raw yes/no flags without AWAITING
428 : : *
429 : : * @return Return describing success or failure
430 : : */
431 : 1 : static Return test_librational_0005_8(void)
432 : : {
433 : 1 : INITTEST;
434 : :
435 : : /* Keep the function name flexible because __func__ is intentionally part of the diagnostic */
436 : : static const char expected_stderr_pattern[] =
437 : : "\\A"
438 : : "ERROR: [A-Za-z0-9_]+:\\d+ ask\\(\\) expected a yes/no Return answer\\Z";
439 : :
440 : 1 : ASSERT(SUCCESS == match_function_output(
441 : : expected_stderr_pattern,
442 : : NULL,
443 : : capture_librational_0005_8));
444 : :
445 : 1 : RETURN_STATUS;
446 : : }
447 : :
448 : : /**
449 : : * @brief Check that an explicit void cast discards a whole Return value
450 : : *
451 : : * @return Return describing success or failure
452 : : */
453 : 1 : static Return test_librational_0005_9(void)
454 : : {
455 : 1 : INITTEST;
456 : :
457 : 1 : atomic_store(&global_return_status,OK);
458 : :
459 : : /* This documents the explicit escape hatch for callers that really ignore the whole Return */
460 : 1 : (void)return_yes();
461 : :
462 : 1 : ASSERT(status == SUCCESS);
463 : :
464 : 1 : RETURN_STATUS;
465 : : }
466 : :
467 : : /**
468 : : * @brief Check global_return_status normalization and propagation
469 : : *
470 : : * @return Return describing success or failure
471 : : */
472 : 1 : static Return test_librational_0005_10(void)
473 : : {
474 : 1 : INITTEST;
475 : :
476 : 1 : atomic_store(&global_return_status,OK);
477 : :
478 : : /* Only GLOBAL bits may propagate from process-wide status into the local return */
479 : 1 : Return returned_status = return_success_with_conflicting_global_status();
480 : 1 : Return stored_global_status = atomic_load(&global_return_status);
481 : :
482 : 1 : ASSERT(SUCCESS & returned_status);
483 : 1 : ASSERT(INFO & returned_status);
484 : 1 : ASSERT(HALTED & returned_status);
485 : 1 : ASSERT((FAILURE & returned_status) == 0);
486 : 1 : ASSERT((YES & returned_status) == 0);
487 : 1 : ASSERT((NO & returned_status) == 0);
488 : :
489 : 1 : ASSERT(FAILURE & stored_global_status);
490 : 1 : ASSERT(NO & stored_global_status);
491 : 1 : ASSERT(INFO & stored_global_status);
492 : 1 : ASSERT(HALTED & stored_global_status);
493 : 1 : ASSERT((SUCCESS & stored_global_status) == 0);
494 : 1 : ASSERT((YES & stored_global_status) == 0);
495 : :
496 : : /* Reset global state so later tests do not inherit this intentionally dirty setup */
497 : 1 : atomic_store(&global_return_status,OK);
498 : :
499 : 1 : RETURN_STATUS;
500 : : }
501 : :
502 : : /**
503 : : * @brief Trigger ask() with AWAITING but without YES or NO
504 : : *
505 : : * @return Return describing whether the misuse was detected
506 : : */
507 : 1 : static Return capture_librational_0005_11(void)
508 : : {
509 : 1 : Return status = SUCCESS;
510 : 1 : Return test_status = SUCCESS;
511 : :
512 : 1 : atomic_store(&global_return_status,OK);
513 : :
514 : : /* A pending marker without a binary answer is malformed and must be rejected */
515 : 1 : status = SUCCESS | AWAITING;
516 : 1 : bool answer = ask(status);
517 : :
518 [ - + ]: 1 : if(answer == true)
519 : : {
520 : 0 : test_status = FAILURE;
521 : : }
522 : :
523 [ - + ]: 1 : if((CRITICAL & status) == 0)
524 : : {
525 : 0 : test_status = FAILURE;
526 : : }
527 : :
528 : 1 : deliver(test_status);
529 : : }
530 : :
531 : : /**
532 : : * @brief Check that ask() rejects pending answers without YES or NO
533 : : *
534 : : * @return Return describing success or failure
535 : : */
536 : 1 : static Return test_librational_0005_11(void)
537 : : {
538 : 1 : INITTEST;
539 : :
540 : : /* Keep the source location flexible while matching the diagnostic text strictly */
541 : : static const char expected_stderr_pattern[] =
542 : : "\\A"
543 : : "ERROR: [A-Za-z0-9_]+:\\d+ ask\\(\\) received a pending Return answer without YES or NO\\Z";
544 : :
545 : 1 : ASSERT(SUCCESS == match_function_output(
546 : : expected_stderr_pattern,
547 : : NULL,
548 : : capture_librational_0005_11));
549 : :
550 : 1 : RETURN_STATUS;
551 : : }
552 : :
553 : : /**
554 : : * @brief Trigger rational_ask() with missing caller status storage
555 : : *
556 : : * @return Return describing whether NULL status storage was rejected
557 : : */
558 : 1 : static Return capture_librational_0005_12(void)
559 : : {
560 : 1 : Return status = SUCCESS;
561 : :
562 : 1 : atomic_store(&global_return_status,OK);
563 : :
564 : : /* Directly call the implementation helper to cover the NULL storage guard */
565 : 1 : bool answer = rational_ask(NULL,SUCCESS | YES | AWAITING,__func__,__LINE__);
566 : :
567 [ - + ]: 1 : if(answer == true)
568 : : {
569 : 0 : status = FAILURE;
570 : : }
571 : :
572 : 1 : deliver(status);
573 : : }
574 : :
575 : : /**
576 : : * @brief Check that rational_ask() rejects NULL status storage
577 : : *
578 : : * @return Return describing success or failure
579 : : */
580 : 1 : static Return test_librational_0005_12(void)
581 : : {
582 : 1 : INITTEST;
583 : :
584 : : /* Keep the source location flexible while matching the diagnostic text strictly */
585 : : static const char expected_stderr_pattern[] =
586 : : "\\A"
587 : : "ERROR: [A-Za-z0-9_]+:\\d+ ask\\(\\) received NULL status storage\\Z";
588 : :
589 : 1 : ASSERT(SUCCESS == match_function_output(
590 : : expected_stderr_pattern,
591 : : NULL,
592 : : capture_librational_0005_12));
593 : :
594 : 1 : RETURN_STATUS;
595 : : }
596 : :
597 : : /**
598 : : * @brief Check human-readable status text for known, unknown, and mixed flags
599 : : *
600 : : * @return Return describing success or failure
601 : : */
602 : 1 : static Return test_librational_0005_13(void)
603 : : {
604 : 1 : INITTEST;
605 : :
606 : : /* Pin the exact flag order because diagnostics and tests compare this string directly */
607 : 1 : ASSERT(0 == strcmp(show_status(OK),"OK"));
608 : 1 : ASSERT(0 == strcmp(show_status((Return)0x8000u),"UNKNOWN"));
609 : 1 : ASSERT(0 == strcmp(show_status(FAILURE | (Return)0x8000u),"FAILURE"));
610 : 1 : ASSERT(0 == strcmp(
611 : : show_status(FAILURE | SUCCESS | HALTED | WARNING | DONOTHING | INFO | YES | NO | AWAITING),
612 : : "FAILURE|SUCCESS|HALTED|WARNING|DONOTHING|INFO|YES|NO|AWAITING"));
613 : :
614 : 1 : RETURN_STATUS;
615 : : }
616 : :
617 : : #ifndef EVIL_EMPIRE_OS
618 : : /**
619 : : * @brief Check show_status() fallback behavior for snprintf problems
620 : : *
621 : : * @return Return describing success or failure
622 : : */
623 : 1 : static Return test_librational_0005_14(void)
624 : : {
625 : 1 : INITTEST;
626 : :
627 : : /* A formatting failure must fall back to UNKNOWN instead of exposing partial buffer state */
628 : 1 : testmocking_snprintf_fail_next(1);
629 : 1 : const char *failed_format_status = show_status(FAILURE);
630 : 1 : testmocking_snprintf_disable();
631 : :
632 : 1 : ASSERT(0 == strcmp(failed_format_status,"UNKNOWN"));
633 : :
634 : : /* A truncated write must still leave a terminated static buffer */
635 : 1 : testmocking_snprintf_truncate_next(1);
636 : 1 : const char *truncated_format_status = show_status(FAILURE);
637 : 1 : testmocking_snprintf_disable();
638 : :
639 : 1 : ASSERT(0 == strcmp(truncated_format_status,"T"));
640 : :
641 : 1 : RETURN_STATUS;
642 : : }
643 : : #endif
644 : :
645 : : /**
646 : : * @brief Check local conflict resolution in rational_normalize_return()
647 : : *
648 : : * @return Return describing success or failure
649 : : */
650 : 1 : static Return test_librational_0005_15(void)
651 : : {
652 : 1 : INITTEST;
653 : :
654 : 1 : atomic_store(&global_return_status,OK);
655 : :
656 : : /* Critical technical bits remove SUCCESS from the normalized result */
657 : 1 : Return normalized_status = rational_normalize_return(SUCCESS | FAILURE);
658 : 1 : ASSERT(FAILURE & normalized_status);
659 : 1 : ASSERT((SUCCESS & normalized_status) == 0);
660 : :
661 : 1 : normalized_status = rational_normalize_return(SUCCESS | WARNING);
662 : 1 : ASSERT(WARNING & normalized_status);
663 : 1 : ASSERT((SUCCESS & normalized_status) == 0);
664 : :
665 : : /* NO is the dominant binary answer when both local answer flags are present */
666 : 1 : normalized_status = rational_normalize_return(SUCCESS | YES | NO);
667 : 1 : ASSERT(SUCCESS & normalized_status);
668 : 1 : ASSERT(NO & normalized_status);
669 : 1 : ASSERT((YES & normalized_status) == 0);
670 : :
671 : 1 : RETURN_STATUS;
672 : : }
673 : :
674 : : /**
675 : : * @brief Check that global yes/no flags stay local to global_return_status
676 : : *
677 : : * @return Return describing success or failure
678 : : */
679 : 1 : static Return test_librational_0005_16(void)
680 : : {
681 : 1 : INITTEST;
682 : :
683 : : /* Only GLOBAL bits should reach the returned status from global_return_status */
684 : 1 : atomic_store(&global_return_status,SUCCESS | INFO | YES | NO | AWAITING);
685 : :
686 : 1 : Return normalized_status = rational_normalize_return(SUCCESS);
687 : 1 : Return stored_global_status = atomic_load(&global_return_status);
688 : :
689 : 1 : ASSERT(SUCCESS & normalized_status);
690 : 1 : ASSERT(INFO & normalized_status);
691 : 1 : ASSERT((YES & normalized_status) == 0);
692 : 1 : ASSERT((NO & normalized_status) == 0);
693 : 1 : ASSERT((AWAITING & normalized_status) == 0);
694 : :
695 : 1 : ASSERT(SUCCESS & stored_global_status);
696 : 1 : ASSERT(INFO & stored_global_status);
697 : 1 : ASSERT(NO & stored_global_status);
698 : 1 : ASSERT(AWAITING & stored_global_status);
699 : 1 : ASSERT((YES & stored_global_status) == 0);
700 : :
701 : : /* Reset global state so later tests do not inherit the deliberately unusual flags */
702 : 1 : atomic_store(&global_return_status,OK);
703 : :
704 : 1 : RETURN_STATUS;
705 : : }
706 : :
707 : : /**
708 : : * @brief Check that ask() does not evaluate a new expression while status has SKIP
709 : : *
710 : : * @return Return describing success or failure
711 : : */
712 : 1 : static Return test_librational_0005_17(void)
713 : : {
714 : 1 : INITTEST;
715 : :
716 : 1 : atomic_store(&global_return_status,OK);
717 : 1 : counted_return_calls = 0U;
718 : :
719 : : /* First prove that the helper is callable and increments the counter in normal ask() flow */
720 : 1 : bool direct_answer = ask(return_yes_and_count_call());
721 : 1 : ASSERT(direct_answer == true);
722 : 1 : ASSERT(counted_return_calls == 1U);
723 : 1 : ASSERT(status == SUCCESS);
724 : :
725 : : /* With SKIP set and no pending answer, ask() must short-circuit before evaluating its argument */
726 : 1 : counted_return_calls = 0U;
727 : 1 : status = FAILURE;
728 : 1 : bool answer = ask(return_yes_and_count_call());
729 : 1 : Return skipped_status = status;
730 : 1 : status = SUCCESS;
731 : :
732 : 1 : ASSERT(answer == false);
733 : 1 : ASSERT(counted_return_calls == 0U);
734 : 1 : ASSERT(FAILURE & skipped_status);
735 : 1 : ASSERT((AWAITING & skipped_status) == 0);
736 : :
737 : 1 : RETURN_STATUS;
738 : : }
739 : :
740 : : /**
741 : : * @brief Check that run() does not execute its function while status has SKIP
742 : : *
743 : : * @return Return describing success or failure
744 : : */
745 : 1 : static Return test_librational_0005_18(void)
746 : : {
747 : 1 : INITTEST;
748 : :
749 : 1 : atomic_store(&global_return_status,OK);
750 : 1 : counted_return_calls = 0U;
751 : :
752 : : /* run() is gated by SKIP, so the work expression must not be evaluated */
753 : 1 : status = FAILURE;
754 : 1 : run(return_success_and_count_call());
755 : 1 : Return skipped_status = status;
756 : 1 : status = SUCCESS;
757 : :
758 : 1 : ASSERT(counted_return_calls == 0U);
759 : 1 : ASSERT(FAILURE & skipped_status);
760 : :
761 : 1 : RETURN_STATUS;
762 : : }
763 : :
764 : : /**
765 : : * @brief Check that call() still executes its function while status has SKIP
766 : : *
767 : : * @return Return describing success or failure
768 : : */
769 : 1 : static Return test_librational_0005_19(void)
770 : : {
771 : 1 : INITTEST;
772 : :
773 : 1 : atomic_store(&global_return_status,OK);
774 : 1 : counted_return_calls = 0U;
775 : :
776 : : /* call() is for mandatory cleanup/final actions, so SKIP must not suppress it */
777 : 1 : status = FAILURE;
778 : 1 : call(return_success_and_count_call());
779 : 1 : Return accumulated_status = status;
780 : 1 : status = SUCCESS;
781 : :
782 : 1 : ASSERT(counted_return_calls == 1U);
783 : 1 : ASSERT(FAILURE & accumulated_status);
784 : 1 : ASSERT((SUCCESS & accumulated_status) == 0);
785 : :
786 : 1 : RETURN_STATUS;
787 : : }
788 : :
789 : : /**
790 : : * @brief Exercise yes/no Return handling in provide, deliver, ask, run, and call
791 : : *
792 : : * The suite verifies the contract for check functions that return YES or NO,
793 : : * including pending-answer marking, ask() consumption, regular-work macro
794 : : * rejection, function-exit guards, SKIP behavior, global_return_status
795 : : * propagation, rational_normalize_return() conflicts, and show_status()
796 : : * formatting. The diagnostics are checked through captured stderr when the
797 : : * expected behavior is an intentional contract violation
798 : : *
799 : : * @return Return describing success or failure
800 : : */
801 : 1 : Return test_librational_0005(void)
802 : : {
803 : 1 : INITTEST;
804 : :
805 : 1 : TEST(test_librational_0005_1,"provide() marks fresh yes/no answers as pending");
806 : 1 : TEST(test_librational_0005_2,"ask() consumes direct yes/no check calls");
807 : 1 : TEST(test_librational_0005_3,"ask() consumes stored and assigned yes/no Return values");
808 : 1 : TEST(test_librational_0005_4,"ask() keeps technical failure stronger than a yes answer");
809 : 1 : TEST(test_librational_0005_5,"run() and call() reject yes/no check functions");
810 : 1 : TEST(test_librational_0005_6,"run() and call() reject previous unhandled yes/no answers");
811 : 1 : TEST(test_librational_0005_7,"provide() and deliver() reject unhandled yes/no answers");
812 : 1 : TEST(test_librational_0005_8,"ask() rejects raw yes/no flags without function-return marking");
813 : 1 : TEST(test_librational_0005_9,"void cast intentionally discards the whole Return value");
814 : 1 : TEST(test_librational_0005_10,"global_return_status is normalized and only global bits propagate");
815 : 1 : TEST(test_librational_0005_11,"ask() rejects pending answers without YES or NO");
816 : 1 : TEST(test_librational_0005_12,"rational_ask() rejects NULL status storage");
817 : 1 : TEST(test_librational_0005_13,"show_status() reports OK, known, unknown and mixed flags");
818 : : #ifndef EVIL_EMPIRE_OS
819 : 1 : TEST(test_librational_0005_14,"show_status() tolerates snprintf failure and truncation");
820 : : #endif
821 : 1 : TEST(test_librational_0005_15,"rational_normalize_return() resolves local flag conflicts");
822 : 1 : TEST(test_librational_0005_16,"rational_normalize_return() keeps global yes/no flags local");
823 : 1 : TEST(test_librational_0005_17,"ask() skips new expressions when local status already has SKIP");
824 : 1 : TEST(test_librational_0005_18,"run() skips work functions when local status already has SKIP");
825 : 1 : TEST(test_librational_0005_19,"call() still executes mandatory functions when local status has SKIP");
826 : :
827 : 1 : RETURN_STATUS;
828 : : }
|