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('__guarded_by'), ' '), 324 (CMatch('__pt_guarded_by'), ' '), 325 326 (CMatch('__cacheline_group_(begin|end)'), ''), 327 328 (CMatch('struct_group'), r'\2'), 329 (CMatch('struct_group_attr'), r'\3'), 330 (CMatch('struct_group_tagged'), r'struct \1 { \3+ } \2;'), 331 (CMatch('__struct_group'), r'\4'), 332 333 (CMatch('__ETHTOOL_DECLARE_LINK_MODE_MASK'), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), 334 (CMatch('DECLARE_PHY_INTERFACE_MASK',), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), 335 (CMatch('DECLARE_BITMAP'), r'unsigned long \1[BITS_TO_LONGS(\2)]'), 336 337 (CMatch('DECLARE_HASHTABLE'), r'unsigned long \1[1 << ((\2) - 1)]'), 338 (CMatch('DECLARE_KFIFO'), r'\2 *\1'), 339 (CMatch('DECLARE_KFIFO_PTR'), r'\2 *\1'), 340 (CMatch('(?:__)?DECLARE_FLEX_ARRAY'), r'\1 \2[]'), 341 (CMatch('DEFINE_DMA_UNMAP_ADDR'), r'dma_addr_t \1'), 342 (CMatch('DEFINE_DMA_UNMAP_LEN'), r'__u32 \1'), 343 (CMatch('VIRTIO_DECLARE_FEATURES'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'), 344 ] 345 346 function_xforms = [ 347 (CMatch('__printf'), ""), 348 (CMatch('__(?:re)?alloc_size'), ""), 349 (CMatch("__diagnose_as"), ""), 350 (CMatch("DECL_BUCKET_PARAMS"), r"\1, \2"), 351 352 (CMatch("__cond_acquires"), ""), 353 (CMatch("__cond_releases"), ""), 354 (CMatch("__acquires"), ""), 355 (CMatch("__releases"), ""), 356 (CMatch("__must_hold"), ""), 357 (CMatch("__must_not_hold"), ""), 358 (CMatch("__must_hold_shared"), ""), 359 (CMatch("__cond_acquires_shared"), ""), 360 (CMatch("__acquires_shared"), ""), 361 (CMatch("__releases_shared"), ""), 362 (CMatch("__attribute__"), ""), 363 ] 364 365 var_xforms = [ 366 (CMatch('__guarded_by'), ""), 367 (CMatch('__pt_guarded_by'), ""), 368 (CMatch("LIST_HEAD"), r"struct list_head \1"), 369 ] 370 371 #: Transforms main dictionary used at apply_transforms(). 372 xforms = { 373 "struct": struct_xforms, 374 "func": function_xforms, 375 "var": var_xforms, 376 } 377 378 @classmethod 379 def apply_transforms(cls, xform_type, text): 380 """ 381 Mimic the behavior of kdoc_parser.apply_transforms() method. 382 383 For each element of STRUCT_XFORMS, apply apply_transforms. 384 385 There are two parameters: 386 387 - ``xform_type`` 388 Can be ``func``, ``struct`` or ``var``; 389 - ``text`` 390 The text where the sub patterns from CTransforms will be applied. 391 """ 392 for search, subst in cls.xforms.get(xform_type): 393 text = search.sub(subst, text) 394 395 return text.strip() 396 397 cls.matcher = CMatch(r"struct_group[\w\_]*") 398 399 def test_struct_group(self): 400 """ 401 Test struct_group using a pattern from 402 drivers/net/ethernet/asix/ax88796c_main.h. 403 """ 404 line = """ 405 struct tx_pkt_info { 406 struct_group(tx_overhead, 407 struct tx_sop_header sop; 408 struct tx_segment_header seg; 409 ); 410 struct tx_eop_header eop; 411 u16 pkt_len; 412 u16 seq_num; 413 }; 414 """ 415 expected = """ 416 struct tx_pkt_info { 417 struct tx_sop_header sop; 418 struct tx_segment_header seg; 419 struct tx_eop_header eop; 420 u16 pkt_len; 421 u16 seq_num; 422 }; 423 """ 424 425 result = self.apply_transforms("struct", line) 426 self.assertLogicallyEqual(result, expected) 427 428 def test_struct_group_attr(self): 429 """ 430 Test two struct_group_attr using patterns from fs/smb/client/cifspdu.h. 431 """ 432 line = """ 433 typedef struct smb_com_open_rsp { 434 struct smb_hdr hdr; /* wct = 34 BB */ 435 __u8 AndXCommand; 436 __u8 AndXReserved; 437 __le16 AndXOffset; 438 __u8 OplockLevel; 439 __u16 Fid; 440 __le32 CreateAction; 441 struct_group_attr(common_attributes,, 442 __le64 CreationTime; 443 __le64 LastAccessTime; 444 __le64 LastWriteTime; 445 __le64 ChangeTime; 446 __le32 FileAttributes; 447 ); 448 __le64 AllocationSize; 449 __le64 EndOfFile; 450 __le16 FileType; 451 __le16 DeviceState; 452 __u8 DirectoryFlag; 453 __u16 ByteCount; /* bct = 0 */ 454 } OPEN_RSP; 455 typedef struct { 456 struct_group_attr(common_attributes,, 457 __le64 CreationTime; 458 __le64 LastAccessTime; 459 __le64 LastWriteTime; 460 __le64 ChangeTime; 461 __le32 Attributes; 462 ); 463 __u32 Pad1; 464 __le64 AllocationSize; 465 __le64 EndOfFile; 466 __le32 NumberOfLinks; 467 __u8 DeletePending; 468 __u8 Directory; 469 __u16 Pad2; 470 __le32 EASize; 471 __le32 FileNameLength; 472 union { 473 char __pad; 474 DECLARE_FLEX_ARRAY(char, FileName); 475 }; 476 } FILE_ALL_INFO; /* level 0x107 QPathInfo */ 477 """ 478 expected = """ 479 typedef struct smb_com_open_rsp { 480 struct smb_hdr hdr; 481 __u8 AndXCommand; 482 __u8 AndXReserved; 483 __le16 AndXOffset; 484 __u8 OplockLevel; 485 __u16 Fid; 486 __le32 CreateAction; 487 __le64 CreationTime; 488 __le64 LastAccessTime; 489 __le64 LastWriteTime; 490 __le64 ChangeTime; 491 __le32 FileAttributes; 492 __le64 AllocationSize; 493 __le64 EndOfFile; 494 __le16 FileType; 495 __le16 DeviceState; 496 __u8 DirectoryFlag; 497 __u16 ByteCount; 498 } OPEN_RSP; 499 typedef struct { 500 __le64 CreationTime; 501 __le64 LastAccessTime; 502 __le64 LastWriteTime; 503 __le64 ChangeTime; 504 __le32 Attributes; 505 __u32 Pad1; 506 __le64 AllocationSize; 507 __le64 EndOfFile; 508 __le32 NumberOfLinks; 509 __u8 DeletePending; 510 __u8 Directory; 511 __u16 Pad2; 512 __le32 EASize; 513 __le32 FileNameLength; 514 union { 515 char __pad; 516 char FileName[]; 517 }; 518 } FILE_ALL_INFO; 519 """ 520 521 result = self.apply_transforms("struct", line) 522 self.assertLogicallyEqual(result, expected) 523 524 def test_raw_struct_group(self): 525 """ 526 Test a __struct_group pattern from include/uapi/cxl/features.h. 527 """ 528 line = """ 529 struct cxl_mbox_get_sup_feats_out { 530 __struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* empty */, 531 __le16 num_entries; 532 __le16 supported_feats; 533 __u8 reserved[4]; 534 ); 535 struct cxl_feat_entry ents[] __counted_by_le(num_entries); 536 } __attribute__ ((__packed__)); 537 """ 538 expected = """ 539 struct cxl_mbox_get_sup_feats_out { 540 __le16 num_entries; 541 __le16 supported_feats; 542 __u8 reserved[4]; 543 struct cxl_feat_entry ents[]; 544 }; 545 """ 546 547 result = self.apply_transforms("struct", line) 548 self.assertLogicallyEqual(result, expected) 549 550 def test_raw_struct_group_tagged(self): 551 r""" 552 Test cxl_regs with struct_group_tagged patterns from drivers/cxl/cxl.h. 553 554 NOTE: 555 556 This one has actually a violation from what kernel-doc would 557 expect: Kernel-doc regex expects only 3 members, but this is 558 actually defined as:: 559 560 #define struct_group_tagged(TAG, NAME, MEMBERS...) 561 562 The replace expression there is:: 563 564 struct \1 { \3 } \2; 565 566 but it should be really something like:: 567 568 struct \1 { \3 \4 \5 \6 \7 \8 ... } \2; 569 570 a later fix would be needed to address it. 571 572 """ 573 line = """ 574 struct cxl_regs { 575 struct_group_tagged(cxl_component_regs, component, 576 void __iomem *hdm_decoder; 577 void __iomem *ras; 578 ); 579 580 581 /* This is actually a violation: too much commas */ 582 struct_group_tagged(cxl_device_regs, device_regs, 583 void __iomem *status, *mbox, *memdev; 584 ); 585 586 struct_group_tagged(cxl_pmu_regs, pmu_regs, 587 void __iomem *pmu; 588 ); 589 590 struct_group_tagged(cxl_rch_regs, rch_regs, 591 void __iomem *dport_aer; 592 ); 593 594 struct_group_tagged(cxl_rcd_regs, rcd_regs, 595 void __iomem *rcd_pcie_cap; 596 ); 597 }; 598 """ 599 expected = """ 600 struct cxl_regs { 601 struct cxl_component_regs { 602 void __iomem *hdm_decoder; 603 void __iomem *ras; 604 } component; 605 606 struct cxl_device_regs { 607 void __iomem *status, *mbox, *memdev; 608 } device_regs; 609 610 struct cxl_pmu_regs { 611 void __iomem *pmu; 612 } pmu_regs; 613 614 struct cxl_rch_regs { 615 void __iomem *dport_aer; 616 } rch_regs; 617 618 struct cxl_rcd_regs { 619 void __iomem *rcd_pcie_cap; 620 } rcd_regs; 621 }; 622 """ 623 624 result = self.apply_transforms("struct", line) 625 self.assertLogicallyEqual(result, expected) 626 627 def test_struct_group_tagged_with_private(self): 628 """ 629 Replace struct_group_tagged with private, using the same regex 630 for the replacement as what happens in xforms_lists.py. 631 632 As the private removal happens outside NestedGroup class, we manually 633 dropped the remaining part of the struct, to simulate what happens 634 at kdoc_parser. 635 636 Taken from include/net/page_pool/types.h 637 """ 638 line = """ 639 struct page_pool_params { 640 struct_group_tagged(page_pool_params_slow, slow, 641 struct net_device *netdev; 642 unsigned int queue_idx; 643 unsigned int flags; 644 /* private: only under "slow" struct */ 645 unsigned int ignored; 646 ); 647 /* Struct below shall not be ignored */ 648 struct_group_tagged(page_pool_params_fast, fast, 649 unsigned int order; 650 unsigned int pool_size; 651 int nid; 652 struct device *dev; 653 struct napi_struct *napi; 654 enum dma_data_direction dma_dir; 655 unsigned int max_len; 656 unsigned int offset; 657 ); 658 }; 659 """ 660 expected = """ 661 struct page_pool_params { 662 struct page_pool_params_slow { 663 struct net_device *netdev; 664 unsigned int queue_idx; 665 unsigned int flags; 666 } slow; 667 struct page_pool_params_fast { 668 unsigned int order; 669 unsigned int pool_size; 670 int nid; 671 struct device *dev; 672 struct napi_struct *napi; 673 enum dma_data_direction dma_dir; 674 unsigned int max_len; 675 unsigned int offset; 676 } fast; 677 }; 678 """ 679 680 result = self.apply_transforms("struct", line) 681 self.assertLogicallyEqual(result, expected) 682 683 def test_struct_kcov(self): 684 """ 685 """ 686 line = """ 687 struct kcov { 688 refcount_t refcount; 689 spinlock_t lock; 690 enum kcov_mode mode __guarded_by(&lock); 691 unsigned int size __guarded_by(&lock); 692 void *area __guarded_by(&lock); 693 struct task_struct *t __guarded_by(&lock); 694 bool remote; 695 unsigned int remote_size; 696 int sequence; 697 }; 698 """ 699 expected = """ 700 """ 701 702 result = self.apply_transforms("struct", line) 703 self.assertLogicallyEqual(result, expected) 704 705 706 def test_struct_kcov(self): 707 """ 708 Test a struct from kernel/kcov.c. 709 """ 710 line = """ 711 struct kcov { 712 refcount_t refcount; 713 spinlock_t lock; 714 enum kcov_mode mode __guarded_by(&lock); 715 unsigned int size __guarded_by(&lock); 716 void *area __guarded_by(&lock); 717 struct task_struct *t __guarded_by(&lock); 718 bool remote; 719 unsigned int remote_size; 720 int sequence; 721 }; 722 """ 723 expected = """ 724 struct kcov { 725 refcount_t refcount; 726 spinlock_t lock; 727 enum kcov_mode mode; 728 unsigned int size; 729 void *area; 730 struct task_struct *t; 731 bool remote; 732 unsigned int remote_size; 733 int sequence; 734 }; 735 """ 736 737 result = self.apply_transforms("struct", line) 738 self.assertLogicallyEqual(result, expected) 739 740 def test_vars_stackdepot(self): 741 """ 742 Test guarded_by on vars from lib/stackdepot.c. 743 """ 744 line = """ 745 size_t pool_offset __guarded_by(&pool_lock) = DEPOT_POOL_SIZE; 746 __guarded_by(&pool_lock) LIST_HEAD(free_stacks); 747 void **stack_pools __pt_guarded_by(&pool_lock); 748 """ 749 expected = """ 750 size_t pool_offset = DEPOT_POOL_SIZE; 751 struct list_head free_stacks; 752 void **stack_pools; 753 """ 754 755 result = self.apply_transforms("var", line) 756 self.assertLogicallyEqual(result, expected) 757 758 def test_functions_with_acquires_and_releases(self): 759 """ 760 Test guarded_by on vars from lib/stackdepot.c. 761 """ 762 line = """ 763 bool prepare_report_consumer(unsigned long *flags, 764 const struct access_info *ai, 765 struct other_info *other_info) \ 766 __cond_acquires(true, &report_lock); 767 768 int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) \ 769 __cond_acquires(0, RCU_BH); 770 771 bool undo_report_consumer(unsigned long *flags, 772 const struct access_info *ai, 773 struct other_info *other_info) \ 774 __cond_releases(true, &report_lock); 775 776 void debugfs_enter_cancellation(struct file *file, 777 struct debugfs_cancellation *c) \ 778 __acquires(cancellation); 779 780 void debugfs_leave_cancellation(struct file *file, 781 struct debugfs_cancellation *c) \ 782 __releases(cancellation); 783 784 acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) \ 785 __acquires(lockp); 786 787 void acpi_os_release_lock(acpi_spinlock lockp, 788 acpi_cpu_flags not_used) \ 789 __releases(lockp) 790 """ 791 expected = """ 792 bool prepare_report_consumer(unsigned long *flags, 793 const struct access_info *ai, 794 struct other_info *other_info); 795 796 int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c); 797 798 bool undo_report_consumer(unsigned long *flags, 799 const struct access_info *ai, 800 struct other_info *other_info); 801 802 void debugfs_enter_cancellation(struct file *file, 803 struct debugfs_cancellation *c); 804 805 void debugfs_leave_cancellation(struct file *file, 806 struct debugfs_cancellation *c); 807 808 acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp); 809 810 void acpi_os_release_lock(acpi_spinlock lockp, 811 acpi_cpu_flags not_used) 812 """ 813 814 result = self.apply_transforms("func", line) 815 self.assertLogicallyEqual(result, expected) 816 817# 818# Run all tests 819# 820if __name__ == "__main__": 821 run_unittest(__file__) 822