Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/unittests/test_tokenizer.py
170833 views
1
#!/usr/bin/env python3
2
3
"""
4
Unit tests for struct/union member extractor class.
5
"""
6
7
8
import os
9
import re
10
import unittest
11
import sys
12
13
from unittest.mock import MagicMock
14
15
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
16
sys.path.insert(0, os.path.join(SRC_DIR, "../lib/python"))
17
18
from kdoc.c_lex import CToken, CTokenizer
19
from unittest_helper import run_unittest
20
21
#
22
# List of tests.
23
#
24
# The code will dynamically generate one test for each key on this dictionary.
25
#
26
def tokens_to_list(tokens):
27
tuples = []
28
29
for tok in tokens:
30
if tok.kind == CToken.SPACE:
31
continue
32
33
tuples += [(tok.kind, tok.value, tok.level)]
34
35
return tuples
36
37
38
def make_tokenizer_test(name, data):
39
"""
40
Create a test named ``name`` using parameters given by ``data`` dict.
41
"""
42
43
def test(self):
44
"""In-lined lambda-like function to run the test"""
45
46
#
47
# Check if logger is working
48
#
49
if "log_msg" in data:
50
with self.assertLogs() as cm:
51
tokenizer = CTokenizer(data["source"])
52
53
msg_found = False
54
for result in cm.output:
55
if data["log_msg"] in result:
56
msg_found = True
57
58
self.assertTrue(msg_found, f"Missing log {data['log_msg']}")
59
60
return
61
62
#
63
# Check if tokenizer is producing expected results
64
#
65
tokens = CTokenizer(data["source"]).tokens
66
67
result = tokens_to_list(tokens)
68
expected = tokens_to_list(data["expected"])
69
70
self.assertEqual(result, expected, msg=f"{name}")
71
72
return test
73
74
#: Tokenizer tests.
75
TESTS_TOKENIZER = {
76
"__run__": make_tokenizer_test,
77
78
"basic_tokens": {
79
"source": """
80
int a; // comment
81
float b = 1.23;
82
""",
83
"expected": [
84
CToken(CToken.NAME, "int"),
85
CToken(CToken.NAME, "a"),
86
CToken(CToken.ENDSTMT, ";"),
87
CToken(CToken.COMMENT, "// comment"),
88
CToken(CToken.NAME, "float"),
89
CToken(CToken.NAME, "b"),
90
CToken(CToken.OP, "="),
91
CToken(CToken.NUMBER, "1.23"),
92
CToken(CToken.ENDSTMT, ";"),
93
],
94
},
95
96
"depth_counters": {
97
"source": """
98
struct X {
99
int arr[10];
100
func(a[0], (b + c));
101
}
102
""",
103
"expected": [
104
CToken(CToken.STRUCT, "struct"),
105
CToken(CToken.NAME, "X"),
106
CToken(CToken.BEGIN, "{", brace_level=1),
107
108
CToken(CToken.NAME, "int", brace_level=1),
109
CToken(CToken.NAME, "arr", brace_level=1),
110
CToken(CToken.BEGIN, "[", brace_level=1, bracket_level=1),
111
CToken(CToken.NUMBER, "10", brace_level=1, bracket_level=1),
112
CToken(CToken.END, "]", brace_level=1),
113
CToken(CToken.ENDSTMT, ";", brace_level=1),
114
CToken(CToken.NAME, "func", brace_level=1),
115
CToken(CToken.BEGIN, "(", brace_level=1, paren_level=1),
116
CToken(CToken.NAME, "a", brace_level=1, paren_level=1),
117
CToken(CToken.BEGIN, "[", brace_level=1, paren_level=1, bracket_level=1),
118
CToken(CToken.NUMBER, "0", brace_level=1, paren_level=1, bracket_level=1),
119
CToken(CToken.END, "]", brace_level=1, paren_level=1),
120
CToken(CToken.PUNC, ",", brace_level=1, paren_level=1),
121
CToken(CToken.BEGIN, "(", brace_level=1, paren_level=2),
122
CToken(CToken.NAME, "b", brace_level=1, paren_level=2),
123
CToken(CToken.OP, "+", brace_level=1, paren_level=2),
124
CToken(CToken.NAME, "c", brace_level=1, paren_level=2),
125
CToken(CToken.END, ")", brace_level=1, paren_level=1),
126
CToken(CToken.END, ")", brace_level=1),
127
CToken(CToken.ENDSTMT, ";", brace_level=1),
128
CToken(CToken.END, "}"),
129
],
130
},
131
132
"mismatch_error": {
133
"source": "int a$ = 5;", # $ is illegal
134
"log_msg": "Unexpected token",
135
},
136
}
137
138
def make_private_test(name, data):
139
"""
140
Create a test named ``name`` using parameters given by ``data`` dict.
141
"""
142
143
def test(self):
144
"""In-lined lambda-like function to run the test"""
145
tokens = CTokenizer(data["source"])
146
result = str(tokens)
147
148
#
149
# Avoid whitespace false positives
150
#
151
result = re.sub(r"\s++", " ", result).strip()
152
expected = re.sub(r"\s++", " ", data["trimmed"]).strip()
153
154
msg = f"failed when parsing this source:\n{data['source']}"
155
self.assertEqual(result, expected, msg=msg)
156
157
return test
158
159
#: Tests to check if CTokenizer is handling properly public/private comments.
160
TESTS_PRIVATE = {
161
#
162
# Simplest case: no private. Ensure that trimming won't affect struct
163
#
164
"__run__": make_private_test,
165
"no private": {
166
"source": """
167
struct foo {
168
int a;
169
int b;
170
int c;
171
};
172
""",
173
"trimmed": """
174
struct foo {
175
int a;
176
int b;
177
int c;
178
};
179
""",
180
},
181
182
#
183
# Play "by the books" by always having a public in place
184
#
185
186
"balanced_private": {
187
"source": """
188
struct foo {
189
int a;
190
/* private: */
191
int b;
192
/* public: */
193
int c;
194
};
195
""",
196
"trimmed": """
197
struct foo {
198
int a;
199
int c;
200
};
201
""",
202
},
203
204
"balanced_non_greddy_private": {
205
"source": """
206
struct foo {
207
int a;
208
/* private: */
209
int b;
210
/* public: */
211
int c;
212
/* private: */
213
int d;
214
/* public: */
215
int e;
216
217
};
218
""",
219
"trimmed": """
220
struct foo {
221
int a;
222
int c;
223
int e;
224
};
225
""",
226
},
227
228
"balanced_inner_private": {
229
"source": """
230
struct foo {
231
struct {
232
int a;
233
/* private: ignore below */
234
int b;
235
/* public: but this should not be ignored */
236
};
237
int b;
238
};
239
""",
240
"trimmed": """
241
struct foo {
242
struct {
243
int a;
244
};
245
int b;
246
};
247
""",
248
},
249
250
#
251
# Test what happens if there's no public after private place
252
#
253
254
"unbalanced_private": {
255
"source": """
256
struct foo {
257
int a;
258
/* private: */
259
int b;
260
int c;
261
};
262
""",
263
"trimmed": """
264
struct foo {
265
int a;
266
};
267
""",
268
},
269
270
"unbalanced_inner_private": {
271
"source": """
272
struct foo {
273
struct {
274
int a;
275
/* private: ignore below */
276
int b;
277
/* but this should not be ignored */
278
};
279
int b;
280
};
281
""",
282
"trimmed": """
283
struct foo {
284
struct {
285
int a;
286
};
287
int b;
288
};
289
""",
290
},
291
292
"unbalanced_struct_group_tagged_with_private": {
293
"source": """
294
struct page_pool_params {
295
struct_group_tagged(page_pool_params_fast, fast,
296
unsigned int order;
297
unsigned int pool_size;
298
int nid;
299
struct device *dev;
300
struct napi_struct *napi;
301
enum dma_data_direction dma_dir;
302
unsigned int max_len;
303
unsigned int offset;
304
};
305
struct_group_tagged(page_pool_params_slow, slow,
306
struct net_device *netdev;
307
unsigned int queue_idx;
308
unsigned int flags;
309
/* private: used by test code only */
310
void (*init_callback)(netmem_ref netmem, void *arg);
311
void *init_arg;
312
};
313
};
314
""",
315
"trimmed": """
316
struct page_pool_params {
317
struct_group_tagged(page_pool_params_fast, fast,
318
unsigned int order;
319
unsigned int pool_size;
320
int nid;
321
struct device *dev;
322
struct napi_struct *napi;
323
enum dma_data_direction dma_dir;
324
unsigned int max_len;
325
unsigned int offset;
326
};
327
struct_group_tagged(page_pool_params_slow, slow,
328
struct net_device *netdev;
329
unsigned int queue_idx;
330
unsigned int flags;
331
};
332
};
333
""",
334
},
335
336
"unbalanced_two_struct_group_tagged_first_with_private": {
337
"source": """
338
struct page_pool_params {
339
struct_group_tagged(page_pool_params_slow, slow,
340
struct net_device *netdev;
341
unsigned int queue_idx;
342
unsigned int flags;
343
/* private: used by test code only */
344
void (*init_callback)(netmem_ref netmem, void *arg);
345
void *init_arg;
346
};
347
struct_group_tagged(page_pool_params_fast, fast,
348
unsigned int order;
349
unsigned int pool_size;
350
int nid;
351
struct device *dev;
352
struct napi_struct *napi;
353
enum dma_data_direction dma_dir;
354
unsigned int max_len;
355
unsigned int offset;
356
};
357
};
358
""",
359
"trimmed": """
360
struct page_pool_params {
361
struct_group_tagged(page_pool_params_slow, slow,
362
struct net_device *netdev;
363
unsigned int queue_idx;
364
unsigned int flags;
365
};
366
struct_group_tagged(page_pool_params_fast, fast,
367
unsigned int order;
368
unsigned int pool_size;
369
int nid;
370
struct device *dev;
371
struct napi_struct *napi;
372
enum dma_data_direction dma_dir;
373
unsigned int max_len;
374
unsigned int offset;
375
};
376
};
377
""",
378
},
379
"unbalanced_without_end_of_line": {
380
"source": """ \
381
struct page_pool_params { \
382
struct_group_tagged(page_pool_params_slow, slow, \
383
struct net_device *netdev; \
384
unsigned int queue_idx; \
385
unsigned int flags;
386
/* private: used by test code only */
387
void (*init_callback)(netmem_ref netmem, void *arg); \
388
void *init_arg; \
389
}; \
390
struct_group_tagged(page_pool_params_fast, fast, \
391
unsigned int order; \
392
unsigned int pool_size; \
393
int nid; \
394
struct device *dev; \
395
struct napi_struct *napi; \
396
enum dma_data_direction dma_dir; \
397
unsigned int max_len; \
398
unsigned int offset; \
399
}; \
400
};
401
""",
402
"trimmed": """
403
struct page_pool_params {
404
struct_group_tagged(page_pool_params_slow, slow,
405
struct net_device *netdev;
406
unsigned int queue_idx;
407
unsigned int flags;
408
};
409
struct_group_tagged(page_pool_params_fast, fast,
410
unsigned int order;
411
unsigned int pool_size;
412
int nid;
413
struct device *dev;
414
struct napi_struct *napi;
415
enum dma_data_direction dma_dir;
416
unsigned int max_len;
417
unsigned int offset;
418
};
419
};
420
""",
421
},
422
}
423
424
#: Dict containing all test groups fror CTokenizer
425
TESTS = {
426
"TestPublicPrivate": TESTS_PRIVATE,
427
"TestTokenizer": TESTS_TOKENIZER,
428
}
429
430
def setUp(self):
431
self.maxDiff = None
432
433
def build_test_class(group_name, table):
434
"""
435
Dynamically creates a class instance using type() as a generator
436
for a new class derivated from unittest.TestCase.
437
438
We're opting to do it inside a function to avoid the risk of
439
changing the globals() dictionary.
440
"""
441
442
class_dict = {
443
"setUp": setUp
444
}
445
446
run = table["__run__"]
447
448
for test_name, data in table.items():
449
if test_name == "__run__":
450
continue
451
452
class_dict[f"test_{test_name}"] = run(test_name, data)
453
454
cls = type(group_name, (unittest.TestCase,), class_dict)
455
456
return cls.__name__, cls
457
458
#
459
# Create classes and add them to the global dictionary
460
#
461
for group, table in TESTS.items():
462
t = build_test_class(group, table)
463
globals()[t[0]] = t[1]
464
465
#
466
# main
467
#
468
if __name__ == "__main__":
469
run_unittest(__file__)
470
471