1#include "cmdline/internal.h"
2#include "tests/test_internal.h"
3#include <cmdline.h>
4#include <math/units.h>
5
6TEST_GROUP_DEFINE(cmdline);
7
8struct shared_schema_probe {
9 bool enabled;
10};
11
12static struct shared_schema_probe shared_left;
13static struct shared_schema_probe shared_right;
14
15static void *shared_left_resolve(const char *path, size_t len) {
16 return len == strlen(str: "left") && strncmp(s1: path, s2: "left", n: len) == 0
17 ? &shared_left
18 : NULL;
19}
20
21static void *shared_right_resolve(const char *path, size_t len) {
22 return len == strlen(str: "right") && strncmp(s1: path, s2: "right", n: len) == 0
23 ? &shared_right
24 : NULL;
25}
26
27CMDLINE_SCHEMA_DEFINE(shared_left_schema, "schema_probe", "left",
28 "shared-root resolver probe", shared_left_resolve,
29 CMDLINE_SCHEMA_PROP(struct shared_schema_probe, enabled));
30
31CMDLINE_SCHEMA_DEFINE(shared_right_schema, "schema_probe", "right",
32 "shared-root resolver probe", shared_right_resolve,
33 CMDLINE_SCHEMA_PROP(struct shared_schema_probe, enabled));
34
35TEST_DEFINE_UNIT(cmdline, shared_schema_root) {
36 cmdline_dispatch(var: "schema_probe.right.enabled", val: "true");
37 cmdline_dispatch(var: "schema_probe.left.enabled", val: "true");
38 TEST_ASSERT(shared_left.enabled);
39 TEST_ASSERT(shared_right.enabled);
40 return TEST_SUCCESS;
41}
42
43TEST_DEFINE_UNIT(cmdline, xmacro_descriptors) {
44 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_BOOL), "bool");
45 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_INT), "int");
46 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_UINT), "uint");
47 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_FX), "fx");
48 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_DURATION), "duration");
49 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_DATA_SIZE),
50 "data_size");
51 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_RANGE), "range");
52 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_CPU_MASK), "cpu_mask");
53 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_MAC), "mac");
54 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_STRING), "string");
55 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_LIST), "list");
56 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_ERR), "err");
57 TEST_ASSERT_STR_EQ(cmdline_type_to_str(CMDLINE_TYPE_NONE), "none");
58
59 TEST_ASSERT_STR_EQ(cmdline_expr_type_to_str(CMDLINE_TYPE_BOOL), "<on/off>");
60 TEST_ASSERT_STR_EQ(cmdline_expr_type_to_str(CMDLINE_TYPE_DURATION),
61 "<time>");
62 TEST_ASSERT_STR_EQ(cmdline_expr_type_to_str(CMDLINE_TYPE_DATA_SIZE),
63 "<size>");
64 TEST_ASSERT_STR_EQ(cmdline_expr_type_to_str(CMDLINE_TYPE_FX), "<float>");
65
66 TEST_ASSERT_STR_EQ(cmdline_type_raw_hint(CMDLINE_TYPE_BOOL), "on/off");
67 TEST_ASSERT_STR_EQ(cmdline_type_raw_hint(CMDLINE_TYPE_DURATION), "time");
68 TEST_ASSERT_STR_EQ(cmdline_type_raw_hint(CMDLINE_TYPE_DATA_SIZE), "size");
69
70 return TEST_SUCCESS;
71}
72
73TEST_DEFINE_UNIT(cmdline, polymorphic_parsing) {
74 uint64_t mask = 0; /* 0 means unconstrained */
75
76 struct cmdline_value vb = cmdline_parse_value_for(value: "true", accepted: mask);
77 TEST_ASSERT(vb.type == CMDLINE_TYPE_BOOL && vb.b == true);
78
79 struct cmdline_value vu = cmdline_parse_value_for(value: "12345", accepted: mask);
80 TEST_ASSERT(vu.type == CMDLINE_TYPE_UINT && vu.u64 == 12345);
81
82 struct cmdline_value vi = cmdline_parse_value_for(value: "-999", accepted: mask);
83 TEST_ASSERT(vi.type == CMDLINE_TYPE_INT && vi.i64 == -999);
84
85 struct cmdline_value vfx = cmdline_parse_value_for(value: "0.75", accepted: mask);
86 TEST_ASSERT(vfx.type == CMDLINE_TYPE_FX && vfx.fx == FX(0.75));
87
88 struct cmdline_value vdur = cmdline_parse_value_for(value: "250ms", accepted: mask);
89 TEST_ASSERT(vdur.type == CMDLINE_TYPE_DURATION &&
90 vdur.duration == MS_TO_NS(250));
91
92 struct cmdline_value vsz = cmdline_parse_value_for(value: "64MiB", accepted: mask);
93 TEST_ASSERT(vsz.type == CMDLINE_TYPE_DATA_SIZE && vsz.u64 == MIB(64));
94
95 struct cmdline_value vstr = cmdline_parse_value_for(value: "hello_world", accepted: mask);
96 TEST_ASSERT(vstr.type == CMDLINE_TYPE_STRING &&
97 strcmp((char *) vstr.data, "hello_world") == 0);
98
99 uint64_t bool_only_mask = CMDLINE_TYPES(CMDLINE_TYPE_BOOL);
100 struct cmdline_value v_err =
101 cmdline_parse_value_for(value: "12345", accepted: bool_only_mask);
102 TEST_ASSERT_EQ(v_err.type, CMDLINE_TYPE_ERR);
103
104 return TEST_SUCCESS;
105}
106
107TEST_DEFINE_UNIT(cmdline, extraction_helpers) {
108 struct cmdline_value vu = {.type = CMDLINE_TYPE_UINT,
109 .u64 = 0x123456789ABCDEF0ULL};
110 uint64_t u64_val = 0;
111 TEST_ASSERT(cmdline_extract_u64(&vu, &u64_val) == ERR_OK &&
112 u64_val == 0x123456789ABCDEF0ULL);
113
114 uint32_t u32_val = 0;
115 struct cmdline_value vu32 = {.type = CMDLINE_TYPE_UINT, .u64 = 0x12345678};
116 TEST_ASSERT(cmdline_extract_u32(&vu32, &u32_val) == ERR_OK &&
117 u32_val == 0x12345678);
118
119 struct cmdline_value vi = {.type = CMDLINE_TYPE_INT, .i64 = -42};
120 int64_t i64_val = 0;
121 TEST_ASSERT(cmdline_extract_i64(&vi, &i64_val) == ERR_OK && i64_val == -42);
122
123 struct cmdline_value vb = {.type = CMDLINE_TYPE_BOOL, .b = true};
124 bool b_val = false;
125 TEST_ASSERT(cmdline_extract_bool(&vb, &b_val) == ERR_OK && b_val == true);
126
127 struct cmdline_value vfx = {.type = CMDLINE_TYPE_FX, .fx = FX(0.5)};
128 fx32_32_t fx_val = 0;
129 TEST_ASSERT(cmdline_extract_fx(&vfx, &fx_val) == ERR_OK &&
130 fx_val == FX(0.5));
131
132 struct cmdline_value vdur = {.type = CMDLINE_TYPE_DURATION,
133 .duration = SECONDS_TO_NS(2)};
134 time_ns_t dur_val = 0;
135 TEST_ASSERT(cmdline_extract_duration(&vdur, &dur_val) == ERR_OK &&
136 dur_val == SECONDS_TO_NS(2));
137
138 const char *str_val = NULL;
139 struct cmdline_value vstr = {.type = CMDLINE_TYPE_STRING,
140 .data = "my_string"};
141 TEST_ASSERT(cmdline_extract_const_string(&vstr, &str_val) == ERR_OK &&
142 strcmp(str_val, "my_string") == 0);
143
144 uint64_t gen_u64 = 0;
145 int64_t gen_i64 = 0;
146 bool gen_b = false;
147 const char *gen_str = NULL;
148
149 TEST_ASSERT(CMDLINE_EXTRACT(&vu, gen_u64) == ERR_OK &&
150 gen_u64 == 0x123456789ABCDEF0ULL);
151 TEST_ASSERT(CMDLINE_EXTRACT(&vi, gen_i64) == ERR_OK && gen_i64 == -42);
152 TEST_ASSERT(CMDLINE_EXTRACT(&vb, gen_b) == ERR_OK && gen_b == true);
153 TEST_ASSERT(CMDLINE_EXTRACT(&vstr, gen_str) == ERR_OK &&
154 strcmp(gen_str, "my_string") == 0);
155
156 return TEST_SUCCESS;
157}
158
159TEST_DEFINE_UNIT(cmdline, choices_and_mappings) {
160 const char *const *choices = CMDLINE_CHOICES("alpha", "beta", "gamma");
161 TEST_ASSERT(cmdline_has_choice(choices, "alpha"));
162 TEST_ASSERT(cmdline_has_choice(choices, "beta"));
163 TEST_ASSERT(cmdline_has_choice(choices, "gamma"));
164 TEST_ASSERT(!cmdline_has_choice(choices, "delta"));
165 TEST_ASSERT(!cmdline_has_choice(choices, ""));
166
167 const struct cmdline_map *mappings =
168 CMDLINE_MAPPINGS(CMDLINE_MAP("fast", 100), CMDLINE_MAP("slow", 10));
169 uint64_t map_val = 0;
170 TEST_ASSERT(cmdline_lookup_mapping(mappings, "fast", &map_val) &&
171 map_val == 100);
172 TEST_ASSERT(cmdline_lookup_mapping(mappings, "slow", &map_val) &&
173 map_val == 10);
174 TEST_ASSERT(!cmdline_lookup_mapping(mappings, "medium", &map_val));
175
176 return TEST_SUCCESS;
177}
178
179TEST_DEFINE_UNIT(cmdline, flags_table) {
180 const struct cmdline_flag *flags =
181 CMDLINE_FLAGS({"read", BIT(0)}, {"write", BIT(1)}, {"exec", BIT(2)});
182 uint64_t mask = 0;
183 const char *err_tok = NULL;
184 size_t err_len = 0;
185
186 TEST_ASSERT(
187 cmdline_parse_flags(flags, "read,write", &mask, &err_tok, &err_len) &&
188 mask == 0x3);
189 TEST_ASSERT(cmdline_parse_flags(flags, "exec", &mask, &err_tok, &err_len) &&
190 mask == 0x4);
191 TEST_ASSERT(cmdline_parse_flags(flags, "read,exec,write", &mask, &err_tok,
192 &err_len) &&
193 mask == 0x7);
194 TEST_ASSERT(!cmdline_parse_flags(flags, "read,invalid_flag", &mask,
195 &err_tok, &err_len));
196
197 return TEST_SUCCESS;
198}
199
200CMDLINE_CHILD_EXTERN(test_root, group_opt_in);
201CMDLINE_CHILD_EXTERN(watchdog, master, heartbeat_interval);
202
203TEST_DEFINE_UNIT(cmdline, runtime_query) {
204 struct cmdline_entry *e_root = cmdline_lookup(key: "root");
205 TEST_ASSERT_NONNULL(e_root);
206 TEST_ASSERT_STR_EQ(e_root->name, "root");
207
208 struct cmdline_entry *e_none = cmdline_lookup(key: "non_existent_key_123");
209 TEST_ASSERT_NULL(e_none);
210
211 uint64_t fallback_u64 = CMDLINE_GET("non_existent_key_123", uint64_t, 9999);
212 TEST_ASSERT_EQ(fallback_u64, 9999);
213
214 uint64_t out_val = 0;
215 bool found = cmdline_read_or("non_existent_key_123", out_val, 42);
216 TEST_ASSERT(!found);
217 TEST_ASSERT_EQ(out_val, 42);
218
219 bool opt_in = CMDLINE_CHILD_VALUE(test_root, group_opt_in);
220 TEST_ASSERT_EQ(opt_in, false);
221
222 struct cmdline_entry *e_hb1 =
223 CMDLINE_CHILD(watchdog, master, heartbeat_interval);
224 struct cmdline_entry *e_hb2 =
225 CMDLINE_CHILD(CMDLINE_NODE(watchdog, master), heartbeat_interval);
226 TEST_ASSERT_NONNULL(e_hb1);
227 TEST_ASSERT_PTR_EQ(e_hb1, e_hb2);
228 TEST_ASSERT_STR_EQ(e_hb1->name, "heartbeat_interval");
229
230 /* TODO: Move these two a separate test */
231 uint64_t reg = 0;
232 reg = BIT_SET_FIELD(reg, 0x5, 4, 7);
233 TEST_ASSERT_EQ(BIT_GET_FIELD(reg, 4, 7), 0x5);
234
235 uint64_t full_mask = BIT_MASK(0, 63);
236 TEST_ASSERT_EQ(full_mask, UINT64_MAX);
237
238 return TEST_SUCCESS;
239}
240
241TEST_DEFINE_UNIT(cmdline, list_parsing) {
242 struct cmdline_value vlist = cmdline_parse_list(value: "10,20,30", accepted: 0);
243 TEST_ASSERT_EQ(vlist.type, CMDLINE_TYPE_LIST);
244
245 struct cmdline_list list = {0};
246 TEST_ASSERT_OK(cmdline_extract_list(&vlist, &list));
247 TEST_ASSERT_EQ(list.count, 3);
248 TEST_ASSERT(list.items[0].type == CMDLINE_TYPE_UINT &&
249 list.items[0].u64 == 10);
250 TEST_ASSERT(list.items[1].type == CMDLINE_TYPE_UINT &&
251 list.items[1].u64 == 20);
252 TEST_ASSERT(list.items[2].type == CMDLINE_TYPE_UINT &&
253 list.items[2].u64 == 30);
254
255 return TEST_SUCCESS;
256}
257