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