1#include "nightmare/internal.h"
2#include <sch/sched.h>
3#include <test/test.h>
4#include <thread/thread.h>
5
6#if defined(TEST_ENABLED) && defined(TEST_NIGHTMARE_SMOKE)
7TEST_GROUP_DECLARE(nightmare_harness);
8
9TEST_DECLARE_UNIT(nightmare_harness, perturb_verdict_mailbox) {
10 char reason[] = "first_reason";
11 char msg[] = "first message";
12 struct nightmare_verdict first = NIGHTMARE_FAIL(reason, msg);
13
14 atomic_store_explicit(&nightmare_runtime.perturb_verdict_ready, false,
15 memory_order_relaxed);
16 TEST_ASSERT(!nightmare_load_perturb_verdict(&first));
17
18 nightmare_publish_perturb_verdict(NIGHTMARE_FAIL(reason, msg));
19 reason[0] = 'X';
20 msg[0] = 'X';
21 nightmare_publish_perturb_verdict(
22 NIGHTMARE_FAIL("second_reason", "second message"));
23
24 struct nightmare_verdict loaded;
25 TEST_ASSERT(nightmare_load_perturb_verdict(&loaded));
26 TEST_ASSERT_EQ(loaded.result, NIGHTMARE_RESULT_FAIL);
27 TEST_ASSERT_STR_EQ(loaded.reason, "first_reason");
28 TEST_ASSERT_STR_EQ(loaded.msg, "first message");
29
30 atomic_store_explicit(&nightmare_runtime.perturb_verdict_ready, false,
31 memory_order_relaxed);
32 return TEST_SUCCESS;
33}
34
35TEST_DECLARE_UNIT(nightmare_harness, stop_priority) {
36 atomic_store_explicit(&nightmare_runtime.stop, NM_RUN,
37 memory_order_relaxed);
38
39 nightmare_publish_stop(reason: NM_STOP_BUDGET);
40 TEST_ASSERT_EQ(atomic_load(&nightmare_runtime.stop), NM_STOP_BUDGET);
41
42 nightmare_stop_after_finding();
43 TEST_ASSERT_EQ(atomic_load(&nightmare_runtime.stop), NM_STOP_FINDING);
44
45 nightmare_publish_stop(reason: NM_STOP_BUDGET);
46 TEST_ASSERT_EQ(atomic_load(&nightmare_runtime.stop), NM_STOP_FINDING);
47
48 nightmare_publish_stop(reason: NM_STOP_FAIL);
49 TEST_ASSERT_EQ(atomic_load(&nightmare_runtime.stop), NM_STOP_FAIL);
50
51 nightmare_publish_stop(reason: NM_STOP_STALL);
52 TEST_ASSERT_EQ(atomic_load(&nightmare_runtime.stop), NM_STOP_STALL);
53
54 atomic_store_explicit(&nightmare_runtime.stop, NM_RUN,
55 memory_order_relaxed);
56 return TEST_SUCCESS;
57}
58
59static atomic_size_t stop_sleepers_waiting;
60
61static void heartbeat_waiter(void *arg) {
62 (void) arg;
63 struct thread *self = thread_get_current();
64 thread_prepare_to_sleep(t: self, r: THREAD_SLEEP_REASON_MANUAL,
65 wait_type: THREAD_WAIT_UNINTERRUPTIBLE, expect_wake_src: self);
66 atomic_fetch_add_explicit(&stop_sleepers_waiting, 1, memory_order_release);
67 thread_yield_until_wake_match();
68}
69
70TEST_DECLARE_UNIT(nightmare_harness, first_stop_wakes_sleepers) {
71 atomic_store_explicit(&stop_sleepers_waiting, 0, memory_order_relaxed);
72 atomic_store_explicit(&nightmare_runtime.stop, NM_RUN,
73 memory_order_relaxed);
74
75 struct thread *worker_thread =
76 thread_spawn_joinable(name: "nightmare_stop_worker", entry: heartbeat_waiter, NULL);
77 struct thread *heartbeat = thread_spawn_joinable(name: "nightmare_stop_heartbeat",
78 entry: heartbeat_waiter, NULL);
79 TEST_ASSERT_NONNULL(worker_thread);
80 TEST_ASSERT_NONNULL(heartbeat);
81
82 struct nightmare_worker worker = {0};
83 atomic_store_explicit(&worker.th, worker_thread, memory_order_relaxed);
84 nightmare_runtime.workers = &worker;
85 nightmare_runtime.total_worker_count = 1;
86 nightmare_runtime.heartbeat = heartbeat;
87 while (atomic_load_explicit(&stop_sleepers_waiting, memory_order_acquire) <
88 2)
89 scheduler_yield();
90
91 nightmare_publish_stop(reason: NM_STOP_BUDGET);
92 nightmare_runtime.workers = NULL;
93 nightmare_runtime.total_worker_count = 0;
94 nightmare_runtime.heartbeat = NULL;
95
96 bool worker_joined = thread_join_timeout(t: worker_thread, timeout_ms: 250, NULL);
97 if (!worker_joined) {
98 scheduler_wake_manual(t: worker_thread, wake_src: worker_thread);
99 thread_join(t: worker_thread);
100 }
101 bool heartbeat_joined = thread_join_timeout(t: heartbeat, timeout_ms: 250, NULL);
102 if (!heartbeat_joined) {
103 scheduler_wake_manual(t: heartbeat, wake_src: heartbeat);
104 thread_join(t: heartbeat);
105 }
106
107 atomic_store_explicit(&nightmare_runtime.stop, NM_RUN,
108 memory_order_relaxed);
109 TEST_ASSERT(worker_joined);
110 TEST_ASSERT(heartbeat_joined);
111 return TEST_SUCCESS;
112}
113
114TEST_DECLARE_UNIT(nightmare_harness, finding_stop_preserves_finding_verdict) {
115 struct nightmare_verdict verdict =
116 nightmare_verdict_for_stop(NIGHTMARE_OK, stop: NM_STOP_FINDING);
117 TEST_ASSERT_EQ(verdict.result, NIGHTMARE_RESULT_OK);
118 TEST_ASSERT_EQ(nightmare_result_with_findings(verdict.result, 1),
119 NIGHTMARE_RESULT_FINDING);
120 TEST_ASSERT_EQ(nightmare_result_with_findings(verdict.result, 0),
121 NIGHTMARE_RESULT_OK);
122 return TEST_SUCCESS;
123}
124
125TEST_DECLARE_UNIT(nightmare_harness, forced_stop_verdicts_override_ok) {
126 struct nightmare_verdict failed =
127 nightmare_verdict_for_stop(NIGHTMARE_OK, stop: NM_STOP_FAIL);
128 TEST_ASSERT_EQ(failed.result, NIGHTMARE_RESULT_FAIL);
129 TEST_ASSERT_STR_EQ(failed.reason, "harness");
130
131 struct nightmare_verdict stalled =
132 nightmare_verdict_for_stop(NIGHTMARE_OK, stop: NM_STOP_STALL);
133 TEST_ASSERT_EQ(stalled.result, NIGHTMARE_RESULT_STALL);
134 TEST_ASSERT_STR_EQ(stalled.reason, "liveness");
135 return TEST_SUCCESS;
136}
137#endif
138