Michael Kubacki cbcf0428e8 BaseTools/Plugin: Add DebugMacroCheck
Adds a plugin that finds debug macro formatting issues. These errors
often creep into debug prints in error conditions not frequently
executed and make debug more difficult when they are encountered.

The code can be as a standalone script which is useful to find
problems in a large codebase that has not been checked before or as
a build plugin that notifies a developer of an error right away.

The script was already used to find numerous issues in edk2 in the
past so there's not many code fixes in this change. More details
are available in the readme file:

.pytool\Plugin\DebugMacroCheck\Readme.md

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Rebecca Cran <rebecca@bsdio.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Yuwei Chen <yuwei.chen@intel.com>
Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com>
Reviewed-by: Michael D Kinney <michael.d.kinney@intel.com>
2023-09-19 01:20:27 +00:00

132 lines
3.5 KiB
Python

# @file MacroTest.py
#
# Contains the data classes that are used to compose debug macro tests.
#
# All data classes inherit from a single abstract base class that expects
# the subclass to define the category of test it represents.
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
from dataclasses import dataclass, field
from os import linesep
from typing import Tuple
import abc
@dataclass(frozen=True)
class MacroTest(abc.ABC):
"""Abstract base class for an individual macro test case."""
macro: str
result: Tuple[int, int, int]
description: str = field(default='')
@property
@abc.abstractmethod
def category(self) -> str:
"""Returns the test class category identifier.
Example: 'equal_specifier_equal_argument_macro_test'
This string is used to bind test objects against this class.
Returns:
str: Test category identifier string.
"""
pass
@property
def category_description(self) -> str:
"""Returns the test class category description.
Example: 'Test case with equal count of print specifiers to arguments.'
This string is a human readable description of the test category.
Returns:
str: String describing the test category.
"""
return self.__doc__
def __str__(self):
"""Returns a macro test case description string."""
s = [
f"{linesep}",
"=" * 80,
f"Macro Test Type: {self.category_description}",
f"{linesep}Macro: {self.macro}",
f"{linesep}Expected Result: {self.result}"
]
if self.description:
s.insert(3, f"Test Description: {self.description}")
return f'{linesep}'.join(s)
@dataclass(frozen=True)
class NoSpecifierNoArgumentMacroTest(MacroTest):
"""Test case with no print specifier and no arguments."""
@property
def category(self) -> str:
return "no_specifier_no_argument_macro_test"
@dataclass(frozen=True)
class EqualSpecifierEqualArgumentMacroTest(MacroTest):
"""Test case with equal count of print specifiers to arguments."""
@property
def category(self) -> str:
return "equal_specifier_equal_argument_macro_test"
@dataclass(frozen=True)
class MoreSpecifiersThanArgumentsMacroTest(MacroTest):
"""Test case with more print specifiers than arguments."""
@property
def category(self) -> str:
return "more_specifiers_than_arguments_macro_test"
@dataclass(frozen=True)
class LessSpecifiersThanArgumentsMacroTest(MacroTest):
"""Test case with less print specifiers than arguments."""
@property
def category(self) -> str:
return "less_specifiers_than_arguments_macro_test"
@dataclass(frozen=True)
class IgnoredSpecifiersMacroTest(MacroTest):
"""Test case to test ignored print specifiers."""
@property
def category(self) -> str:
return "ignored_specifiers_macro_test"
@dataclass(frozen=True)
class SpecialParsingMacroTest(MacroTest):
"""Test case with special (complicated) parsing scenarios."""
@property
def category(self) -> str:
return "special_parsing_macro_test"
@dataclass(frozen=True)
class CodeSnippetMacroTest(MacroTest):
"""Test case within a larger code snippet."""
@property
def category(self) -> str:
return "code_snippet_macro_test"