1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0 3# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4# 5# pylint: disable=C0413,R0904 6 7 8""" 9Unit tests for kernel-doc CMatch. 10""" 11 12import os 13import re 14import sys 15import unittest 16 17 18# Import Python modules 19 20SRC_DIR = os.path.dirname(os.path.realpath(__file__)) 21sys.path.insert(0, os.path.join(SRC_DIR, "../lib/python")) 22 23from kdoc.c_lex import CMatch 24from kdoc.kdoc_re import KernRe 25from unittest_helper import run_unittest 26 27# 28# Override unittest.TestCase to better compare diffs ignoring whitespaces 29# 30class TestCaseDiff(unittest.TestCase): 31 """ 32 Disable maximum limit on diffs and add a method to better 33 handle diffs with whitespace differences. 34 """ 35 36 @classmethod 37 def setUpClass(cls): 38 """Ensure that there won't be limit for diffs""" 39 cls.maxDiff = None 40 41 42# 43# Tests doing with different macros 44# 45 46class TestSearch(TestCaseDiff): 47 """ 48 Test search mechanism 49 """ 50 51 def test_search_acquires_simple(self): 52 line = "__acquires(ctx) foo();" 53 result = ", ".join(CMatch("__acquires").search(line)) 54 self.assertEqual(result, "__acquires(ctx)") 55 56 def test_search_acquires_multiple(self): 57 line = "__acquires(ctx) __acquires(other) bar();" 58 result = ", ".join(CMatch("__acquires").search(line)) 59 self.assertEqual(result, "__acquires(ctx), __acquires(other)") 60 61 def test_search_acquires_nested_paren(self): 62 line = "__acquires((ctx1, ctx2)) baz();" 63 result = ", ".join(CMatch("__acquires").search(line)) 64 self.assertEqual(result, "__acquires((ctx1, ctx2))") 65 66 def test_search_must_hold(self): 67 line = "__must_hold(&lock) do_something();" 68 result = ", ".join(CMatch("__must_hold").search(line)) 69 self.assertEqual(result, "__must_hold(&lock)") 70 71 def test_search_must_hold_shared(self): 72 line = "__must_hold_shared(RCU) other();" 73 result = ", ".join(CMatch("__must_hold_shared").search(line)) 74 self.assertEqual(result, "__must_hold_shared(RCU)") 75 76 def test_search_no_false_positive(self): 77 line = "call__acquires(foo); // should stay intact" 78 result = ", ".join(CMatch(r"__acquires").search(line)) 79 self.assertEqual(result, "") 80 81 def test_search_no_macro_remains(self): 82 line = "do_something_else();" 83 result = ", ".join(CMatch("__acquires").search(line)) 84 self.assertEqual(result, "") 85 86 def test_search_no_function(self): 87 line = "something" 88 result = ", ".join(CMatch(line).search(line)) 89 self.assertEqual(result, "") 90 91# 92# Override unittest.TestCase to better compare diffs ignoring whitespaces 93# 94class TestCaseDiff(unittest.TestCase): 95 """ 96 Disable maximum limit on diffs and add a method to better 97 handle diffs with whitespace differences. 98 """ 99 100 @classmethod 101 def setUpClass(cls): 102 """Ensure that there won't be limit for diffs""" 103 cls.maxDiff = None 104 105 def assertLogicallyEqual(self, a, b): 106 """ 107 Compare two results ignoring multiple whitespace differences. 108 109 This is useful to check more complex matches picked from examples. 110 On a plus side, we also don't need to use dedent. 111 Please notice that line breaks still need to match. We might 112 remove it at the regex, but this way, checking the diff is easier. 113 """ 114 a = re.sub(r"[\t ]+", " ", a.strip()) 115 b = re.sub(r"[\t ]+", " ", b.strip()) 116 117 a = re.sub(r"\s+\n", "\n", a) 118 b = re.sub(r"\s+\n", "\n", b) 119 120 a = re.sub(" ;", ";", a) 121 b = re.sub(" ;", ";", b) 122 123 self.assertEqual(a, b) 124 125# 126# Tests doing with different macros 127# 128 129class TestSubMultipleMacros(TestCaseDiff): 130 """ 131 Tests doing with different macros. 132 133 Here, we won't use assertLogicallyEqual. Instead, we'll check if each 134 of the expected patterns are present at the answer. 135 """ 136 137 def test_acquires_simple(self): 138 """Simple replacement test with __acquires""" 139 line = "__acquires(ctx) foo();" 140 result = CMatch(r"__acquires").sub("REPLACED", line) 141 142 self.assertEqual("REPLACED foo();", result) 143 144 def test_acquires_multiple(self): 145 """Multiple __acquires""" 146 line = "__acquires(ctx) __acquires(other) bar();" 147 result = CMatch(r"__acquires").sub("REPLACED", line) 148 149 self.assertEqual("REPLACED REPLACED bar();", result) 150 151 def test_acquires_nested_paren(self): 152 """__acquires with nested pattern""" 153 line = "__acquires((ctx1, ctx2)) baz();" 154 result = CMatch(r"__acquires").sub("REPLACED", line) 155 156 self.assertEqual("REPLACED baz();", result) 157 158 def test_must_hold(self): 159 """__must_hold with a pointer""" 160 line = "__must_hold(&lock) do_something();" 161 result = CMatch(r"__must_hold").sub("REPLACED", line) 162 163 self.assertNotIn("__must_hold(", result) 164 self.assertIn("do_something();", result) 165 166 def test_must_hold_shared(self): 167 """__must_hold with an upercase defined value""" 168 line = "__must_hold_shared(RCU) other();" 169 result = CMatch(r"__must_hold_shared").sub("REPLACED", line) 170 171 self.assertNotIn("__must_hold_shared(", result) 172 self.assertIn("other();", result) 173 174 def test_no_false_positive(self): 175 """ 176 Ensure that unrelated text containing similar patterns is preserved 177 """ 178 line = "call__acquires(foo); // should stay intact" 179 result = CMatch(r"\b__acquires").sub("REPLACED", line) 180 181 self.assertLogicallyEqual(result, "call__acquires(foo);") 182 183 def test_mixed_macros(self): 184 """Add a mix of macros""" 185 line = "__acquires(ctx) __releases(ctx) __must_hold(&lock) foo();" 186 187 result = CMatch(r"__acquires").sub("REPLACED", line) 188 result = CMatch(r"__releases").sub("REPLACED", result) 189 result = CMatch(r"__must_hold").sub("REPLACED", result) 190 191 self.assertNotIn("__acquires(", result) 192 self.assertNotIn("__releases(", result) 193 self.assertNotIn("__must_hold(", result) 194 195 self.assertIn("foo();", result) 196 197 def test_no_macro_remains(self): 198 """Ensures that unmatched macros are untouched""" 199 line = "do_something_else();" 200 result = CMatch(r"__acquires").sub("REPLACED", line) 201 202 self.assertEqual(result, line) 203 204 def test_no_function(self): 205 """Ensures that no functions will remain untouched""" 206 line = "something" 207 result = CMatch(line).sub("REPLACED", line) 208 209 self.assertEqual(result, line) 210 211# 212# Check if the diff is logically equivalent. To simplify, the tests here 213# use a single macro name for all replacements. 214# 215 216class TestSubSimple(TestCaseDiff): 217 """ 218 Test argument replacements. 219 220 Here, the function name can be anything. So, we picked __attribute__(), 221 to mimic a macro found at the Kernel, but none of the replacements her 222 has any relationship with the Kernel usage. 223 """ 224 225 MACRO = "__attribute__" 226 227 @classmethod 228 def setUpClass(cls): 229 """Define a CMatch to be used for all tests""" 230 cls.matcher = CMatch(cls.MACRO) 231 232 def test_sub_with_capture(self): 233 """Test all arguments replacement with a single arg""" 234 line = f"{self.MACRO}(&ctx)\nfoo();" 235 236 result = self.matcher.sub(r"ACQUIRED(\0)", line) 237 238 self.assertLogicallyEqual("ACQUIRED(&ctx)\nfoo();", result) 239 240 def test_sub_zero_placeholder(self): 241 """Test all arguments replacement with a multiple args""" 242 line = f"{self.MACRO}(arg1, arg2)\nbar();" 243 244 result = self.matcher.sub(r"REPLACED(\0)", line) 245 246 self.assertLogicallyEqual("REPLACED(arg1, arg2)\nbar();", result) 247 248 def test_sub_single_placeholder(self): 249 """Single replacement rule for \1""" 250 line = f"{self.MACRO}(ctx, boo)\nfoo();" 251 result = self.matcher.sub(r"ACQUIRED(\1)", line) 252 253 self.assertLogicallyEqual("ACQUIRED(ctx)\nfoo();", result) 254 255 def test_sub_multiple_placeholders(self): 256 """Replacement rule for both \1 and \2""" 257 line = f"{self.MACRO}(arg1, arg2)\nbar();" 258 result = self.matcher.sub(r"REPLACE(\1, \2)", line) 259 260 self.assertLogicallyEqual("REPLACE(arg1, arg2)\nbar();", result) 261 262 def test_sub_mixed_placeholders(self): 263 """Replacement rule for \0, \1 and additional text""" 264 line = f"{self.MACRO}(foo, bar)\nbaz();" 265 result = self.matcher.sub(r"ALL(\0) FIRST(\1)", line) 266 267 self.assertLogicallyEqual("ALL(foo, bar) FIRST(foo)\nbaz();", result) 268 269 def test_sub_no_placeholder(self): 270 """Replacement without placeholders""" 271 line = f"{self.MACRO}(arg)\nfoo();" 272 result = self.matcher.sub(r"NO_BACKREFS()", line) 273 274 self.assertLogicallyEqual("NO_BACKREFS()\nfoo();", result) 275 276 def test_sub_count_parameter(self): 277 """Verify that the algorithm stops after the requested count""" 278 line = f"{self.MACRO}(a1) x();\n{self.MACRO}(a2) y();" 279 result = self.matcher.sub(r"ONLY_FIRST(\1) ", line, count=1) 280 281 self.assertLogicallyEqual(f"ONLY_FIRST(a1) x();\n{self.MACRO}(a2) y();", 282 result) 283 284 def test_strip_multiple_acquires(self): 285 """Check if spaces between removed delimiters will be dropped""" 286 line = f"int {self.MACRO}(1) {self.MACRO}(2 ) {self.MACRO}(3) foo;" 287 result = self.matcher.sub("", line) 288 289 self.assertLogicallyEqual(result, "int foo;") 290 291 def test_rise_early_greedy(self): 292 line = f"{self.MACRO}(a, b, c, d);" 293 sub = r"\1, \2+, \3" 294 295 with self.assertRaises(ValueError): 296 result = self.matcher.sub(sub, line) 297 298 def test_rise_multiple_greedy(self): 299 line = f"{self.MACRO}(a, b, c, d);" 300 sub = r"\1, \2+, \3+" 301 302 with self.assertRaises(ValueError): 303 result = self.matcher.sub(sub, line) 304 305# 306# Test replacements with slashrefs 307# 308 309 310class TestSubWithLocalXforms(TestCaseDiff): 311 """ 312 Test diferent usecase patterns found at the Kernel. 313 314 Here, replacements using both CMatch and KernRe can be tested, 315 as it will import the actual replacement rules used by kernel-doc. 316 """ 317 318 struct_xforms = [ 319 (CMatch("__attribute__"), ' '), 320 (CMatch('__aligned'), ' '), 321 (CMatch('__counted_by'), ' '), 322 (CMatch('__counted_by_(le|be)'), ' '), 323 (CMatch('__counted_by_ptr'), ' '), 324 (CMatch('__guarded_by'), ' '), 325 (CMatch('__pt_guarded_by'), ' '), 326 327 (CMatch('__cacheline_group_(begin|end)'), ''), 328 329 (CMatch('struct_group'), r'\2'), 330 (CMatch('struct_group_attr'), r'\3'), 331 (CMatch('struct_group_tagged'), r'struct \1 { \3+ } \2;'), 332 (CMatch('__struct_group'), r'\4'), 333 334 (CMatch('__ETHTOOL_DECLARE_LINK_MODE_MASK'), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), 335 (CMatch('DECLARE_PHY_INTERFACE_MASK',), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), 336 (CMatch('DECLARE_BITMAP'), r'unsigned long \1[BITS_TO_LONGS(\2)]'), 337 338 (CMatch('DECLARE_HASHTABLE'), r'unsigned long \1[1 << ((\2) - 1)]'), 339 (CMatch('DECLARE_KFIFO'), r'\2 *\1'), 340 (CMatch('DECLARE_KFIFO_PTR'), r'\2 *\1'), 341 (CMatch('(?:__)?DECLARE_FLEX_ARRAY'), r'\1 \2[]'), 342 (CMatch('DEFINE_DMA_UNMAP_ADDR'), r'dma_addr_t \1'), 343 (CMatch('DEFINE_DMA_UNMAP_LEN'), r'__u32 \1'), 344 (CMatch('VIRTIO_DECLARE_FEATURES'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'), 345 ] 346 347 function_xforms = [ 348 (CMatch('__printf'), ""), 349 (CMatch('__(?:re)?alloc_size'), ""), 350 (CMatch("__diagnose_as"), ""), 351 (CMatch("DECL_BUCKET_PARAMS"), r"\1, \2"), 352 353 (CMatch("__cond_acquires"), ""), 354 (CMatch("__cond_releases"), ""), 355 (CMatch("__acquires"), ""), 356 (CMatch("__releases"), ""), 357 (CMatch("__must_hold"), ""), 358 (CMatch("__must_not_hold"), ""), 359 (CMatch("__must_hold_shared"), ""), 360 (CMatch("__cond_acquires_shared"), ""), 361 (CMatch("__acquires_shared"), ""), 362 (CMatch("__releases_shared"), ""), 363 (CMatch("__attribute__"), ""), 364 ] 365 366 var_xforms = [ 367 (CMatch('__guarded_by'), ""), 368 (CMatch('__pt_guarded_by'), ""), 369 (CMatch("LIST_HEAD"), r"struct list_head \1"), 370 ] 371 372 #: Transforms main dictionary used at apply_transforms(). 373 xforms = { 374 "struct": struct_xforms, 375 "func": function_xforms, 376 "var": var_xforms, 377 } 378 379 @classmethod 380 def apply_transforms(cls, xform_type, text): 381 """ 382 Mimic the behavior of kdoc_parser.apply_transforms() method. 383 384 For each element of STRUCT_XFORMS, apply apply_transforms. 385 386 There are two parameters: 387 388 - ``xform_type`` 389 Can be ``func``, ``struct`` or ``var``; 390 - ``text`` 391 The text where the sub patterns from CTransforms will be applied. 392 """ 393 for search, subst in cls.xforms.get(xform_type): 394 text = search.sub(subst, text) 395 396 return text.strip() 397 398 cls.matcher = CMatch(r"struct_group[\w\_]*") 399 400 def test_struct_group(self): 401 """ 402 Test struct_group using a pattern from 403 drivers/net/ethernet/asix/ax88796c_main.h. 404 """ 405 line = """ 406 struct tx_pkt_info { 407 struct_group(tx_overhead, 408 struct tx_sop_header sop; 409 struct tx_segment_header seg; 410 ); 411 struct tx_eop_header eop; 412 u16 pkt_len; 413 u16 seq_num; 414 }; 415 """ 416 expected = """ 417 struct tx_pkt_info { 418 struct tx_sop_header sop; 419 struct tx_segment_header seg; 420 struct tx_eop_header eop; 421 u16 pkt_len; 422 u16 seq_num; 423 }; 424 """ 425 426 result = self.apply_transforms("struct", line) 427 self.assertLogicallyEqual(result, expected) 428 429 def test_struct_group_attr(self): 430 """ 431 Test two struct_group_attr using patterns from fs/smb/client/cifspdu.h. 432 """ 433 line = """ 434 typedef struct smb_com_open_rsp { 435 struct smb_hdr hdr; /* wct = 34 BB */ 436 __u8 AndXCommand; 437 __u8 AndXReserved; 438 __le16 AndXOffset; 439 __u8 OplockLevel; 440 __u16 Fid; 441 __le32 CreateAction; 442 struct_group_attr(common_attributes,, 443 __le64 CreationTime; 444 __le64 LastAccessTime; 445 __le64 LastWriteTime; 446 __le64 ChangeTime; 447 __le32 FileAttributes; 448 ); 449 __le64 AllocationSize; 450 __le64 EndOfFile; 451 __le16 FileType; 452 __le16 DeviceState; 453 __u8 DirectoryFlag; 454 __u16 ByteCount; /* bct = 0 */ 455 } OPEN_RSP; 456 typedef struct { 457 struct_group_attr(common_attributes,, 458 __le64 CreationTime; 459 __le64 LastAccessTime; 460 __le64 LastWriteTime; 461 __le64 ChangeTime; 462 __le32 Attributes; 463 ); 464 __u32 Pad1; 465 __le64 AllocationSize; 466 __le64 EndOfFile; 467 __le32 NumberOfLinks; 468 __u8 DeletePending; 469 __u8 Directory; 470 __u16 Pad2; 471 __le32 EASize; 472 __le32 FileNameLength; 473 union { 474 char __pad; 475 DECLARE_FLEX_ARRAY(char, FileName); 476 }; 477 } FILE_ALL_INFO; /* level 0x107 QPathInfo */ 478 """ 479 expected = """ 480 typedef struct smb_com_open_rsp { 481 struct smb_hdr hdr; 482 __u8 AndXCommand; 483 __u8 AndXReserved; 484 __le16 AndXOffset; 485 __u8 OplockLevel; 486 __u16 Fid; 487 __le32 CreateAction; 488 __le64 CreationTime; 489 __le64 LastAccessTime; 490 __le64 LastWriteTime; 491 __le64 ChangeTime; 492 __le32 FileAttributes; 493 __le64 AllocationSize; 494 __le64 EndOfFile; 495 __le16 FileType; 496 __le16 DeviceState; 497 __u8 DirectoryFlag; 498 __u16 ByteCount; 499 } OPEN_RSP; 500 typedef struct { 501 __le64 CreationTime; 502 __le64 LastAccessTime; 503 __le64 LastWriteTime; 504 __le64 ChangeTime; 505 __le32 Attributes; 506 __u32 Pad1; 507 __le64 AllocationSize; 508 __le64 EndOfFile; 509 __le32 NumberOfLinks; 510 __u8 DeletePending; 511 __u8 Directory; 512 __u16 Pad2; 513 __le32 EASize; 514 __le32 FileNameLength; 515 union { 516 char __pad; 517 char FileName[]; 518 }; 519 } FILE_ALL_INFO; 520 """ 521 522 result = self.apply_transforms("struct", line) 523 self.assertLogicallyEqual(result, expected) 524 525 def test_raw_struct_group(self): 526 """ 527 Test a __struct_group pattern from include/uapi/cxl/features.h. 528 """ 529 line = """ 530 struct cxl_mbox_get_sup_feats_out { 531 __struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* empty */, 532 __le16 num_entries; 533 __le16 supported_feats; 534 __u8 reserved[4]; 535 ); 536 struct cxl_feat_entry ents[] __counted_by_le(num_entries); 537 } __attribute__ ((__packed__)); 538 """ 539 expected = """ 540 struct cxl_mbox_get_sup_feats_out { 541 __le16 num_entries; 542 __le16 supported_feats; 543 __u8 reserved[4]; 544 struct cxl_feat_entry ents[]; 545 }; 546 """ 547 548 result = self.apply_transforms("struct", line) 549 self.assertLogicallyEqual(result, expected) 550 551 def test_raw_struct_group_tagged(self): 552 r""" 553 Test cxl_regs with struct_group_tagged patterns from drivers/cxl/cxl.h. 554 555 NOTE: 556 557 This one has actually a violation from what kernel-doc would 558 expect: Kernel-doc regex expects only 3 members, but this is 559 actually defined as:: 560 561 #define struct_group_tagged(TAG, NAME, MEMBERS...) 562 563 The replace expression there is:: 564 565 struct \1 { \3 } \2; 566 567 but it should be really something like:: 568 569 struct \1 { \3 \4 \5 \6 \7 \8 ... } \2; 570 571 a later fix would be needed to address it. 572 573 """ 574 line = """ 575 struct cxl_regs { 576 struct_group_tagged(cxl_component_regs, component, 577 void __iomem *hdm_decoder; 578 void __iomem *ras; 579 ); 580 581 582 /* This is actually a violation: too much commas */ 583 struct_group_tagged(cxl_device_regs, device_regs, 584 void __iomem *status, *mbox, *memdev; 585 ); 586 587 struct_group_tagged(cxl_pmu_regs, pmu_regs, 588 void __iomem *pmu; 589 ); 590 591 struct_group_tagged(cxl_rch_regs, rch_regs, 592 void __iomem *dport_aer; 593 ); 594 595 struct_group_tagged(cxl_rcd_regs, rcd_regs, 596 void __iomem *rcd_pcie_cap; 597 ); 598 }; 599 """ 600 expected = """ 601 struct cxl_regs { 602 struct cxl_component_regs { 603 void __iomem *hdm_decoder; 604 void __iomem *ras; 605 } component; 606 607 struct cxl_device_regs { 608 void __iomem *status, *mbox, *memdev; 609 } device_regs; 610 611 struct cxl_pmu_regs { 612 void __iomem *pmu; 613 } pmu_regs; 614 615 struct cxl_rch_regs { 616 void __iomem *dport_aer; 617 } rch_regs; 618 619 struct cxl_rcd_regs { 620 void __iomem *rcd_pcie_cap; 621 } rcd_regs; 622 }; 623 """ 624 625 result = self.apply_transforms("struct", line) 626 self.assertLogicallyEqual(result, expected) 627 628 def test_struct_group_tagged_with_private(self): 629 """ 630 Replace struct_group_tagged with private, using the same regex 631 for the replacement as what happens in xforms_lists.py. 632 633 As the private removal happens outside NestedGroup class, we manually 634 dropped the remaining part of the struct, to simulate what happens 635 at kdoc_parser. 636 637 Taken from include/net/page_pool/types.h 638 """ 639 line = """ 640 struct page_pool_params { 641 struct_group_tagged(page_pool_params_slow, slow, 642 struct net_device *netdev; 643 unsigned int queue_idx; 644 unsigned int flags; 645 /* private: only under "slow" struct */ 646 unsigned int ignored; 647 ); 648 /* Struct below shall not be ignored */ 649 struct_group_tagged(page_pool_params_fast, fast, 650 unsigned int order; 651 unsigned int pool_size; 652 int nid; 653 struct device *dev; 654 struct napi_struct *napi; 655 enum dma_data_direction dma_dir; 656 unsigned int max_len; 657 unsigned int offset; 658 ); 659 }; 660 """ 661 expected = """ 662 struct page_pool_params { 663 struct page_pool_params_slow { 664 struct net_device *netdev; 665 unsigned int queue_idx; 666 unsigned int flags; 667 } slow; 668 struct page_pool_params_fast { 669 unsigned int order; 670 unsigned int pool_size; 671 int nid; 672 struct device *dev; 673 struct napi_struct *napi; 674 enum dma_data_direction dma_dir; 675 unsigned int max_len; 676 unsigned int offset; 677 } fast; 678 }; 679 """ 680 681 result = self.apply_transforms("struct", line) 682 self.assertLogicallyEqual(result, expected) 683 684 def test_struct_kcov(self): 685 """ 686 """ 687 line = """ 688 struct kcov { 689 refcount_t refcount; 690 spinlock_t lock; 691 enum kcov_mode mode __guarded_by(&lock); 692 unsigned int size __guarded_by(&lock); 693 void *area __guarded_by(&lock); 694 struct task_struct *t __guarded_by(&lock); 695 bool remote; 696 unsigned int remote_size; 697 int sequence; 698 }; 699 """ 700 expected = """ 701 """ 702 703 result = self.apply_transforms("struct", line) 704 self.assertLogicallyEqual(result, expected) 705 706 707 def test_struct_kcov(self): 708 """ 709 Test a struct from kernel/kcov.c. 710 """ 711 line = """ 712 struct kcov { 713 refcount_t refcount; 714 spinlock_t lock; 715 enum kcov_mode mode __guarded_by(&lock); 716 unsigned int size __guarded_by(&lock); 717 void *area __guarded_by(&lock); 718 struct task_struct *t __guarded_by(&lock); 719 bool remote; 720 unsigned int remote_size; 721 int sequence; 722 }; 723 """ 724 expected = """ 725 struct kcov { 726 refcount_t refcount; 727 spinlock_t lock; 728 enum kcov_mode mode; 729 unsigned int size; 730 void *area; 731 struct task_struct *t; 732 bool remote; 733 unsigned int remote_size; 734 int sequence; 735 }; 736 """ 737 738 result = self.apply_transforms("struct", line) 739 self.assertLogicallyEqual(result, expected) 740 741 def test_vars_stackdepot(self): 742 """ 743 Test guarded_by on vars from lib/stackdepot.c. 744 """ 745 line = """ 746 size_t pool_offset __guarded_by(&pool_lock) = DEPOT_POOL_SIZE; 747 __guarded_by(&pool_lock) LIST_HEAD(free_stacks); 748 void **stack_pools __pt_guarded_by(&pool_lock); 749 """ 750 expected = """ 751 size_t pool_offset = DEPOT_POOL_SIZE; 752 struct list_head free_stacks; 753 void **stack_pools; 754 """ 755 756 result = self.apply_transforms("var", line) 757 self.assertLogicallyEqual(result, expected) 758 759 def test_functions_with_acquires_and_releases(self): 760 """ 761 Test guarded_by on vars from lib/stackdepot.c. 762 """ 763 line = """ 764 bool prepare_report_consumer(unsigned long *flags, 765 const struct access_info *ai, 766 struct other_info *other_info) \ 767 __cond_acquires(true, &report_lock); 768 769 int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) \ 770 __cond_acquires(0, RCU_BH); 771 772 bool undo_report_consumer(unsigned long *flags, 773 const struct access_info *ai, 774 struct other_info *other_info) \ 775 __cond_releases(true, &report_lock); 776 777 void debugfs_enter_cancellation(struct file *file, 778 struct debugfs_cancellation *c) \ 779 __acquires(cancellation); 780 781 void debugfs_leave_cancellation(struct file *file, 782 struct debugfs_cancellation *c) \ 783 __releases(cancellation); 784 785 acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) \ 786 __acquires(lockp); 787 788 void acpi_os_release_lock(acpi_spinlock lockp, 789 acpi_cpu_flags not_used) \ 790 __releases(lockp) 791 """ 792 expected = """ 793 bool prepare_report_consumer(unsigned long *flags, 794 const struct access_info *ai, 795 struct other_info *other_info); 796 797 int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c); 798 799 bool undo_report_consumer(unsigned long *flags, 800 const struct access_info *ai, 801 struct other_info *other_info); 802 803 void debugfs_enter_cancellation(struct file *file, 804 struct debugfs_cancellation *c); 805 806 void debugfs_leave_cancellation(struct file *file, 807 struct debugfs_cancellation *c); 808 809 acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp); 810 811 void acpi_os_release_lock(acpi_spinlock lockp, 812 acpi_cpu_flags not_used) 813 """ 814 815 result = self.apply_transforms("func", line) 816 self.assertLogicallyEqual(result, expected) 817 818# 819# Run all tests 820# 821if __name__ == "__main__": 822 run_unittest(__file__) 823