Path:
tests/unit/strictdoc/backend/sdoc_source_code/readers/test_reader_c.py
Lines:
657
Non-empty lines:
522
Non-empty lines covered with requirements:
522 / 522 (100.0%)
Functions:
18
Functions covered by requirements:
18 / 18 (100.0%)
1
"""2
@relation(SDOC-SRS-146, scope=file)3
"""4
5
from strictdoc.backend.sdoc_source_code.models.source_file_info import (
6
SourceFileTraceabilityInfo,
7
)8
from strictdoc.backend.sdoc_source_code.reader_c import (
9
SourceFileTraceabilityReader_C,
10
)11
12
13
def test_00_empty_file():
14
input_string = b""""""
15
16
reader = SourceFileTraceabilityReader_C()
17
18
info = reader.read(input_string, file_path="NOT_RELEVANT")
19
20
assert isinstance(info, SourceFileTraceabilityInfo)
21
assert len(info.markers) == 0
22
23
24
def test_01_single_string():
25
input_string = b"""\
26
// Unimportant comment.27
"""28
29
reader = SourceFileTraceabilityReader_C()
30
31
info = reader.read(input_string, file_path="NOT_RELEVANT")
32
33
assert isinstance(info, SourceFileTraceabilityInfo)
34
assert len(info.functions) == 0
35
assert len(info.markers) == 0
36
37
38
def test_02_functions():
39
input_string = b"""\
40
#include <stdio.h>41
42
/**43
* Some text.44
*45
* @relation(REQ-1, scope=function)46
*/47
void hello_world(void) {48
print("hello world\\n");
49
}50
51
/**52
* Some text.53
*54
* @relation(REQ-2, scope=function)55
*/56
void hello_world_2(void) {57
print("hello world\\n");
58
}59
"""60
61
reader = SourceFileTraceabilityReader_C()
62
63
info: SourceFileTraceabilityInfo = reader.read(
64
input_string, file_path="foo.cpp"
65
)66
67
assert isinstance(info, SourceFileTraceabilityInfo)
68
assert len(info.markers) == 2
69
assert info.markers[0].ng_source_line_begin == 6
70
assert info.markers[0].ng_range_line_begin == 3
71
assert info.markers[0].ng_range_line_end == 10
72
assert info.markers[0].reqs_objs[0].ng_source_line == 6
73
assert info.markers[0].reqs_objs[0].ng_source_column == 14
74
75
assert info.markers[1].ng_source_line_begin == 15
76
assert info.markers[1].ng_range_line_begin == 12
77
assert info.markers[1].ng_range_line_end == 19
78
assert info.markers[1].reqs_objs[0].ng_source_line == 15
79
assert info.markers[1].reqs_objs[0].ng_source_column == 14
80
81
82
def test_03_functions_multiline():
83
input_string = b"""\
84
#include <stdio.h>85
86
/**87
* Some text.88
*89
* @relation(90
* REQ-1, scope=function91
* )92
*/93
void hello_world(void) {94
print("hello world\\n");
95
}96
97
/**98
* Some text.99
*100
* @relation(REQ-2,101
* scope=function)102
*/103
void hello_world_2(void) {104
print("hello world\\n");
105
}106
"""107
108
reader = SourceFileTraceabilityReader_C()
109
110
info: SourceFileTraceabilityInfo = reader.read(
111
input_string, file_path="foo.cpp"
112
)113
114
assert isinstance(info, SourceFileTraceabilityInfo)
115
assert len(info.markers) == 2
116
assert info.markers[0].ng_source_line_begin == 6
117
assert info.markers[0].ng_range_line_begin == 3
118
assert info.markers[0].ng_range_line_end == 12
119
assert info.markers[0].reqs_objs[0].ng_source_line == 7
120
assert info.markers[0].reqs_objs[0].ng_source_column == 6
121
122
assert info.markers[1].ng_source_line_begin == 17
123
assert info.markers[1].ng_range_line_begin == 14
124
assert info.markers[1].ng_range_line_end == 22
125
assert info.markers[1].reqs_objs[0].ng_source_line == 17
126
assert info.markers[1].reqs_objs[0].ng_source_column == 14
127
128
129
def test_04_multiline_markers_with_underscores():
130
"""
131
Bug: requirements UID not detected in source code when there's an EOL #2130132
https://github.com/strictdoc-project/strictdoc/issues/2130133
"""134
135
input_string = b"""\
136
#include <stdio.h>137
138
/**139
* @brief some text140
* @return some text.141
* @param[in] void142
* @param[out] void143
* @param[in, out] void144
* @pre145
* @post146
* @relation{INT_STP_016_0000, INT_STP_016_0001, INT_STP_016_0002,147
* scope=function}148
* @note Reference:149
*/150
stilib_result_t stilib_smu_start_state_check(void);151
"""152
153
reader = SourceFileTraceabilityReader_C()
154
155
info: SourceFileTraceabilityInfo = reader.read(
156
input_string, file_path="foo.c"
157
)158
159
assert isinstance(info, SourceFileTraceabilityInfo)
160
assert len(info.markers) == 1
161
assert info.markers[0].reqs == [
162
"INT_STP_016_0000",
163
"INT_STP_016_0001",
164
"INT_STP_016_0002",
165
]166
167
assert info.markers[0].ng_source_line_begin == 11
168
assert info.markers[0].ng_range_line_begin == 3
169
assert info.markers[0].ng_range_line_end == 15
170
assert info.markers[0].reqs_objs[0].ng_source_line == 11
171
assert info.markers[0].reqs_objs[0].ng_source_column == 14
172
assert info.markers[0].reqs_objs[1].ng_source_line == 11
173
assert info.markers[0].reqs_objs[1].ng_source_column == 32
174
assert info.markers[0].reqs_objs[2].ng_source_line == 11
175
assert info.markers[0].reqs_objs[2].ng_source_column == 50
176
177
178
def test_20_node_fields():
179
input_string = b"""\
180
#include <stdio.h>181
182
/**183
* Some text.184
*185
* INTENTION: This186
* is187
* the188
* intention.189
*190
* @relation(REQ-1, scope=function)191
*/192
void hello_world(void) {193
print("hello world\\n");
194
}195
"""196
197
reader = SourceFileTraceabilityReader_C()
198
199
info: SourceFileTraceabilityInfo = reader.read(
200
input_string, file_path="foo.c"
201
)202
203
assert isinstance(info, SourceFileTraceabilityInfo)
204
assert len(info.markers) == 1
205
assert info.markers[0].ng_source_line_begin == 11
206
assert info.markers[0].ng_range_line_begin == 3
207
assert info.markers[0].ng_range_line_end == 15
208
assert info.markers[0].reqs_objs[0].ng_source_line == 11
209
assert info.markers[0].reqs_objs[0].ng_source_column == 14
210
211
212
def test_90_edge_case_capitalized_field_with_colon_and_colon():
213
"""
214
Ensure that there is no missing grammar token for a case reported by a user.215
216
Previously, StrictDoc would raise an exception related to Lark not finding217
a grammar token when parsing "L:LABEL: ..." kind of string (see below).218
This test makes sure that the parser works with no issues.219
220
https://github.com/strictdoc-project/strictdoc/issues/2342221
"""222
223
input_string = b"""\
224
EFI_DEVICE_PATH *FileDevicePathFromConfig(EFI_HANDLE device,225
CHAR16 *payloadpath)226
{227
UINTN prefixlen = 0;228
EFI_DEVICE_PATH *devpath = NULL;229
230
LABELMODE lm = NOLABEL;231
/* Check if payload path contains a232
* L:LABEL: item to specify a FAT partition or a233
* C:LABEL: to specify a custom labeled FAT partition */234
if (StrnCmp(payloadpath, L"L:", 2) == 0) {235
lm = DOSFSLABEL;236
} else if (StrnCmp(payloadpath, L"C:", 2) == 0) {237
lm = CUSTOMLABEL;238
}239
// ... truncated ...240
}241
"""242
243
reader = SourceFileTraceabilityReader_C()
244
245
info: SourceFileTraceabilityInfo = reader.read(
246
input_string, file_path="foo.c"
247
)248
249
assert isinstance(info, SourceFileTraceabilityInfo)
250
assert len(info.markers) == 0
251
252
253
def test_91_edge_case_capitalized_field_with_relation_marker():
254
"""
255
Ensures that "@relation:" is not treated as an incomplete StrictDoc marker.256
257
Reduced fragment from this source file:258
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/cpufreq/cpufreq-nforce2.c#n241259
260
https://github.com/strictdoc-project/strictdoc/issues/2342261
"""262
263
input_string = b"""\
264
/**265
* nforce2_target - set a new CPUFreq policy266
* @policy: new policy267
* @target_freq: the target frequency268
* @relation: how that frequency relates to achieved frequency269
* (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)270
*271
* Sets a new CPUFreq policy.272
*/273
static int nforce2_target(struct cpufreq_policy *policy,274
unsigned int target_freq, unsigned int relation)275
{276
}277
"""278
279
reader = SourceFileTraceabilityReader_C()
280
281
info: SourceFileTraceabilityInfo = reader.read(
282
input_string, file_path="foo.c"
283
)284
285
assert isinstance(info, SourceFileTraceabilityInfo)
286
assert len(info.markers) == 0
287
288
289
def test_92_edge_case_capitalized_letters():
290
"""
291
Ensure that fragments such as "IP_VS_SCTP_S_COOKIE_REPLIED: C:COOKIE-ECHO"292
do not trigger parsing errors.293
294
Reduced fragment from this source file:295
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/ipvs/ip_vs_proto_sctp.c#n247296
297
https://github.com/strictdoc-project/strictdoc/issues/2342298
"""299
300
input_string = b"""\
301
/*302
* IP_VS_SCTP_S_COOKIE_REPLIED: C:COOKIE-ECHO sent, wait for S:COOKIE-ACK303
*/304
void foobar(void) {}
305
"""306
307
reader = SourceFileTraceabilityReader_C()
308
309
info: SourceFileTraceabilityInfo = reader.read(
310
input_string, file_path="foo.c"
311
)312
313
assert isinstance(info, SourceFileTraceabilityInfo)
314
assert len(info.markers) == 0
315
316
317
def test_93_edge_case_capitalized_letters():
318
"""
319
Ensure that fragments like "A: VMRUN:" do not trigger parsing errors.320
321
Reduced fragment from this source file:322
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/x86/kvm/svm/sev.c#n4532323
324
https://github.com/strictdoc-project/strictdoc/issues/2342325
"""326
327
input_string = b"""\
328
void sev_es_prepare_switch_to_guest(struct vcpu_svm *svm, struct sev_es_save_area *hostsa)329
{330
struct kvm *kvm = svm->vcpu.kvm;331
332
/*333
* All host state for SEV-ES guests is categorized into three swap types334
* based on how it is handled by hardware during a world switch:335
*336
* A: VMRUN: Host state saved in host save area337
*/338
}339
"""340
341
reader = SourceFileTraceabilityReader_C()
342
343
info: SourceFileTraceabilityInfo = reader.read(
344
input_string, file_path="foo.c"
345
)346
347
assert isinstance(info, SourceFileTraceabilityInfo)
348
assert len(info.markers) == 0
349
350
351
def test_94_edge_case_field_name_then_newline_then_field_value():
352
r"""
353
Ensure that fragments with <field name>\n<field value...> do not trigger354
parsing errors.355
356
Reduced fragment from this source file:357
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/video/fbdev/i810/i810_gtf.c#n115358
359
https://github.com/strictdoc-project/strictdoc/issues/2342360
"""361
362
input_string = b"""\
363
/**364
* i810fb_encode_registers - encode @var to hardware register values365
* @var: pointer to var structure366
* @par: pointer to hardware par structure367
*368
* DESCRIPTION:369
* Timing values in @var will be converted to appropriate370
* register values of @par.371
*/372
void foobar(void) {}
373
"""374
375
reader = SourceFileTraceabilityReader_C()
376
377
info: SourceFileTraceabilityInfo = reader.read(
378
input_string, file_path="foo.c"
379
)380
381
assert isinstance(info, SourceFileTraceabilityInfo)
382
assert len(info.markers) == 0
383
assert len(info.source_nodes) == 1
384
385
386
def test_95_namespace_with_function():
387
r"""
388
Ensure that namespace is parsed correctly for free functions.389
"""390
input_string = b"""\
391
namespace math {392
393
int add(int a, int b) {394
return a + b;395
}396
}397
"""398
reader = SourceFileTraceabilityReader_C()
399
400
info: SourceFileTraceabilityInfo = reader.read(
401
input_string, file_path="foo.cpp"
402
)403
404
assert isinstance(info, SourceFileTraceabilityInfo)
405
assert len(info.markers) == 0
406
assert len(info.functions) == 1
407
assert len(info.source_nodes) == 0
408
409
assert info.functions[0].name == "math::add(int a, int b)"
410
411
412
def test_96_namespace_with_class_function():
413
r"""
414
Ensure that namespace is parsed correctly for class member functions.415
"""416
input_string = b"""\
417
namespace math {418
419
class Adder {420
public:421
int add(int a, int b) {422
return a + b;423
}424
};425
}426
"""427
reader = SourceFileTraceabilityReader_C()
428
429
info: SourceFileTraceabilityInfo = reader.read(
430
input_string, file_path="foo.cpp"
431
)432
433
assert isinstance(info, SourceFileTraceabilityInfo)
434
assert len(info.markers) == 0
435
assert len(info.functions) == 1
436
assert len(info.source_nodes) == 0
437
438
assert info.functions[0].name == "math::Adder::add(int a, int b)"
439
440
441
def test_97_multiline_function():
442
r"""
443
Ensure functions defined on multiple lines due to linting444
tools are parsed correctly. The saved name should not have any445
extra spaces, or newline characters.446
447
"""448
input_string = b"""\
449
int add(int a,450
int b) {451
return a + b;452
}453
"""454
reader = SourceFileTraceabilityReader_C()
455
456
info: SourceFileTraceabilityInfo = reader.read(
457
input_string, file_path="foo.c"
458
)459
460
assert isinstance(info, SourceFileTraceabilityInfo)
461
assert len(info.markers) == 0
462
assert len(info.functions) == 1
463
assert len(info.source_nodes) == 0
464
465
assert info.functions[0].name == "add(int a, int b)"
466
467
468
def test_100_known_c_macro_zephyr_test():
469
"""
470
Ensure that Zephyr's function-like test macro is recognized as a471
function with its full macro invocation as the display name.472
"""473
474
input_string = b"""\
475
ZTEST_USER(semaphore, test_k_sem_correct_count_limit)476
{477
// Some code here...478
}479
"""480
reader = SourceFileTraceabilityReader_C()
481
482
info: SourceFileTraceabilityInfo = reader.read(
483
input_string, file_path="foo.cpp"
484
)485
486
assert isinstance(info, SourceFileTraceabilityInfo)
487
assert len(info.markers) == 0
488
assert len(info.functions) == 1
489
assert len(info.source_nodes) == 0
490
491
assert (
492
info.functions[0].name
493
== "ZTEST_USER(semaphore, test_k_sem_correct_count_limit)"
494
)495
assert (
496
info.functions[0].display_name
497
== "ZTEST_USER(semaphore, test_k_sem_correct_count_limit)"
498
)499
500
501
def test_101_known_c_macro_zephyr_test_with_comment():
502
"""
503
Ensure that Zephyr's function-like test macro is recognized as a504
function with its full macro invocation as the display name.505
"""506
507
input_string = b"""\
508
/*509
* Copyright (c) 2016, 2020 Intel Corporation510
*511
* SPDX-License-Identifier: Apache-2.0512
*/513
514
515
/**516
* @brief Test the max value a semaphore can be given and taken517
* @details518
* - Reset an initialized semaphore's count to zero.519
* - Give the semaphore by a thread and verify the semaphore's count is520
* as expected.521
* - Verify the max count a semaphore can reach.522
* - Take the semaphore by a thread and verify the semaphore's count is523
* as expected.524
* - Verify the max times a semaphore can be taken.525
* @ingroup kernel_semaphore_tests526
* @see k_sem_count_get(), k_sem_give()527
*/528
ZTEST_USER(semaphore, test_k_sem_correct_count_limit)529
{530
531
/* reset an initialized semaphore's count to zero */532
k_sem_reset(&simple_sem);533
expect_k_sem_count_get(&simple_sem, 0U, "k_sem_reset failed: %u != %u");
534
535
/* Give the semaphore by a thread and verify the semaphore's536
* count is as expected537
*/538
for (int i = 1; i <= SEM_MAX_VAL; i++) {539
k_sem_give(&simple_sem);540
expect_k_sem_count_get_nomsg(&simple_sem, i);541
}542
543
/* Verify the max count a semaphore can reach544
* continue to run k_sem_give,545
* the count of simple_sem will not increase anymore546
*/547
for (int i = 0; i < 5; i++) {548
k_sem_give(&simple_sem);549
expect_k_sem_count_get_nomsg(&simple_sem, SEM_MAX_VAL);550
}551
552
/* Take the semaphore by a thread and verify the semaphore's553
* count is as expected554
*/555
for (int i = SEM_MAX_VAL - 1; i >= 0; i--) {556
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, 0);557
expect_k_sem_count_get_nomsg(&simple_sem, i);558
}559
560
/* Verify the max times a semaphore can be taken561
* continue to run k_sem_take, simple_sem can not be taken and562
* it's count will be zero563
*/564
for (int i = 0; i < 5; i++) {565
expect_k_sem_take_nomsg(&simple_sem, K_NO_WAIT, -EBUSY);566
567
expect_k_sem_count_get_nomsg(&simple_sem, 0U);568
}569
}570
"""571
reader = SourceFileTraceabilityReader_C()
572
573
info: SourceFileTraceabilityInfo = reader.read(
574
input_string, file_path="foo.cpp"
575
)576
577
assert isinstance(info, SourceFileTraceabilityInfo)
578
assert len(info.markers) == 0
579
assert len(info.functions) == 1
580
assert len(info.source_nodes) == 1
581
582
assert (
583
info.functions[0].name
584
== "ZTEST_USER(semaphore, test_k_sem_correct_count_limit)"
585
)586
assert (
587
info.functions[0].display_name
588
== "ZTEST_USER(semaphore, test_k_sem_correct_count_limit)"
589
)590
591
592
def test_102_known_c_macro_linux_syscall_define():
593
input_string = b"""\
594
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,595
struct __kernel_timespec __user *, tp)596
{597
// ...598
return error;599
}600
"""601
reader = SourceFileTraceabilityReader_C()
602
603
info: SourceFileTraceabilityInfo = reader.read(
604
input_string, file_path="foo.cpp"
605
)606
607
assert isinstance(info, SourceFileTraceabilityInfo)
608
assert len(info.markers) == 0
609
assert len(info.functions) == 1
610
assert len(info.source_nodes) == 0
611
612
expected_function_name = (
613
"SYSCALL_DEFINE2(clock_gettime, "614
"const clockid_t, which_clock, struct __kernel_timespec __user *, tp"615
")"616
)617
618
assert info.functions[0].name == expected_function_name
619
assert info.functions[0].display_name == expected_function_name
620
621
622
def test_103_error_recovery_linux_define_per_cpu_macro():
623
"""
624
Ensure this snippet derived from Linux kernel/softirq.c doesn't drive the625
C reader into NotImplementedError. DEFINE_PER_CPU expectedly confuses the626
parser. The rest of the snippet is special context that triggered undefined627
behavior manifesting as NotImplemented error in StrictDoc <= 0.22.0a1.628
"""629
input_string = b"""\
630
static DEFINE_PER_CPU(struct softirq_ctrl, softirq_ctrl) = {631
\t.lock\t= INIT_LOCAL_LOCK(softirq_ctrl.lock),
632
};633
634
struct lockdep_map bh_lock_map = {635
\t.name\t\t\t= "local_bh",
636
};637
638
static void handle_softirqs()639
{640
\tfoo("\\n", bar());
641
\tif (pending) { }
642
}643
"""644
645
reader = SourceFileTraceabilityReader_C()
646
647
info: SourceFileTraceabilityInfo = reader.read(
648
input_string, file_path="foo.c"
649
)650
651
assert isinstance(info, SourceFileTraceabilityInfo)
652
assert len(info.markers) == 0
653
assert len(info.functions) == 1
654
assert len(info.source_nodes) == 0
655
656
assert info.functions[0].name == "handle_softirqs()"
657
assert info.functions[0].display_name == "handle_softirqs"