-
-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathtest_yaml_validate.c
More file actions
337 lines (304 loc) · 12 KB
/
Copy pathtest_yaml_validate.c
File metadata and controls
337 lines (304 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/**
* @file test_yaml_validate.c
* @brief Layer 1 unit tests for yaml_validate helpers (T1).
*
* Tests the libyaml-backed wrapper:
* - yaml_validate_str
* - yaml_is_mapping_root
* - yaml_validate_is_available
*
* When built WITHOUT libyaml (LIGHTNVR_HAVE_LIBYAML undefined), the
* stubs return a stable "validation disabled" answer; several of the
* error-path assertions are relaxed in that mode so the test suite
* still passes as a compile/link smoke check.
*/
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "unity.h"
#include "utils/yaml_validate.h"
/* ---- Unity boilerplate ---- */
void setUp(void) {}
void tearDown(void) {}
/* ================================================================
* 1. Valid mapping
* ================================================================ */
void test_valid_mapping_returns_zero(void) {
const char *src = "key: value\n";
char err[256] = {0};
int rc = yaml_validate_str(src, strlen(src), err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc);
}
/* ================================================================
* 2. Malformed YAML → parse error with line/col in err_buf
*
* libyaml's tolerance for plain-text indentation is high, so we use
* an unmistakably invalid construct: a flow-sequence that is never
* closed. This is guaranteed to raise a parser error.
* ================================================================ */
void test_invalid_indentation_returns_error(void) {
/* Unclosed flow sequence — libyaml reliably rejects this. */
const char *src = "key: [1, 2, 3\nkey2: value\n";
char err[256] = {0};
int rc = yaml_validate_str(src, strlen(src), err, sizeof(err));
if (yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, rc);
TEST_ASSERT_TRUE_MESSAGE(err[0] != '\0', "err_buf should contain a message");
/* Error text should mention line or column info. */
int has_line_or_col =
(strstr(err, "line") != NULL) ||
(strstr(err, "column") != NULL) ||
(strstr(err, "Line") != NULL) ||
(strstr(err, "Column") != NULL);
TEST_ASSERT_TRUE_MESSAGE(has_line_or_col,
"err_buf should contain line/column info");
} else {
/* Stub path — succeeds silently. */
TEST_ASSERT_EQUAL_INT(0, rc);
}
}
/* ================================================================
* 3. Non-mapping root: sequence
* yaml_is_mapping_root returns 0 (not mapping), but
* yaml_validate_str returns 0 (valid YAML).
* ================================================================ */
void test_sequence_root_is_valid_but_not_mapping(void) {
const char *src = "- a\n- b\n";
char err[256] = {0};
int rc_valid = yaml_validate_str(src, strlen(src), err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc_valid);
int is_map = yaml_is_mapping_root(src, strlen(src));
/* 0 means "not mapping root" — true for both libyaml and stub. */
TEST_ASSERT_EQUAL_INT(0, is_map);
}
/* Sanity — mapping root IS detected when libyaml is available. */
void test_mapping_root_detected(void) {
const char *src = "key: value\n";
int is_map = yaml_is_mapping_root(src, strlen(src));
if (yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(1, is_map);
} else {
/* Stub returns 0 (unknown) — documented contract. */
TEST_ASSERT_EQUAL_INT(0, is_map);
}
}
/* ================================================================
* 4. Empty string: behavior defined as "valid, empty document".
* ================================================================ */
void test_empty_string_is_valid(void) {
const char *src = "";
char err[256] = {0};
int rc = yaml_validate_str(src, 0, err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc);
}
void test_empty_string_not_mapping_root(void) {
const char *src = "";
int is_map = yaml_is_mapping_root(src, 0);
TEST_ASSERT_EQUAL_INT(0, is_map);
}
/* ================================================================
* 5. CRLF line endings
* ================================================================ */
void test_crlf_line_endings_valid(void) {
const char *src = "key: value\r\nkey2: value2\r\n";
char err[256] = {0};
int rc = yaml_validate_str(src, strlen(src), err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc);
}
/* ================================================================
* 6. Huge valid document (≥ 50 KB)
* ================================================================ */
void test_huge_valid_document(void) {
const size_t n_entries = 5000; /* each "kNNNN: vNNNN\n" ≈ 13 bytes → ~65 KB */
size_t cap = n_entries * 32 + 64;
char *buf = (char *)malloc(cap);
TEST_ASSERT_NOT_NULL(buf);
size_t off = 0;
for (size_t i = 0; i < n_entries; ++i) {
int n = snprintf(buf + off, cap - off, "k%05zu: v%05zu\n", i, i);
TEST_ASSERT_TRUE(n > 0 && (size_t)n < cap - off);
off += (size_t)n;
}
TEST_ASSERT_TRUE_MESSAGE(off >= 50 * 1024, "document should be at least 50 KB");
char err[256] = {0};
int rc = yaml_validate_str(buf, off, err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc);
/* And the huge document is a mapping root (when libyaml available). */
int is_map = yaml_is_mapping_root(buf, off);
if (yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(1, is_map);
} else {
TEST_ASSERT_EQUAL_INT(0, is_map);
}
free(buf);
}
/* ================================================================
* Null-safety: null src / zero err_size should not crash.
* ================================================================ */
void test_null_src_returns_zero(void) {
/* NULL src with zero length is treated as empty → valid. */
char err[256] = {0};
int rc = yaml_validate_str(NULL, 0, err, sizeof(err));
TEST_ASSERT_EQUAL_INT(0, rc);
}
void test_null_err_buf_does_not_crash(void) {
const char *src = "key: value\n";
int rc = yaml_validate_str(src, strlen(src), NULL, 0);
TEST_ASSERT_EQUAL_INT(0, rc);
}
/* ================================================================
* T6 — yaml_validate_go2rtc_override
* ================================================================ */
void test_go2rtc_validate_empty_is_valid(void) {
yaml_validation_result_t r;
yaml_validate_go2rtc_override(NULL, 0, &r);
if (yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(1, r.valid);
TEST_ASSERT_EQUAL_INT(0, r.warning_count);
} else {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
}
}
void test_go2rtc_validate_known_sections_no_warnings(void) {
const char *src =
"ffmpeg:\n"
" h264: \"-codec:v copy\"\n"
"log:\n"
" level: trace\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(1, r.valid);
TEST_ASSERT_EQUAL_INT(0, r.duplicate_keys);
TEST_ASSERT_EQUAL_INT(0, r.non_mapping_root);
TEST_ASSERT_EQUAL_INT(0, r.warning_count);
}
/* The exact issue #394 shape — a top-level `ffmpeg:` section that the
* generator already emits, duplicated in the user override. */
void test_go2rtc_validate_duplicate_top_level_key_rejected(void) {
const char *src =
"ffmpeg:\n"
" h264: \"-codec:v copy\"\n"
"log:\n"
" level: trace\n"
"ffmpeg:\n"
" h265: \"-codec:v copy\"\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(0, r.valid);
TEST_ASSERT_EQUAL_INT(1, r.duplicate_keys);
/* Error should pinpoint the SECOND `ffmpeg:` (line 5, column 1). */
TEST_ASSERT_EQUAL_INT(5, r.err_line);
TEST_ASSERT_EQUAL_INT(1, r.err_column);
TEST_ASSERT_NOT_NULL(strstr(r.err_message, "duplicate"));
TEST_ASSERT_NOT_NULL(strstr(r.err_message, "ffmpeg"));
}
void test_go2rtc_validate_non_mapping_root_rejected(void) {
const char *src = "- a\n- b\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(0, r.valid);
TEST_ASSERT_EQUAL_INT(1, r.non_mapping_root);
}
void test_go2rtc_validate_unknown_section_warns(void) {
/* "fmpeg" — typo of ffmpeg. Should warn (not error). */
const char *src =
"fmpeg:\n"
" h264: \"-codec:v copy\"\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(1, r.valid); /* still valid — typos are warnings */
TEST_ASSERT_GREATER_OR_EQUAL_INT(1, r.warning_count);
TEST_ASSERT_NOT_NULL(strstr(r.warnings[0], "fmpeg"));
}
void test_go2rtc_validate_malformed_yaml_returns_parse_error(void) {
const char *src = "ffmpeg: [unclosed\nlog: trace\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(0, r.valid);
TEST_ASSERT_EQUAL_INT(1, r.parse_error);
TEST_ASSERT_GREATER_THAN_INT(0, r.err_line);
}
/* Whitespace/comment-only documents must validate as a no-op (Copilot review).
* libyaml emits DOCUMENT_START → DOCUMENT_END with no node in between when
* the input is just comments and blank lines; the previous code mistakenly
* flagged that as a non-mapping root. */
void test_go2rtc_validate_comments_only_is_valid(void) {
const char *src =
"# this is a comment-only override\n"
"# nothing to set right now\n"
"\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(1, r.valid);
TEST_ASSERT_EQUAL_INT(0, r.non_mapping_root);
TEST_ASSERT_EQUAL_INT(0, r.warning_count);
}
/* The exact reporter override from issue #394 — should pass cleanly. */
void test_go2rtc_validate_issue_394_reporter_shape_is_valid(void) {
const char *src =
"ffmpeg:\n"
" h264: \"-codec:v copy -codec:a copy\"\n"
" h265: \"-codec:v copy -codec:a copy\"\n"
"log:\n"
" level: trace\n";
yaml_validation_result_t r;
yaml_validate_go2rtc_override(src, strlen(src), &r);
if (!yaml_validate_is_available()) {
TEST_ASSERT_EQUAL_INT(-1, r.valid);
return;
}
TEST_ASSERT_EQUAL_INT(1, r.valid);
TEST_ASSERT_EQUAL_INT(0, r.duplicate_keys);
TEST_ASSERT_EQUAL_INT(0, r.warning_count);
}
/* ================================================================
* main
* ================================================================ */
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_valid_mapping_returns_zero);
RUN_TEST(test_invalid_indentation_returns_error);
RUN_TEST(test_sequence_root_is_valid_but_not_mapping);
RUN_TEST(test_mapping_root_detected);
RUN_TEST(test_empty_string_is_valid);
RUN_TEST(test_empty_string_not_mapping_root);
RUN_TEST(test_crlf_line_endings_valid);
RUN_TEST(test_huge_valid_document);
RUN_TEST(test_null_src_returns_zero);
RUN_TEST(test_null_err_buf_does_not_crash);
/* T6 */
RUN_TEST(test_go2rtc_validate_empty_is_valid);
RUN_TEST(test_go2rtc_validate_known_sections_no_warnings);
RUN_TEST(test_go2rtc_validate_duplicate_top_level_key_rejected);
RUN_TEST(test_go2rtc_validate_non_mapping_root_rejected);
RUN_TEST(test_go2rtc_validate_unknown_section_warns);
RUN_TEST(test_go2rtc_validate_malformed_yaml_returns_parse_error);
RUN_TEST(test_go2rtc_validate_comments_only_is_valid);
RUN_TEST(test_go2rtc_validate_issue_394_reporter_shape_is_valid);
return UNITY_END();
}