"Das U-Boot" Source Tree
1# -*- coding: utf-8 -*-
2# SPDX-License-Identifier: GPL-2.0+
3#
4# Tests for U-Boot-specific checkpatch.pl features
5#
6# Copyright (c) 2011 The Chromium OS Authors.
7#
8
9import os
10import tempfile
11import unittest
12
13from patman import checkpatch
14from patman import gitutil
15from patman import patchstream
16from patman import series
17from patman import commit
18
19
20class Line:
21 """Single changed line in one file in a patch
22
23 Args:
24 fname (str): Filename containing the added line
25 text (str): Text of the added line
26 """
27 def __init__(self, fname, text):
28 self.fname = fname
29 self.text = text
30
31
32class PatchMaker:
33 """Makes a patch for checking with checkpatch.pl
34
35 The idea here is to create a patch which adds one line in one file,
36 intended to provoke a checkpatch error or warning. The base patch is empty
37 (i.e. invalid), so you should call add_line() to add at least one line.
38 """
39 def __init__(self):
40 """Set up the PatchMaker object
41
42 Properties:
43 lines (list of Line): List of lines to add to the patch. Note that
44 each line has both a file and some text associated with it,
45 since for simplicity we just add a single line for each file
46 """
47 self.lines = []
48
49 def add_line(self, fname, text):
50 """Add to the list of filename/line pairs"""
51 self.lines.append(Line(fname, text))
52
53 def get_patch_text(self):
54 """Build the patch text
55
56 Takes a base patch and adds a diffstat and patch for each filename/line
57 pair in the list.
58
59 Returns:
60 str: Patch text ready for submission to checkpatch
61 """
62 base = '''From 125b77450f4c66b8fd9654319520bbe795c9ef31 Mon Sep 17 00:00:00 2001
63From: Simon Glass <sjg@chromium.org>
64Date: Sun, 14 Jun 2020 09:45:14 -0600
65Subject: [PATCH] Test commit
66
67This is a test commit.
68
69Signed-off-by: Simon Glass <sjg@chromium.org>
70---
71
72'''
73 lines = base.splitlines()
74
75 # Create the diffstat
76 change = 0
77 insert = 0
78 for line in self.lines:
79 lines.append(' %s | 1 +' % line.fname)
80 change += 1
81 insert += 1
82 lines.append(' %d files changed, %d insertions(+)' % (change, insert))
83 lines.append('')
84
85 # Create the patch info for each file
86 for line in self.lines:
87 lines.append('diff --git a/%s b/%s' % (line.fname, line.fname))
88 lines.append('index 7837d459f18..5ba7840f68e 100644')
89 lines.append('--- a/%s' % line.fname)
90 lines.append('+++ b/%s' % line.fname)
91 lines += ('''@@ -121,6 +121,7 @@ enum uclass_id {
92 UCLASS_W1, /* Dallas 1-Wire bus */
93 UCLASS_W1_EEPROM, /* one-wire EEPROMs */
94 UCLASS_WDT, /* Watchdog Timer driver */
95+%s
96
97 UCLASS_COUNT,
98 UCLASS_INVALID = -1,
99''' % line.text).splitlines()
100 lines.append('---')
101 lines.append('2.17.1')
102
103 return '\n'.join(lines)
104
105 def get_patch(self):
106 """Get the patch text and write it into a temporary file
107
108 Returns:
109 str: Filename containing the patch
110 """
111 inhandle, inname = tempfile.mkstemp()
112 infd = os.fdopen(inhandle, 'w')
113 infd.write(self.get_patch_text())
114 infd.close()
115 return inname
116
117 def run_checkpatch(self):
118 """Run checkpatch on the patch file
119
120 Returns:
121 namedtuple containing:
122 ok: False=failure, True=ok
123 problems: List of problems, each a dict:
124 'type'; error or warning
125 'msg': text message
126 'file' : filename
127 'line': line number
128 errors: Number of errors
129 warnings: Number of warnings
130 checks: Number of checks
131 lines: Number of lines
132 stdout: Full output of checkpatch
133 """
134 return checkpatch.check_patch(self.get_patch(), show_types=True)
135
136
137class TestPatch(unittest.TestCase):
138 """Test the u_boot_line() function in checkpatch.pl"""
139
140 def test_basic(self):
141 """Test basic filter operation"""
142 data='''
143
144From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001
145From: Simon Glass <sjg@chromium.org>
146Date: Thu, 28 Apr 2011 09:58:51 -0700
147Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support
148
149This adds functions to enable/disable clocks and reset to on-chip peripherals.
150
151cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type
152 ‘long long unsigned int’, but argument 3 has type
153 ‘u64 {aka long unsigned int}’ [-Wformat=]
154
155BUG=chromium-os:13875
156TEST=build U-Boot for Seaboard, boot
157
158Change-Id: I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413
159
160Review URL: http://codereview.chromium.org/6900006
161
162Signed-off-by: Simon Glass <sjg@chromium.org>
163---
164 arch/arm/cpu/armv7/tegra2/Makefile | 2 +-
165 arch/arm/cpu/armv7/tegra2/ap20.c | 57 ++----
166 arch/arm/cpu/armv7/tegra2/clock.c | 163 +++++++++++++++++
167'''
168 expected='''Message-Id: <19991231235959.0.I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413@changeid>
169
170
171From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001
172From: Simon Glass <sjg@chromium.org>
173Date: Thu, 28 Apr 2011 09:58:51 -0700
174Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support
175
176This adds functions to enable/disable clocks and reset to on-chip peripherals.
177
178cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type
179 ‘long long unsigned int’, but argument 3 has type
180 ‘u64 {aka long unsigned int}’ [-Wformat=]
181
182Signed-off-by: Simon Glass <sjg@chromium.org>
183---
184
185 arch/arm/cpu/armv7/tegra2/Makefile | 2 +-
186 arch/arm/cpu/armv7/tegra2/ap20.c | 57 ++----
187 arch/arm/cpu/armv7/tegra2/clock.c | 163 +++++++++++++++++
188'''
189 out = ''
190 inhandle, inname = tempfile.mkstemp()
191 infd = os.fdopen(inhandle, 'w', encoding='utf-8')
192 infd.write(data)
193 infd.close()
194
195 exphandle, expname = tempfile.mkstemp()
196 expfd = os.fdopen(exphandle, 'w', encoding='utf-8')
197 expfd.write(expected)
198 expfd.close()
199
200 # Normally by the time we call fix_patch we've already collected
201 # metadata. Here, we haven't, but at least fake up something.
202 # Set the "count" to -1 which tells fix_patch to use a bogus/fixed
203 # time for generating the Message-Id.
204 com = commit.Commit('')
205 com.change_id = 'I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413'
206 com.count = -1
207
208 patchstream.fix_patch(None, inname, series.Series(), com)
209
210 rc = os.system('diff -u %s %s' % (inname, expname))
211 self.assertEqual(rc, 0)
212 os.remove(inname)
213
214 # Test whether the keep_change_id settings works.
215 inhandle, inname = tempfile.mkstemp()
216 infd = os.fdopen(inhandle, 'w', encoding='utf-8')
217 infd.write(data)
218 infd.close()
219
220 patchstream.fix_patch(None, inname, series.Series(), com,
221 keep_change_id=True)
222
223 with open(inname, 'r') as f:
224 content = f.read()
225 self.assertIn(
226 'Change-Id: I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413',
227 content)
228
229 os.remove(inname)
230 os.remove(expname)
231
232 def get_data(self, data_type):
233 data='''From 4924887af52713cabea78420eff03badea8f0035 Mon Sep 17 00:00:00 2001
234From: Simon Glass <sjg@chromium.org>
235Date: Thu, 7 Apr 2011 10:14:41 -0700
236Subject: [PATCH 1/4] Add microsecond boot time measurement
237
238This defines the basics of a new boot time measurement feature. This allows
239logging of very accurate time measurements as the boot proceeds, by using
240an available microsecond counter.
241
242%s
243---
244 README | 11 ++++++++
245 MAINTAINERS | 3 ++
246 common/bootstage.c | 50 ++++++++++++++++++++++++++++++++++++
247 include/bootstage.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++
248 include/common.h | 8 ++++++
249 5 files changed, 141 insertions(+), 0 deletions(-)
250 create mode 100644 common/bootstage.c
251 create mode 100644 include/bootstage.h
252
253diff --git a/README b/README
254index 6f3748d..f9e4e65 100644
255--- a/README
256+++ b/README
257@@ -2026,6 +2026,17 @@ The following options need to be configured:
258 example, some LED's) on your board. At the moment,
259 the following checkpoints are implemented:
260
261+- Time boot progress
262+ CONFIG_BOOTSTAGE
263+
264+ Define this option to enable microsecond boot stage timing
265+ on supported platforms. For this to work your platform
266+ needs to define a function timer_get_us() which returns the
267+ number of microseconds since reset. This would normally
268+ be done in your SOC or board timer.c file.
269+
270+ You can add calls to bootstage_mark() to set time markers.
271+
272 - Standalone program support:
273 CONFIG_STANDALONE_LOAD_ADDR
274
275diff --git a/MAINTAINERS b/MAINTAINERS
276index b167b028ec..beb7dc634f 100644
277--- a/MAINTAINERS
278+++ b/MAINTAINERS
279@@ -474,3 +474,8 @@ S: Maintained
280 T: git git://git.denx.de/u-boot.git
281 F: *
282 F: */
283+
284+BOOTSTAGE
285+M: Simon Glass <sjg@chromium.org>
286+L: u-boot@lists.denx.de
287+F: common/bootstage.c
288diff --git a/common/bootstage.c b/common/bootstage.c
289new file mode 100644
290index 0000000..2234c87
291--- /dev/null
292+++ b/common/bootstage.c
293@@ -0,0 +1,37 @@
294+%s
295+/*
296+ * Copyright (c) 2011, Google Inc. All rights reserved.
297+ *
298+ */
299+
300+/*
301+ * This module records the progress of boot and arbitrary commands, and
302+ * permits accurate timestamping of each. The records can optionally be
303+ * passed to kernel in the ATAGs
304+ */
305+
306+#include <config.h>
307+
308+struct bootstage_record {
309+ u32 time_us;
310+ const char *name;
311+};
312+
313+static struct bootstage_record record[BOOTSTAGE_COUNT];
314+
315+u32 bootstage_mark(enum bootstage_id id, const char *name)
316+{
317+ struct bootstage_record *rec = &record[id];
318+
319+ /* Only record the first event for each */
320+%sif (!rec->name) {
321+ rec->time_us = (u32)timer_get_us();
322+ rec->name = name;
323+ }
324+ if (!rec->name &&
325+ %ssomething_else) {
326+ rec->time_us = (u32)timer_get_us();
327+ rec->name = name;
328+ }
329+%sreturn rec->time_us;
330+}
331--
3321.7.3.1
333'''
334 signoff = 'Signed-off-by: Simon Glass <sjg@chromium.org>\n'
335 license = '// SPDX-License-Identifier: GPL-2.0+'
336 tab = ' '
337 indent = ' '
338 if data_type == 'good':
339 pass
340 elif data_type == 'no-signoff':
341 signoff = ''
342 elif data_type == 'no-license':
343 license = ''
344 elif data_type == 'spaces':
345 tab = ' '
346 elif data_type == 'indent':
347 indent = tab
348 else:
349 print('not implemented')
350 return data % (signoff, license, tab, indent, tab)
351
352 def setup_data(self, data_type):
353 inhandle, inname = tempfile.mkstemp()
354 infd = os.fdopen(inhandle, 'w')
355 data = self.get_data(data_type)
356 infd.write(data)
357 infd.close()
358 return inname
359
360 def test_good(self):
361 """Test checkpatch operation"""
362 inf = self.setup_data('good')
363 result = checkpatch.check_patch(inf)
364 self.assertEqual(result.ok, True)
365 self.assertEqual(result.problems, [])
366 self.assertEqual(result.errors, 0)
367 self.assertEqual(result.warnings, 0)
368 self.assertEqual(result.checks, 0)
369 self.assertEqual(result.lines, 62)
370 os.remove(inf)
371
372 def test_no_signoff(self):
373 inf = self.setup_data('no-signoff')
374 result = checkpatch.check_patch(inf)
375 self.assertEqual(result.ok, False)
376 self.assertEqual(len(result.problems), 1)
377 self.assertEqual(result.errors, 1)
378 self.assertEqual(result.warnings, 0)
379 self.assertEqual(result.checks, 0)
380 self.assertEqual(result.lines, 62)
381 os.remove(inf)
382
383 def test_no_license(self):
384 inf = self.setup_data('no-license')
385 result = checkpatch.check_patch(inf)
386 self.assertEqual(result.ok, False)
387 self.assertEqual(len(result.problems), 1)
388 self.assertEqual(result.errors, 0)
389 self.assertEqual(result.warnings, 1)
390 self.assertEqual(result.checks, 0)
391 self.assertEqual(result.lines, 62)
392 os.remove(inf)
393
394 def test_spaces(self):
395 inf = self.setup_data('spaces')
396 result = checkpatch.check_patch(inf)
397 self.assertEqual(result.ok, False)
398 self.assertEqual(len(result.problems), 3)
399 self.assertEqual(result.errors, 0)
400 self.assertEqual(result.warnings, 3)
401 self.assertEqual(result.checks, 0)
402 self.assertEqual(result.lines, 62)
403 os.remove(inf)
404
405 def test_indent(self):
406 inf = self.setup_data('indent')
407 result = checkpatch.check_patch(inf)
408 self.assertEqual(result.ok, False)
409 self.assertEqual(len(result.problems), 1)
410 self.assertEqual(result.errors, 0)
411 self.assertEqual(result.warnings, 0)
412 self.assertEqual(result.checks, 1)
413 self.assertEqual(result.lines, 62)
414 os.remove(inf)
415
416 def check_single_message(self, pm, msg, pmtype = 'warning'):
417 """Helper function to run checkpatch and check the result
418
419 Args:
420 pm: PatchMaker object to use
421 msg: Expected message (e.g. 'LIVETREE')
422 pmtype: Type of problem ('error', 'warning')
423 """
424 result = pm.run_checkpatch()
425 if pmtype == 'warning':
426 self.assertEqual(result.warnings, 1)
427 elif pmtype == 'error':
428 self.assertEqual(result.errors, 1)
429 if len(result.problems) != 1:
430 print(result.problems)
431 self.assertEqual(len(result.problems), 1)
432 self.assertIn(msg, result.problems[0]['cptype'])
433
434 def test_uclass(self):
435 """Test for possible new uclass"""
436 pm = PatchMaker()
437 pm.add_line('include/dm/uclass-id.h', 'UCLASS_WIBBLE,')
438 self.check_single_message(pm, 'NEW_UCLASS')
439
440 def test_livetree(self):
441 """Test for using the livetree API"""
442 pm = PatchMaker()
443 pm.add_line('common/main.c', 'fdtdec_do_something()')
444 self.check_single_message(pm, 'LIVETREE')
445
446 def test_new_command(self):
447 """Test for adding a new command"""
448 pm = PatchMaker()
449 pm.add_line('common/main.c', 'do_wibble(struct cmd_tbl *cmd_tbl)')
450 self.check_single_message(pm, 'CMD_TEST')
451
452 def test_prefer_if(self):
453 """Test for using #ifdef"""
454 pm = PatchMaker()
455 pm.add_line('common/main.c', '#ifdef CONFIG_YELLOW')
456 pm.add_line('common/init.h', '#ifdef CONFIG_YELLOW')
457 pm.add_line('fred.dtsi', '#ifdef CONFIG_YELLOW')
458 self.check_single_message(pm, "PREFER_IF")
459
460 def test_command_use_defconfig(self):
461 """Test for enabling/disabling commands using preprocesor"""
462 pm = PatchMaker()
463 pm.add_line('common/main.c', '#undef CONFIG_CMD_WHICH')
464 self.check_single_message(pm, 'DEFINE_CONFIG_SYM', 'error')
465
466 def test_barred_include_in_hdr(self):
467 """Test for using a barred include in a header file"""
468 pm = PatchMaker()
469 pm.add_line('include/myfile.h', '#include <dm.h>')
470 self.check_single_message(pm, 'BARRED_INCLUDE_IN_HDR', 'error')
471
472 def test_barred_include_common_h(self):
473 """Test for adding common.h to a file"""
474 pm = PatchMaker()
475 pm.add_line('include/myfile.h', '#include <common.h>')
476 self.check_single_message(pm, 'BARRED_INCLUDE_COMMON_H', 'error')
477
478 def test_config_is_enabled_config(self):
479 """Test for accidental CONFIG_IS_ENABLED(CONFIG_*) calls"""
480 pm = PatchMaker()
481 pm.add_line('common/main.c', 'if (CONFIG_IS_ENABLED(CONFIG_CLK))')
482 self.check_single_message(pm, 'CONFIG_IS_ENABLED_CONFIG', 'error')
483
484 def check_struct(self, auto, suffix, warning):
485 """Check one of the warnings for struct naming
486
487 Args:
488 auto: Auto variable name, e.g. 'per_child_auto'
489 suffix: Suffix to expect on member, e.g. '_priv'
490 warning: Warning name, e.g. 'PRIV_AUTO'
491 """
492 pm = PatchMaker()
493 pm.add_line('common/main.c', '.%s = sizeof(struct(fred)),' % auto)
494 pm.add_line('common/main.c', '.%s = sizeof(struct(mary%s)),' %
495 (auto, suffix))
496 self.check_single_message(
497 pm, warning, "struct 'fred' should have a %s suffix" % suffix)
498
499 def test_dm_driver_auto(self):
500 """Check for the correct suffix on 'struct driver' auto members"""
501 self.check_struct('priv_auto', '_priv', 'PRIV_AUTO')
502 self.check_struct('plat_auto', '_plat', 'PLAT_AUTO')
503 self.check_struct('per_child_auto', '_priv', 'CHILD_PRIV_AUTO')
504 self.check_struct('per_child_plat_auto', '_plat', 'CHILD_PLAT_AUTO')
505
506 def test_dm_uclass_auto(self):
507 """Check for the correct suffix on 'struct uclass' auto members"""
508 # Some of these are omitted since they match those from struct driver
509 self.check_struct('per_device_auto', '_priv', 'DEVICE_PRIV_AUTO')
510 self.check_struct('per_device_plat_auto', '_plat', 'DEVICE_PLAT_AUTO')
511
512 def check_strl(self, func):
513 """Check one of the checks for strn(cpy|cat)"""
514 pm = PatchMaker()
515 pm.add_line('common/main.c', "strn%s(foo, bar, sizeof(foo));" % func)
516 self.check_single_message(pm, "STRL",
517 "strl%s is preferred over strn%s because it always produces a nul-terminated string\n"
518 % (func, func))
519
520 def test_strl(self):
521 """Check for uses of strn(cat|cpy)"""
522 self.check_strl("cat");
523 self.check_strl("cpy");
524
525 def test_schema(self):
526 """Check for uses of strn(cat|cpy)"""
527 pm = PatchMaker()
528 pm.add_line('arch/sandbox/dts/sandbox.dtsi', '\tu-boot,dm-pre-proper;')
529 self.check_single_message(pm, 'PRE_SCHEMA', 'error')
530
531if __name__ == "__main__":
532 unittest.main()
533 gitutil.RunTests()