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    def __init__(self, fname, text):
22        self.fname = fname
23        self.text = text
24
25
26class PatchMaker:
27    def __init__(self):
28        self.lines = []
29
30    def add_line(self, fname, text):
31        self.lines.append(Line(fname, text))
32
33    def get_patch_text(self):
34        base = '''From 125b77450f4c66b8fd9654319520bbe795c9ef31 Mon Sep 17 00:00:00 2001
35From: Simon Glass <sjg@chromium.org>
36Date: Sun, 14 Jun 2020 09:45:14 -0600
37Subject: [PATCH] Test commit
38
39This is a test commit.
40
41Signed-off-by: Simon Glass <sjg@chromium.org>
42---
43
44'''
45        lines = base.splitlines()
46
47        # Create the diffstat
48        change = 0
49        insert = 0
50        for line in self.lines:
51            lines.append(' %s      | 1 +' % line.fname)
52            change += 1
53            insert += 1
54        lines.append(' %d files changed, %d insertions(+)' % (change, insert))
55        lines.append('')
56
57        # Create the patch info for each file
58        for line in self.lines:
59            lines.append('diff --git a/%s b/%s' % (line.fname, line.fname))
60            lines.append('index 7837d459f18..5ba7840f68e 100644')
61            lines.append('--- a/%s' % line.fname)
62            lines.append('+++ b/%s' % line.fname)
63            lines += ('''@@ -121,6 +121,7 @@ enum uclass_id {
64 	UCLASS_W1,		/* Dallas 1-Wire bus */
65 	UCLASS_W1_EEPROM,	/* one-wire EEPROMs */
66 	UCLASS_WDT,		/* Watchdog Timer driver */
67+%s
68
69 	UCLASS_COUNT,
70 	UCLASS_INVALID = -1,
71''' % line.text).splitlines()
72        lines.append('---')
73        lines.append('2.17.1')
74
75        return '\n'.join(lines)
76
77    def get_patch(self):
78        inhandle, inname = tempfile.mkstemp()
79        infd = os.fdopen(inhandle, 'w')
80        infd.write(self.get_patch_text())
81        infd.close()
82        return inname
83
84    def run_checkpatch(self):
85        return checkpatch.CheckPatch(self.get_patch(), show_types=True)
86
87
88class TestPatch(unittest.TestCase):
89    """Test the u_boot_line() function in checkpatch.pl"""
90
91    def testBasic(self):
92        """Test basic filter operation"""
93        data='''
94
95From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001
96From: Simon Glass <sjg@chromium.org>
97Date: Thu, 28 Apr 2011 09:58:51 -0700
98Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support
99
100This adds functions to enable/disable clocks and reset to on-chip peripherals.
101
102cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type
103   ‘long long unsigned int’, but argument 3 has type
104   ‘u64 {aka long unsigned int}’ [-Wformat=]
105
106BUG=chromium-os:13875
107TEST=build U-Boot for Seaboard, boot
108
109Change-Id: I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413
110
111Review URL: http://codereview.chromium.org/6900006
112
113Signed-off-by: Simon Glass <sjg@chromium.org>
114---
115 arch/arm/cpu/armv7/tegra2/Makefile         |    2 +-
116 arch/arm/cpu/armv7/tegra2/ap20.c           |   57 ++----
117 arch/arm/cpu/armv7/tegra2/clock.c          |  163 +++++++++++++++++
118'''
119        expected='''Message-Id: <19991231235959.0.I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413@changeid>
120
121
122From 656c9a8c31fa65859d924cd21da920d6ba537fad Mon Sep 17 00:00:00 2001
123From: Simon Glass <sjg@chromium.org>
124Date: Thu, 28 Apr 2011 09:58:51 -0700
125Subject: [PATCH (resend) 3/7] Tegra2: Add more clock support
126
127This adds functions to enable/disable clocks and reset to on-chip peripherals.
128
129cmd/pci.c:152:11: warning: format ‘%llx’ expects argument of type
130   ‘long long unsigned int’, but argument 3 has type
131   ‘u64 {aka long unsigned int}’ [-Wformat=]
132
133Signed-off-by: Simon Glass <sjg@chromium.org>
134---
135
136 arch/arm/cpu/armv7/tegra2/Makefile         |    2 +-
137 arch/arm/cpu/armv7/tegra2/ap20.c           |   57 ++----
138 arch/arm/cpu/armv7/tegra2/clock.c          |  163 +++++++++++++++++
139'''
140        out = ''
141        inhandle, inname = tempfile.mkstemp()
142        infd = os.fdopen(inhandle, 'w', encoding='utf-8')
143        infd.write(data)
144        infd.close()
145
146        exphandle, expname = tempfile.mkstemp()
147        expfd = os.fdopen(exphandle, 'w', encoding='utf-8')
148        expfd.write(expected)
149        expfd.close()
150
151        # Normally by the time we call fix_patch we've already collected
152        # metadata.  Here, we haven't, but at least fake up something.
153        # Set the "count" to -1 which tells fix_patch to use a bogus/fixed
154        # time for generating the Message-Id.
155        com = commit.Commit('')
156        com.change_id = 'I80fe1d0c0b7dd10aa58ce5bb1d9290b6664d5413'
157        com.count = -1
158
159        patchstream.fix_patch(None, inname, series.Series(), com)
160
161        rc = os.system('diff -u %s %s' % (inname, expname))
162        self.assertEqual(rc, 0)
163
164        os.remove(inname)
165        os.remove(expname)
166
167    def GetData(self, data_type):
168        data='''From 4924887af52713cabea78420eff03badea8f0035 Mon Sep 17 00:00:00 2001
169From: Simon Glass <sjg@chromium.org>
170Date: Thu, 7 Apr 2011 10:14:41 -0700
171Subject: [PATCH 1/4] Add microsecond boot time measurement
172
173This defines the basics of a new boot time measurement feature. This allows
174logging of very accurate time measurements as the boot proceeds, by using
175an available microsecond counter.
176
177%s
178---
179 README              |   11 ++++++++
180 MAINTAINERS         |    3 ++
181 common/bootstage.c  |   50 ++++++++++++++++++++++++++++++++++++
182 include/bootstage.h |   71 +++++++++++++++++++++++++++++++++++++++++++++++++++
183 include/common.h    |    8 ++++++
184 5 files changed, 141 insertions(+), 0 deletions(-)
185 create mode 100644 common/bootstage.c
186 create mode 100644 include/bootstage.h
187
188diff --git a/README b/README
189index 6f3748d..f9e4e65 100644
190--- a/README
191+++ b/README
192@@ -2026,6 +2026,17 @@ The following options need to be configured:
193 		example, some LED's) on your board. At the moment,
194 		the following checkpoints are implemented:
195
196+- Time boot progress
197+		CONFIG_BOOTSTAGE
198+
199+		Define this option to enable microsecond boot stage timing
200+		on supported platforms. For this to work your platform
201+		needs to define a function timer_get_us() which returns the
202+		number of microseconds since reset. This would normally
203+		be done in your SOC or board timer.c file.
204+
205+		You can add calls to bootstage_mark() to set time markers.
206+
207 - Standalone program support:
208 		CONFIG_STANDALONE_LOAD_ADDR
209
210diff --git a/MAINTAINERS b/MAINTAINERS
211index b167b028ec..beb7dc634f 100644
212--- a/MAINTAINERS
213+++ b/MAINTAINERS
214@@ -474,3 +474,8 @@ S:	Maintained
215 T:	git git://git.denx.de/u-boot.git
216 F:	*
217 F:	*/
218+
219+BOOTSTAGE
220+M:	Simon Glass <sjg@chromium.org>
221+L:	u-boot@lists.denx.de
222+F:	common/bootstage.c
223diff --git a/common/bootstage.c b/common/bootstage.c
224new file mode 100644
225index 0000000..2234c87
226--- /dev/null
227+++ b/common/bootstage.c
228@@ -0,0 +1,37 @@
229+%s
230+/*
231+ * Copyright (c) 2011, Google Inc. All rights reserved.
232+ *
233+ */
234+
235+/*
236+ * This module records the progress of boot and arbitrary commands, and
237+ * permits accurate timestamping of each. The records can optionally be
238+ * passed to kernel in the ATAGs
239+ */
240+
241+#include <common.h>
242+
243+struct bootstage_record {
244+	u32 time_us;
245+	const char *name;
246+};
247+
248+static struct bootstage_record record[BOOTSTAGE_COUNT];
249+
250+u32 bootstage_mark(enum bootstage_id id, const char *name)
251+{
252+	struct bootstage_record *rec = &record[id];
253+
254+	/* Only record the first event for each */
255+%sif (!rec->name) {
256+		rec->time_us = (u32)timer_get_us();
257+		rec->name = name;
258+	}
259+	if (!rec->name &&
260+	%ssomething_else) {
261+		rec->time_us = (u32)timer_get_us();
262+		rec->name = name;
263+	}
264+%sreturn rec->time_us;
265+}
266--
2671.7.3.1
268'''
269        signoff = 'Signed-off-by: Simon Glass <sjg@chromium.org>\n'
270        license = '// SPDX-License-Identifier: GPL-2.0+'
271        tab = '	'
272        indent = '    '
273        if data_type == 'good':
274            pass
275        elif data_type == 'no-signoff':
276            signoff = ''
277        elif data_type == 'no-license':
278            license = ''
279        elif data_type == 'spaces':
280            tab = '   '
281        elif data_type == 'indent':
282            indent = tab
283        else:
284            print('not implemented')
285        return data % (signoff, license, tab, indent, tab)
286
287    def SetupData(self, data_type):
288        inhandle, inname = tempfile.mkstemp()
289        infd = os.fdopen(inhandle, 'w')
290        data = self.GetData(data_type)
291        infd.write(data)
292        infd.close()
293        return inname
294
295    def testGood(self):
296        """Test checkpatch operation"""
297        inf = self.SetupData('good')
298        result = checkpatch.CheckPatch(inf)
299        self.assertEqual(result.ok, True)
300        self.assertEqual(result.problems, [])
301        self.assertEqual(result.errors, 0)
302        self.assertEqual(result.warnings, 0)
303        self.assertEqual(result.checks, 0)
304        self.assertEqual(result.lines, 62)
305        os.remove(inf)
306
307    def testNoSignoff(self):
308        inf = self.SetupData('no-signoff')
309        result = checkpatch.CheckPatch(inf)
310        self.assertEqual(result.ok, False)
311        self.assertEqual(len(result.problems), 1)
312        self.assertEqual(result.errors, 1)
313        self.assertEqual(result.warnings, 0)
314        self.assertEqual(result.checks, 0)
315        self.assertEqual(result.lines, 62)
316        os.remove(inf)
317
318    def testNoLicense(self):
319        inf = self.SetupData('no-license')
320        result = checkpatch.CheckPatch(inf)
321        self.assertEqual(result.ok, False)
322        self.assertEqual(len(result.problems), 1)
323        self.assertEqual(result.errors, 0)
324        self.assertEqual(result.warnings, 1)
325        self.assertEqual(result.checks, 0)
326        self.assertEqual(result.lines, 62)
327        os.remove(inf)
328
329    def testSpaces(self):
330        inf = self.SetupData('spaces')
331        result = checkpatch.CheckPatch(inf)
332        self.assertEqual(result.ok, False)
333        self.assertEqual(len(result.problems), 3)
334        self.assertEqual(result.errors, 0)
335        self.assertEqual(result.warnings, 3)
336        self.assertEqual(result.checks, 0)
337        self.assertEqual(result.lines, 62)
338        os.remove(inf)
339
340    def testIndent(self):
341        inf = self.SetupData('indent')
342        result = checkpatch.CheckPatch(inf)
343        self.assertEqual(result.ok, False)
344        self.assertEqual(len(result.problems), 1)
345        self.assertEqual(result.errors, 0)
346        self.assertEqual(result.warnings, 0)
347        self.assertEqual(result.checks, 1)
348        self.assertEqual(result.lines, 62)
349        os.remove(inf)
350
351    def checkSingleMessage(self, pm, msg, pmtype = 'warning'):
352        """Helper function to run checkpatch and check the result
353
354        Args:
355            pm: PatchMaker object to use
356            msg" Expected message (e.g. 'LIVETREE')
357            pmtype: Type of problem ('error', 'warning')
358        """
359        result = pm.run_checkpatch()
360        if pmtype == 'warning':
361            self.assertEqual(result.warnings, 1)
362        elif pmtype == 'error':
363            self.assertEqual(result.errors, 1)
364        if len(result.problems) != 1:
365            print(result.problems)
366        self.assertEqual(len(result.problems), 1)
367        self.assertIn(msg, result.problems[0]['cptype'])
368
369    def testUclass(self):
370        """Test for possible new uclass"""
371        pm = PatchMaker()
372        pm.add_line('include/dm/uclass-id.h', 'UCLASS_WIBBLE,')
373        self.checkSingleMessage(pm, 'NEW_UCLASS')
374
375    def testLivetree(self):
376        """Test for using the livetree API"""
377        pm = PatchMaker()
378        pm.add_line('common/main.c', 'fdtdec_do_something()')
379        self.checkSingleMessage(pm, 'LIVETREE')
380
381    def testNewCommand(self):
382        """Test for adding a new command"""
383        pm = PatchMaker()
384        pm.add_line('common/main.c', 'do_wibble(struct cmd_tbl *cmd_tbl)')
385        self.checkSingleMessage(pm, 'CMD_TEST')
386
387    def testPreferIf(self):
388        """Test for using #ifdef"""
389        pm = PatchMaker()
390        pm.add_line('common/main.c', '#ifdef CONFIG_YELLOW')
391        pm.add_line('common/init.h', '#ifdef CONFIG_YELLOW')
392        pm.add_line('fred.dtsi', '#ifdef CONFIG_YELLOW')
393        self.checkSingleMessage(pm, "PREFER_IF")
394
395    def testCommandUseDefconfig(self):
396        """Test for enabling/disabling commands using preprocesor"""
397        pm = PatchMaker()
398        pm.add_line('common/main.c', '#undef CONFIG_CMD_WHICH')
399        self.checkSingleMessage(pm, 'DEFINE_CONFIG_CMD', 'error')
400
401    def testBarredIncludeInHdr(self):
402        """Test for using a barred include in a header file"""
403        pm = PatchMaker()
404        #pm.add_line('include/myfile.h', '#include <common.h>')
405        pm.add_line('include/myfile.h', '#include <dm.h>')
406        self.checkSingleMessage(pm, 'BARRED_INCLUDE_IN_HDR', 'error')
407
408    def testConfigIsEnabledConfig(self):
409        """Test for accidental CONFIG_IS_ENABLED(CONFIG_*) calls"""
410        pm = PatchMaker()
411        pm.add_line('common/main.c', 'if (CONFIG_IS_ENABLED(CONFIG_CLK))')
412        self.checkSingleMessage(pm, 'CONFIG_IS_ENABLED_CONFIG', 'error')
413
414    def check_struct(self, auto, suffix, warning):
415        """Check one of the warnings for struct naming
416
417        Args:
418            auto: Auto variable name, e.g. 'per_child_auto'
419            suffix: Suffix to expect on member, e.g. '_priv'
420            warning: Warning name, e.g. 'PRIV_AUTO'
421        """
422        pm = PatchMaker()
423        pm.add_line('common/main.c', '.%s = sizeof(struct(fred)),' % auto)
424        pm.add_line('common/main.c', '.%s = sizeof(struct(mary%s)),' %
425                    (auto, suffix))
426        self.checkSingleMessage(
427            pm, warning, "struct 'fred' should have a %s suffix" % suffix)
428
429    def testDmDriverAuto(self):
430        """Check for the correct suffix on 'struct driver' auto members"""
431        self.check_struct('priv_auto', '_priv', 'PRIV_AUTO')
432        self.check_struct('plat_auto', '_plat', 'PLAT_AUTO')
433        self.check_struct('per_child_auto', '_priv', 'CHILD_PRIV_AUTO')
434        self.check_struct('per_child_plat_auto', '_plat', 'CHILD_PLAT_AUTO')
435
436    def testDmUclassAuto(self):
437        """Check for the correct suffix on 'struct uclass' auto members"""
438        # Some of these are omitted since they match those from struct driver
439        self.check_struct('per_device_auto', '_priv', 'DEVICE_PRIV_AUTO')
440        self.check_struct('per_device_plat_auto', '_plat', 'DEVICE_PLAT_AUTO')
441
442
443if __name__ == "__main__":
444    unittest.main()
445    gitutil.RunTests()
446