mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Now that we have code for sub(), test it. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Message-ID: <b2621b0d378317f0db5eeb2408c794429dc9f70a.1773770483.git.mchehab+huawei@kernel.org>
822 lines
28 KiB
Python
Executable File
822 lines
28 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>.
|
|
#
|
|
# pylint: disable=C0413,R0904
|
|
|
|
|
|
"""
|
|
Unit tests for kernel-doc CMatch.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
# Import Python modules
|
|
|
|
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
sys.path.insert(0, os.path.join(SRC_DIR, "../lib/python"))
|
|
|
|
from kdoc.c_lex import CMatch
|
|
from kdoc.kdoc_re import KernRe
|
|
from unittest_helper import run_unittest
|
|
|
|
#
|
|
# Override unittest.TestCase to better compare diffs ignoring whitespaces
|
|
#
|
|
class TestCaseDiff(unittest.TestCase):
|
|
"""
|
|
Disable maximum limit on diffs and add a method to better
|
|
handle diffs with whitespace differences.
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""Ensure that there won't be limit for diffs"""
|
|
cls.maxDiff = None
|
|
|
|
|
|
#
|
|
# Tests doing with different macros
|
|
#
|
|
|
|
class TestSearch(TestCaseDiff):
|
|
"""
|
|
Test search mechanism
|
|
"""
|
|
|
|
def test_search_acquires_simple(self):
|
|
line = "__acquires(ctx) foo();"
|
|
result = ", ".join(CMatch("__acquires").search(line))
|
|
self.assertEqual(result, "__acquires(ctx)")
|
|
|
|
def test_search_acquires_multiple(self):
|
|
line = "__acquires(ctx) __acquires(other) bar();"
|
|
result = ", ".join(CMatch("__acquires").search(line))
|
|
self.assertEqual(result, "__acquires(ctx), __acquires(other)")
|
|
|
|
def test_search_acquires_nested_paren(self):
|
|
line = "__acquires((ctx1, ctx2)) baz();"
|
|
result = ", ".join(CMatch("__acquires").search(line))
|
|
self.assertEqual(result, "__acquires((ctx1, ctx2))")
|
|
|
|
def test_search_must_hold(self):
|
|
line = "__must_hold(&lock) do_something();"
|
|
result = ", ".join(CMatch("__must_hold").search(line))
|
|
self.assertEqual(result, "__must_hold(&lock)")
|
|
|
|
def test_search_must_hold_shared(self):
|
|
line = "__must_hold_shared(RCU) other();"
|
|
result = ", ".join(CMatch("__must_hold_shared").search(line))
|
|
self.assertEqual(result, "__must_hold_shared(RCU)")
|
|
|
|
def test_search_no_false_positive(self):
|
|
line = "call__acquires(foo); // should stay intact"
|
|
result = ", ".join(CMatch(r"__acquires").search(line))
|
|
self.assertEqual(result, "")
|
|
|
|
def test_search_no_macro_remains(self):
|
|
line = "do_something_else();"
|
|
result = ", ".join(CMatch("__acquires").search(line))
|
|
self.assertEqual(result, "")
|
|
|
|
def test_search_no_function(self):
|
|
line = "something"
|
|
result = ", ".join(CMatch(line).search(line))
|
|
self.assertEqual(result, "")
|
|
|
|
#
|
|
# Override unittest.TestCase to better compare diffs ignoring whitespaces
|
|
#
|
|
class TestCaseDiff(unittest.TestCase):
|
|
"""
|
|
Disable maximum limit on diffs and add a method to better
|
|
handle diffs with whitespace differences.
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""Ensure that there won't be limit for diffs"""
|
|
cls.maxDiff = None
|
|
|
|
def assertLogicallyEqual(self, a, b):
|
|
"""
|
|
Compare two results ignoring multiple whitespace differences.
|
|
|
|
This is useful to check more complex matches picked from examples.
|
|
On a plus side, we also don't need to use dedent.
|
|
Please notice that line breaks still need to match. We might
|
|
remove it at the regex, but this way, checking the diff is easier.
|
|
"""
|
|
a = re.sub(r"[\t ]+", " ", a.strip())
|
|
b = re.sub(r"[\t ]+", " ", b.strip())
|
|
|
|
a = re.sub(r"\s+\n", "\n", a)
|
|
b = re.sub(r"\s+\n", "\n", b)
|
|
|
|
a = re.sub(" ;", ";", a)
|
|
b = re.sub(" ;", ";", b)
|
|
|
|
self.assertEqual(a, b)
|
|
|
|
#
|
|
# Tests doing with different macros
|
|
#
|
|
|
|
class TestSubMultipleMacros(TestCaseDiff):
|
|
"""
|
|
Tests doing with different macros.
|
|
|
|
Here, we won't use assertLogicallyEqual. Instead, we'll check if each
|
|
of the expected patterns are present at the answer.
|
|
"""
|
|
|
|
def test_acquires_simple(self):
|
|
"""Simple replacement test with __acquires"""
|
|
line = "__acquires(ctx) foo();"
|
|
result = CMatch(r"__acquires").sub("REPLACED", line)
|
|
|
|
self.assertEqual("REPLACED foo();", result)
|
|
|
|
def test_acquires_multiple(self):
|
|
"""Multiple __acquires"""
|
|
line = "__acquires(ctx) __acquires(other) bar();"
|
|
result = CMatch(r"__acquires").sub("REPLACED", line)
|
|
|
|
self.assertEqual("REPLACED REPLACED bar();", result)
|
|
|
|
def test_acquires_nested_paren(self):
|
|
"""__acquires with nested pattern"""
|
|
line = "__acquires((ctx1, ctx2)) baz();"
|
|
result = CMatch(r"__acquires").sub("REPLACED", line)
|
|
|
|
self.assertEqual("REPLACED baz();", result)
|
|
|
|
def test_must_hold(self):
|
|
"""__must_hold with a pointer"""
|
|
line = "__must_hold(&lock) do_something();"
|
|
result = CMatch(r"__must_hold").sub("REPLACED", line)
|
|
|
|
self.assertNotIn("__must_hold(", result)
|
|
self.assertIn("do_something();", result)
|
|
|
|
def test_must_hold_shared(self):
|
|
"""__must_hold with an upercase defined value"""
|
|
line = "__must_hold_shared(RCU) other();"
|
|
result = CMatch(r"__must_hold_shared").sub("REPLACED", line)
|
|
|
|
self.assertNotIn("__must_hold_shared(", result)
|
|
self.assertIn("other();", result)
|
|
|
|
def test_no_false_positive(self):
|
|
"""
|
|
Ensure that unrelated text containing similar patterns is preserved
|
|
"""
|
|
line = "call__acquires(foo); // should stay intact"
|
|
result = CMatch(r"\b__acquires").sub("REPLACED", line)
|
|
|
|
self.assertLogicallyEqual(result, "call__acquires(foo);")
|
|
|
|
def test_mixed_macros(self):
|
|
"""Add a mix of macros"""
|
|
line = "__acquires(ctx) __releases(ctx) __must_hold(&lock) foo();"
|
|
|
|
result = CMatch(r"__acquires").sub("REPLACED", line)
|
|
result = CMatch(r"__releases").sub("REPLACED", result)
|
|
result = CMatch(r"__must_hold").sub("REPLACED", result)
|
|
|
|
self.assertNotIn("__acquires(", result)
|
|
self.assertNotIn("__releases(", result)
|
|
self.assertNotIn("__must_hold(", result)
|
|
|
|
self.assertIn("foo();", result)
|
|
|
|
def test_no_macro_remains(self):
|
|
"""Ensures that unmatched macros are untouched"""
|
|
line = "do_something_else();"
|
|
result = CMatch(r"__acquires").sub("REPLACED", line)
|
|
|
|
self.assertEqual(result, line)
|
|
|
|
def test_no_function(self):
|
|
"""Ensures that no functions will remain untouched"""
|
|
line = "something"
|
|
result = CMatch(line).sub("REPLACED", line)
|
|
|
|
self.assertEqual(result, line)
|
|
|
|
#
|
|
# Check if the diff is logically equivalent. To simplify, the tests here
|
|
# use a single macro name for all replacements.
|
|
#
|
|
|
|
class TestSubSimple(TestCaseDiff):
|
|
"""
|
|
Test argument replacements.
|
|
|
|
Here, the function name can be anything. So, we picked __attribute__(),
|
|
to mimic a macro found at the Kernel, but none of the replacements her
|
|
has any relationship with the Kernel usage.
|
|
"""
|
|
|
|
MACRO = "__attribute__"
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""Define a CMatch to be used for all tests"""
|
|
cls.matcher = CMatch(cls.MACRO)
|
|
|
|
def test_sub_with_capture(self):
|
|
"""Test all arguments replacement with a single arg"""
|
|
line = f"{self.MACRO}(&ctx)\nfoo();"
|
|
|
|
result = self.matcher.sub(r"ACQUIRED(\0)", line)
|
|
|
|
self.assertLogicallyEqual("ACQUIRED(&ctx)\nfoo();", result)
|
|
|
|
def test_sub_zero_placeholder(self):
|
|
"""Test all arguments replacement with a multiple args"""
|
|
line = f"{self.MACRO}(arg1, arg2)\nbar();"
|
|
|
|
result = self.matcher.sub(r"REPLACED(\0)", line)
|
|
|
|
self.assertLogicallyEqual("REPLACED(arg1, arg2)\nbar();", result)
|
|
|
|
def test_sub_single_placeholder(self):
|
|
"""Single replacement rule for \1"""
|
|
line = f"{self.MACRO}(ctx, boo)\nfoo();"
|
|
result = self.matcher.sub(r"ACQUIRED(\1)", line)
|
|
|
|
self.assertLogicallyEqual("ACQUIRED(ctx)\nfoo();", result)
|
|
|
|
def test_sub_multiple_placeholders(self):
|
|
"""Replacement rule for both \1 and \2"""
|
|
line = f"{self.MACRO}(arg1, arg2)\nbar();"
|
|
result = self.matcher.sub(r"REPLACE(\1, \2)", line)
|
|
|
|
self.assertLogicallyEqual("REPLACE(arg1, arg2)\nbar();", result)
|
|
|
|
def test_sub_mixed_placeholders(self):
|
|
"""Replacement rule for \0, \1 and additional text"""
|
|
line = f"{self.MACRO}(foo, bar)\nbaz();"
|
|
result = self.matcher.sub(r"ALL(\0) FIRST(\1)", line)
|
|
|
|
self.assertLogicallyEqual("ALL(foo, bar) FIRST(foo)\nbaz();", result)
|
|
|
|
def test_sub_no_placeholder(self):
|
|
"""Replacement without placeholders"""
|
|
line = f"{self.MACRO}(arg)\nfoo();"
|
|
result = self.matcher.sub(r"NO_BACKREFS()", line)
|
|
|
|
self.assertLogicallyEqual("NO_BACKREFS()\nfoo();", result)
|
|
|
|
def test_sub_count_parameter(self):
|
|
"""Verify that the algorithm stops after the requested count"""
|
|
line = f"{self.MACRO}(a1) x();\n{self.MACRO}(a2) y();"
|
|
result = self.matcher.sub(r"ONLY_FIRST(\1) ", line, count=1)
|
|
|
|
self.assertLogicallyEqual(f"ONLY_FIRST(a1) x();\n{self.MACRO}(a2) y();",
|
|
result)
|
|
|
|
def test_strip_multiple_acquires(self):
|
|
"""Check if spaces between removed delimiters will be dropped"""
|
|
line = f"int {self.MACRO}(1) {self.MACRO}(2 ) {self.MACRO}(3) foo;"
|
|
result = self.matcher.sub("", line)
|
|
|
|
self.assertLogicallyEqual(result, "int foo;")
|
|
|
|
def test_rise_early_greedy(self):
|
|
line = f"{self.MACRO}(a, b, c, d);"
|
|
sub = r"\1, \2+, \3"
|
|
|
|
with self.assertRaises(ValueError):
|
|
result = self.matcher.sub(sub, line)
|
|
|
|
def test_rise_multiple_greedy(self):
|
|
line = f"{self.MACRO}(a, b, c, d);"
|
|
sub = r"\1, \2+, \3+"
|
|
|
|
with self.assertRaises(ValueError):
|
|
result = self.matcher.sub(sub, line)
|
|
|
|
#
|
|
# Test replacements with slashrefs
|
|
#
|
|
|
|
|
|
class TestSubWithLocalXforms(TestCaseDiff):
|
|
"""
|
|
Test diferent usecase patterns found at the Kernel.
|
|
|
|
Here, replacements using both CMatch and KernRe can be tested,
|
|
as it will import the actual replacement rules used by kernel-doc.
|
|
"""
|
|
|
|
struct_xforms = [
|
|
(CMatch("__attribute__"), ' '),
|
|
(CMatch('__aligned'), ' '),
|
|
(CMatch('__counted_by'), ' '),
|
|
(CMatch('__counted_by_(le|be)'), ' '),
|
|
(CMatch('__guarded_by'), ' '),
|
|
(CMatch('__pt_guarded_by'), ' '),
|
|
|
|
(CMatch('__cacheline_group_(begin|end)'), ''),
|
|
|
|
(CMatch('struct_group'), r'\2'),
|
|
(CMatch('struct_group_attr'), r'\3'),
|
|
(CMatch('struct_group_tagged'), r'struct \1 { \3+ } \2;'),
|
|
(CMatch('__struct_group'), r'\4'),
|
|
|
|
(CMatch('__ETHTOOL_DECLARE_LINK_MODE_MASK'), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
|
|
(CMatch('DECLARE_PHY_INTERFACE_MASK',), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
|
|
(CMatch('DECLARE_BITMAP'), r'unsigned long \1[BITS_TO_LONGS(\2)]'),
|
|
|
|
(CMatch('DECLARE_HASHTABLE'), r'unsigned long \1[1 << ((\2) - 1)]'),
|
|
(CMatch('DECLARE_KFIFO'), r'\2 *\1'),
|
|
(CMatch('DECLARE_KFIFO_PTR'), r'\2 *\1'),
|
|
(CMatch('(?:__)?DECLARE_FLEX_ARRAY'), r'\1 \2[]'),
|
|
(CMatch('DEFINE_DMA_UNMAP_ADDR'), r'dma_addr_t \1'),
|
|
(CMatch('DEFINE_DMA_UNMAP_LEN'), r'__u32 \1'),
|
|
(CMatch('VIRTIO_DECLARE_FEATURES'), r'union { u64 \1; u64 \1_array[VIRTIO_FEATURES_U64S]; }'),
|
|
]
|
|
|
|
function_xforms = [
|
|
(CMatch('__printf'), ""),
|
|
(CMatch('__(?:re)?alloc_size'), ""),
|
|
(CMatch("__diagnose_as"), ""),
|
|
(CMatch("DECL_BUCKET_PARAMS"), r"\1, \2"),
|
|
|
|
(CMatch("__cond_acquires"), ""),
|
|
(CMatch("__cond_releases"), ""),
|
|
(CMatch("__acquires"), ""),
|
|
(CMatch("__releases"), ""),
|
|
(CMatch("__must_hold"), ""),
|
|
(CMatch("__must_not_hold"), ""),
|
|
(CMatch("__must_hold_shared"), ""),
|
|
(CMatch("__cond_acquires_shared"), ""),
|
|
(CMatch("__acquires_shared"), ""),
|
|
(CMatch("__releases_shared"), ""),
|
|
(CMatch("__attribute__"), ""),
|
|
]
|
|
|
|
var_xforms = [
|
|
(CMatch('__guarded_by'), ""),
|
|
(CMatch('__pt_guarded_by'), ""),
|
|
(CMatch("LIST_HEAD"), r"struct list_head \1"),
|
|
]
|
|
|
|
#: Transforms main dictionary used at apply_transforms().
|
|
xforms = {
|
|
"struct": struct_xforms,
|
|
"func": function_xforms,
|
|
"var": var_xforms,
|
|
}
|
|
|
|
@classmethod
|
|
def apply_transforms(cls, xform_type, text):
|
|
"""
|
|
Mimic the behavior of kdoc_parser.apply_transforms() method.
|
|
|
|
For each element of STRUCT_XFORMS, apply apply_transforms.
|
|
|
|
There are two parameters:
|
|
|
|
- ``xform_type``
|
|
Can be ``func``, ``struct`` or ``var``;
|
|
- ``text``
|
|
The text where the sub patterns from CTransforms will be applied.
|
|
"""
|
|
for search, subst in cls.xforms.get(xform_type):
|
|
text = search.sub(subst, text)
|
|
|
|
return text.strip()
|
|
|
|
cls.matcher = CMatch(r"struct_group[\w\_]*")
|
|
|
|
def test_struct_group(self):
|
|
"""
|
|
Test struct_group using a pattern from
|
|
drivers/net/ethernet/asix/ax88796c_main.h.
|
|
"""
|
|
line = """
|
|
struct tx_pkt_info {
|
|
struct_group(tx_overhead,
|
|
struct tx_sop_header sop;
|
|
struct tx_segment_header seg;
|
|
);
|
|
struct tx_eop_header eop;
|
|
u16 pkt_len;
|
|
u16 seq_num;
|
|
};
|
|
"""
|
|
expected = """
|
|
struct tx_pkt_info {
|
|
struct tx_sop_header sop;
|
|
struct tx_segment_header seg;
|
|
struct tx_eop_header eop;
|
|
u16 pkt_len;
|
|
u16 seq_num;
|
|
};
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_struct_group_attr(self):
|
|
"""
|
|
Test two struct_group_attr using patterns from fs/smb/client/cifspdu.h.
|
|
"""
|
|
line = """
|
|
typedef struct smb_com_open_rsp {
|
|
struct smb_hdr hdr; /* wct = 34 BB */
|
|
__u8 AndXCommand;
|
|
__u8 AndXReserved;
|
|
__le16 AndXOffset;
|
|
__u8 OplockLevel;
|
|
__u16 Fid;
|
|
__le32 CreateAction;
|
|
struct_group_attr(common_attributes,,
|
|
__le64 CreationTime;
|
|
__le64 LastAccessTime;
|
|
__le64 LastWriteTime;
|
|
__le64 ChangeTime;
|
|
__le32 FileAttributes;
|
|
);
|
|
__le64 AllocationSize;
|
|
__le64 EndOfFile;
|
|
__le16 FileType;
|
|
__le16 DeviceState;
|
|
__u8 DirectoryFlag;
|
|
__u16 ByteCount; /* bct = 0 */
|
|
} OPEN_RSP;
|
|
typedef struct {
|
|
struct_group_attr(common_attributes,,
|
|
__le64 CreationTime;
|
|
__le64 LastAccessTime;
|
|
__le64 LastWriteTime;
|
|
__le64 ChangeTime;
|
|
__le32 Attributes;
|
|
);
|
|
__u32 Pad1;
|
|
__le64 AllocationSize;
|
|
__le64 EndOfFile;
|
|
__le32 NumberOfLinks;
|
|
__u8 DeletePending;
|
|
__u8 Directory;
|
|
__u16 Pad2;
|
|
__le32 EASize;
|
|
__le32 FileNameLength;
|
|
union {
|
|
char __pad;
|
|
DECLARE_FLEX_ARRAY(char, FileName);
|
|
};
|
|
} FILE_ALL_INFO; /* level 0x107 QPathInfo */
|
|
"""
|
|
expected = """
|
|
typedef struct smb_com_open_rsp {
|
|
struct smb_hdr hdr;
|
|
__u8 AndXCommand;
|
|
__u8 AndXReserved;
|
|
__le16 AndXOffset;
|
|
__u8 OplockLevel;
|
|
__u16 Fid;
|
|
__le32 CreateAction;
|
|
__le64 CreationTime;
|
|
__le64 LastAccessTime;
|
|
__le64 LastWriteTime;
|
|
__le64 ChangeTime;
|
|
__le32 FileAttributes;
|
|
__le64 AllocationSize;
|
|
__le64 EndOfFile;
|
|
__le16 FileType;
|
|
__le16 DeviceState;
|
|
__u8 DirectoryFlag;
|
|
__u16 ByteCount;
|
|
} OPEN_RSP;
|
|
typedef struct {
|
|
__le64 CreationTime;
|
|
__le64 LastAccessTime;
|
|
__le64 LastWriteTime;
|
|
__le64 ChangeTime;
|
|
__le32 Attributes;
|
|
__u32 Pad1;
|
|
__le64 AllocationSize;
|
|
__le64 EndOfFile;
|
|
__le32 NumberOfLinks;
|
|
__u8 DeletePending;
|
|
__u8 Directory;
|
|
__u16 Pad2;
|
|
__le32 EASize;
|
|
__le32 FileNameLength;
|
|
union {
|
|
char __pad;
|
|
char FileName[];
|
|
};
|
|
} FILE_ALL_INFO;
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_raw_struct_group(self):
|
|
"""
|
|
Test a __struct_group pattern from include/uapi/cxl/features.h.
|
|
"""
|
|
line = """
|
|
struct cxl_mbox_get_sup_feats_out {
|
|
__struct_group(cxl_mbox_get_sup_feats_out_hdr, hdr, /* empty */,
|
|
__le16 num_entries;
|
|
__le16 supported_feats;
|
|
__u8 reserved[4];
|
|
);
|
|
struct cxl_feat_entry ents[] __counted_by_le(num_entries);
|
|
} __attribute__ ((__packed__));
|
|
"""
|
|
expected = """
|
|
struct cxl_mbox_get_sup_feats_out {
|
|
__le16 num_entries;
|
|
__le16 supported_feats;
|
|
__u8 reserved[4];
|
|
struct cxl_feat_entry ents[];
|
|
};
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_raw_struct_group_tagged(self):
|
|
r"""
|
|
Test cxl_regs with struct_group_tagged patterns from drivers/cxl/cxl.h.
|
|
|
|
NOTE:
|
|
|
|
This one has actually a violation from what kernel-doc would
|
|
expect: Kernel-doc regex expects only 3 members, but this is
|
|
actually defined as::
|
|
|
|
#define struct_group_tagged(TAG, NAME, MEMBERS...)
|
|
|
|
The replace expression there is::
|
|
|
|
struct \1 { \3 } \2;
|
|
|
|
but it should be really something like::
|
|
|
|
struct \1 { \3 \4 \5 \6 \7 \8 ... } \2;
|
|
|
|
a later fix would be needed to address it.
|
|
|
|
"""
|
|
line = """
|
|
struct cxl_regs {
|
|
struct_group_tagged(cxl_component_regs, component,
|
|
void __iomem *hdm_decoder;
|
|
void __iomem *ras;
|
|
);
|
|
|
|
|
|
/* This is actually a violation: too much commas */
|
|
struct_group_tagged(cxl_device_regs, device_regs,
|
|
void __iomem *status, *mbox, *memdev;
|
|
);
|
|
|
|
struct_group_tagged(cxl_pmu_regs, pmu_regs,
|
|
void __iomem *pmu;
|
|
);
|
|
|
|
struct_group_tagged(cxl_rch_regs, rch_regs,
|
|
void __iomem *dport_aer;
|
|
);
|
|
|
|
struct_group_tagged(cxl_rcd_regs, rcd_regs,
|
|
void __iomem *rcd_pcie_cap;
|
|
);
|
|
};
|
|
"""
|
|
expected = """
|
|
struct cxl_regs {
|
|
struct cxl_component_regs {
|
|
void __iomem *hdm_decoder;
|
|
void __iomem *ras;
|
|
} component;
|
|
|
|
struct cxl_device_regs {
|
|
void __iomem *status, *mbox, *memdev;
|
|
} device_regs;
|
|
|
|
struct cxl_pmu_regs {
|
|
void __iomem *pmu;
|
|
} pmu_regs;
|
|
|
|
struct cxl_rch_regs {
|
|
void __iomem *dport_aer;
|
|
} rch_regs;
|
|
|
|
struct cxl_rcd_regs {
|
|
void __iomem *rcd_pcie_cap;
|
|
} rcd_regs;
|
|
};
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_struct_group_tagged_with_private(self):
|
|
"""
|
|
Replace struct_group_tagged with private, using the same regex
|
|
for the replacement as what happens in xforms_lists.py.
|
|
|
|
As the private removal happens outside NestedGroup class, we manually
|
|
dropped the remaining part of the struct, to simulate what happens
|
|
at kdoc_parser.
|
|
|
|
Taken from include/net/page_pool/types.h
|
|
"""
|
|
line = """
|
|
struct page_pool_params {
|
|
struct_group_tagged(page_pool_params_slow, slow,
|
|
struct net_device *netdev;
|
|
unsigned int queue_idx;
|
|
unsigned int flags;
|
|
/* private: only under "slow" struct */
|
|
unsigned int ignored;
|
|
);
|
|
/* Struct below shall not be ignored */
|
|
struct_group_tagged(page_pool_params_fast, fast,
|
|
unsigned int order;
|
|
unsigned int pool_size;
|
|
int nid;
|
|
struct device *dev;
|
|
struct napi_struct *napi;
|
|
enum dma_data_direction dma_dir;
|
|
unsigned int max_len;
|
|
unsigned int offset;
|
|
);
|
|
};
|
|
"""
|
|
expected = """
|
|
struct page_pool_params {
|
|
struct page_pool_params_slow {
|
|
struct net_device *netdev;
|
|
unsigned int queue_idx;
|
|
unsigned int flags;
|
|
} slow;
|
|
struct page_pool_params_fast {
|
|
unsigned int order;
|
|
unsigned int pool_size;
|
|
int nid;
|
|
struct device *dev;
|
|
struct napi_struct *napi;
|
|
enum dma_data_direction dma_dir;
|
|
unsigned int max_len;
|
|
unsigned int offset;
|
|
} fast;
|
|
};
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_struct_kcov(self):
|
|
"""
|
|
"""
|
|
line = """
|
|
struct kcov {
|
|
refcount_t refcount;
|
|
spinlock_t lock;
|
|
enum kcov_mode mode __guarded_by(&lock);
|
|
unsigned int size __guarded_by(&lock);
|
|
void *area __guarded_by(&lock);
|
|
struct task_struct *t __guarded_by(&lock);
|
|
bool remote;
|
|
unsigned int remote_size;
|
|
int sequence;
|
|
};
|
|
"""
|
|
expected = """
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
|
|
def test_struct_kcov(self):
|
|
"""
|
|
Test a struct from kernel/kcov.c.
|
|
"""
|
|
line = """
|
|
struct kcov {
|
|
refcount_t refcount;
|
|
spinlock_t lock;
|
|
enum kcov_mode mode __guarded_by(&lock);
|
|
unsigned int size __guarded_by(&lock);
|
|
void *area __guarded_by(&lock);
|
|
struct task_struct *t __guarded_by(&lock);
|
|
bool remote;
|
|
unsigned int remote_size;
|
|
int sequence;
|
|
};
|
|
"""
|
|
expected = """
|
|
struct kcov {
|
|
refcount_t refcount;
|
|
spinlock_t lock;
|
|
enum kcov_mode mode;
|
|
unsigned int size;
|
|
void *area;
|
|
struct task_struct *t;
|
|
bool remote;
|
|
unsigned int remote_size;
|
|
int sequence;
|
|
};
|
|
"""
|
|
|
|
result = self.apply_transforms("struct", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_vars_stackdepot(self):
|
|
"""
|
|
Test guarded_by on vars from lib/stackdepot.c.
|
|
"""
|
|
line = """
|
|
size_t pool_offset __guarded_by(&pool_lock) = DEPOT_POOL_SIZE;
|
|
__guarded_by(&pool_lock) LIST_HEAD(free_stacks);
|
|
void **stack_pools __pt_guarded_by(&pool_lock);
|
|
"""
|
|
expected = """
|
|
size_t pool_offset = DEPOT_POOL_SIZE;
|
|
struct list_head free_stacks;
|
|
void **stack_pools;
|
|
"""
|
|
|
|
result = self.apply_transforms("var", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
def test_functions_with_acquires_and_releases(self):
|
|
"""
|
|
Test guarded_by on vars from lib/stackdepot.c.
|
|
"""
|
|
line = """
|
|
bool prepare_report_consumer(unsigned long *flags,
|
|
const struct access_info *ai,
|
|
struct other_info *other_info) \
|
|
__cond_acquires(true, &report_lock);
|
|
|
|
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) \
|
|
__cond_acquires(0, RCU_BH);
|
|
|
|
bool undo_report_consumer(unsigned long *flags,
|
|
const struct access_info *ai,
|
|
struct other_info *other_info) \
|
|
__cond_releases(true, &report_lock);
|
|
|
|
void debugfs_enter_cancellation(struct file *file,
|
|
struct debugfs_cancellation *c) \
|
|
__acquires(cancellation);
|
|
|
|
void debugfs_leave_cancellation(struct file *file,
|
|
struct debugfs_cancellation *c) \
|
|
__releases(cancellation);
|
|
|
|
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp) \
|
|
__acquires(lockp);
|
|
|
|
void acpi_os_release_lock(acpi_spinlock lockp,
|
|
acpi_cpu_flags not_used) \
|
|
__releases(lockp)
|
|
"""
|
|
expected = """
|
|
bool prepare_report_consumer(unsigned long *flags,
|
|
const struct access_info *ai,
|
|
struct other_info *other_info);
|
|
|
|
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c);
|
|
|
|
bool undo_report_consumer(unsigned long *flags,
|
|
const struct access_info *ai,
|
|
struct other_info *other_info);
|
|
|
|
void debugfs_enter_cancellation(struct file *file,
|
|
struct debugfs_cancellation *c);
|
|
|
|
void debugfs_leave_cancellation(struct file *file,
|
|
struct debugfs_cancellation *c);
|
|
|
|
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp);
|
|
|
|
void acpi_os_release_lock(acpi_spinlock lockp,
|
|
acpi_cpu_flags not_used)
|
|
"""
|
|
|
|
result = self.apply_transforms("func", line)
|
|
self.assertLogicallyEqual(result, expected)
|
|
|
|
#
|
|
# Run all tests
|
|
#
|
|
if __name__ == "__main__":
|
|
run_unittest(__file__)
|