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() 534