Skip to content

Commit ffc041d

Browse files
rupprechtJDevlieghere
authored andcommitted
[lldb][test] Remove self references from decorators (llvm#72416)
This is partial step toward removing the vendored `unittest2` dep in favor of the `unittest` library in standard python. One of the large differences is when xfail decorators are evaluated. With the `unittest2` vendored dep, this can happen at the moment of calling the test case, and with LLDB's decorator wrappers, we are passed the test class in the decorator arg. With the `unittest` framework, this is determined much earlier; we cannot decide when the test is about to start that we need to xfail. Fortunately, almost none of these checks require any state that can't be determined statically. For this patch, I moved the impl for all the checks to `lldbplatformutil` and pointed the decorators to that, removing as many `self` (i.e. test class object) references as possible. I left wrappers within `TestBase` that forward to `lldbplatformutil` for convenience, but we should probably remove those later. The remaining check that can't be moved statically is the check for the debug info type (e.g. to xfail only for dwarf). Fixing that requires a different approach, so I will postpone that to the next patch. (cherry picked from commit 212a60e)
1 parent fa20c15 commit ffc041d

File tree

3 files changed

+207
-129
lines changed

3 files changed

+207
-129
lines changed

lldb/packages/Python/lldbsuite/test/decorators.py

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,17 @@ def _decorateTest(
200200
):
201201
def fn(self):
202202
skip_for_os = _match_decorator_property(
203-
lldbplatform.translate(oslist), self.getPlatform()
203+
lldbplatform.translate(oslist), lldbplatformutil.getPlatform()
204204
)
205205
skip_for_hostos = _match_decorator_property(
206206
lldbplatform.translate(hostoslist), lldbplatformutil.getHostPlatform()
207207
)
208208
skip_for_compiler = _match_decorator_property(
209-
compiler, self.getCompiler()
210-
) and self.expectedCompilerVersion(compiler_version)
211-
skip_for_arch = _match_decorator_property(archs, self.getArchitecture())
209+
compiler, lldbplatformutil.getCompiler()
210+
) and lldbplatformutil.expectedCompilerVersion(compiler_version)
211+
skip_for_arch = _match_decorator_property(
212+
archs, lldbplatformutil.getArchitecture()
213+
)
212214
skip_for_debug_info = _match_decorator_property(debug_info, self.getDebugInfo())
213215
skip_for_triple = _match_decorator_property(
214216
triple, lldb.selected_platform.GetTriple()
@@ -241,7 +243,7 @@ def fn(self):
241243
)
242244
skip_for_dwarf_version = (dwarf_version is None) or (
243245
_check_expected_version(
244-
dwarf_version[0], dwarf_version[1], self.getDwarfVersion()
246+
dwarf_version[0], dwarf_version[1], lldbplatformutil.getDwarfVersion()
245247
)
246248
)
247249
skip_for_setting = (setting is None) or (setting in configuration.settings)
@@ -380,7 +382,9 @@ def skipIf(
380382
def _skip_for_android(reason, api_levels, archs):
381383
def impl(obj):
382384
result = lldbplatformutil.match_android_device(
383-
obj.getArchitecture(), valid_archs=archs, valid_api_levels=api_levels
385+
lldbplatformutil.getArchitecture(),
386+
valid_archs=archs,
387+
valid_api_levels=api_levels,
384388
)
385389
return reason if result else None
386390

@@ -542,7 +546,10 @@ def wrapper(*args, **kwargs):
542546

543547
def expectedFlakeyOS(oslist, bugnumber=None, compilers=None):
544548
def fn(self):
545-
return self.getPlatform() in oslist and self.expectedCompiler(compilers)
549+
return (
550+
lldbplatformutil.getPlatform() in oslist
551+
and lldbplatformutil.expectedCompiler(compilers)
552+
)
546553

547554
return expectedFlakey(fn, bugnumber)
548555

@@ -623,9 +630,11 @@ def are_sb_headers_missing():
623630
def skipIfRosetta(bugnumber):
624631
"""Skip a test when running the testsuite on macOS under the Rosetta translation layer."""
625632

626-
def is_running_rosetta(self):
633+
def is_running_rosetta():
627634
if lldbplatformutil.getPlatform() in ["darwin", "macosx"]:
628-
if (platform.uname()[5] == "arm") and (self.getArchitecture() == "x86_64"):
635+
if (platform.uname()[5] == "arm") and (
636+
lldbplatformutil.getArchitecture() == "x86_64"
637+
):
629638
return "skipped under Rosetta"
630639
return None
631640

@@ -699,7 +708,7 @@ def skipIfWindows(func):
699708
def skipIfWindowsAndNonEnglish(func):
700709
"""Decorate the item to skip tests that should be skipped on non-English locales on Windows."""
701710

702-
def is_Windows_NonEnglish(self):
711+
def is_Windows_NonEnglish():
703712
if sys.platform != "win32":
704713
return None
705714
kernel = ctypes.windll.kernel32
@@ -763,11 +772,15 @@ def is_not_swift_compatible(self):
763772
def skipIfHostIncompatibleWithRemote(func):
764773
"""Decorate the item to skip tests if binaries built on this host are incompatible."""
765774

766-
def is_host_incompatible_with_remote(self):
767-
host_arch = self.getLldbArchitecture()
775+
def is_host_incompatible_with_remote():
776+
host_arch = lldbplatformutil.getLLDBArchitecture()
768777
host_platform = lldbplatformutil.getHostPlatform()
769-
target_arch = self.getArchitecture()
770-
target_platform = "darwin" if self.platformIsDarwin() else self.getPlatform()
778+
target_arch = lldbplatformutil.getArchitecture()
779+
target_platform = (
780+
"darwin"
781+
if lldbplatformutil.platformIsDarwin()
782+
else lldbplatformutil.getPlatform()
783+
)
771784
if (
772785
not (target_arch == "x86_64" and host_arch == "i386")
773786
and host_arch != target_arch
@@ -810,8 +823,8 @@ def skipUnlessPlatform(oslist):
810823
def skipUnlessArch(arch):
811824
"""Decorate the item to skip tests unless running on the specified architecture."""
812825

813-
def arch_doesnt_match(self):
814-
target_arch = self.getArchitecture()
826+
def arch_doesnt_match():
827+
target_arch = lldbplatformutil.getArchitecture()
815828
if arch != target_arch:
816829
return "Test only runs on " + arch + ", but target arch is " + target_arch
817830
return None
@@ -836,8 +849,8 @@ def skipIfTargetAndroid(bugnumber=None, api_levels=None, archs=None):
836849
def skipUnlessAppleSilicon(func):
837850
"""Decorate the item to skip tests unless running on Apple Silicon."""
838851

839-
def not_apple_silicon(test):
840-
if platform.system() != "Darwin" or test.getArchitecture() not in [
852+
def not_apple_silicon():
853+
if platform.system() != "Darwin" or lldbplatformutil.getArchitecture() not in [
841854
"arm64",
842855
"arm64e",
843856
]:
@@ -850,10 +863,10 @@ def not_apple_silicon(test):
850863
def skipUnlessSupportedTypeAttribute(attr):
851864
"""Decorate the item to skip test unless Clang supports type __attribute__(attr)."""
852865

853-
def compiler_doesnt_support_struct_attribute(self):
854-
compiler_path = self.getCompiler()
866+
def compiler_doesnt_support_struct_attribute():
867+
compiler_path = lldbplatformutil.getCompiler()
855868
f = tempfile.NamedTemporaryFile()
856-
cmd = [self.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
869+
cmd = [lldbplatformutil.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
857870
p = subprocess.Popen(
858871
cmd,
859872
stdin=subprocess.PIPE,
@@ -872,8 +885,8 @@ def compiler_doesnt_support_struct_attribute(self):
872885
def skipUnlessHasCallSiteInfo(func):
873886
"""Decorate the function to skip testing unless call site info from clang is available."""
874887

875-
def is_compiler_clang_with_call_site_info(self):
876-
compiler_path = self.getCompiler()
888+
def is_compiler_clang_with_call_site_info():
889+
compiler_path = lldbplatformutil.getCompiler()
877890
compiler = os.path.basename(compiler_path)
878891
if not compiler.startswith("clang"):
879892
return "Test requires clang as compiler"
@@ -900,18 +913,21 @@ def is_compiler_clang_with_call_site_info(self):
900913
def skipUnlessThreadSanitizer(func):
901914
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
902915

903-
def is_compiler_clang_with_thread_sanitizer(self):
916+
def is_compiler_clang_with_thread_sanitizer():
904917
if is_running_under_asan():
905918
return "Thread sanitizer tests are disabled when runing under ASAN"
906919

907-
compiler_path = self.getCompiler()
920+
compiler_path = lldbplatformutil.getCompiler()
908921
compiler = os.path.basename(compiler_path)
909922
if not compiler.startswith("clang"):
910923
return "Test requires clang as compiler"
911924
if lldbplatformutil.getPlatform() == "windows":
912925
return "TSAN tests not compatible with 'windows'"
913926
# rdar://28659145 - TSAN tests don't look like they're supported on i386
914-
if self.getArchitecture() == "i386" and platform.system() == "Darwin":
927+
if (
928+
lldbplatformutil.getArchitecture() == "i386"
929+
and platform.system() == "Darwin"
930+
):
915931
return "TSAN tests not compatible with i386 targets"
916932
if not _compiler_supports(compiler_path, "-fsanitize=thread"):
917933
return "Compiler cannot compile with -fsanitize=thread"
@@ -923,7 +939,7 @@ def is_compiler_clang_with_thread_sanitizer(self):
923939
def skipUnlessUndefinedBehaviorSanitizer(func):
924940
"""Decorate the item to skip test unless -fsanitize=undefined is supported."""
925941

926-
def is_compiler_clang_with_ubsan(self):
942+
def is_compiler_clang_with_ubsan():
927943
if is_running_under_asan():
928944
return (
929945
"Undefined behavior sanitizer tests are disabled when runing under ASAN"
@@ -934,7 +950,7 @@ def is_compiler_clang_with_ubsan(self):
934950

935951
# Try to compile with ubsan turned on.
936952
if not _compiler_supports(
937-
self.getCompiler(),
953+
lldbplatformutil.getCompiler(),
938954
"-fsanitize=undefined",
939955
"int main() { int x = 0; return x / x; }",
940956
outputf,
@@ -949,7 +965,10 @@ def is_compiler_clang_with_ubsan(self):
949965

950966
# Find the ubsan dylib.
951967
# FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report.
952-
cmd = "%s -fsanitize=undefined -x c - -o - -### 2>&1" % self.getCompiler()
968+
cmd = (
969+
"%s -fsanitize=undefined -x c - -o - -### 2>&1"
970+
% lldbplatformutil.getCompiler()
971+
)
953972
with os.popen(cmd) as cc_output:
954973
driver_jobs = cc_output.read()
955974
m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs)
@@ -981,7 +1000,7 @@ def is_running_under_asan():
9811000
def skipUnlessAddressSanitizer(func):
9821001
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
9831002

984-
def is_compiler_with_address_sanitizer(self):
1003+
def is_compiler_with_address_sanitizer():
9851004
# Also don't run tests that use address sanitizer inside an
9861005
# address-sanitized LLDB. The tests don't support that
9871006
# configuration.
@@ -990,7 +1009,7 @@ def is_compiler_with_address_sanitizer(self):
9901009

9911010
if lldbplatformutil.getPlatform() == "windows":
9921011
return "ASAN tests not compatible with 'windows'"
993-
if not _compiler_supports(self.getCompiler(), "-fsanitize=address"):
1012+
if not _compiler_supports(lldbplatformutil.getCompiler(), "-fsanitize=address"):
9941013
return "Compiler cannot compile with -fsanitize=address"
9951014
return None
9961015

@@ -1043,8 +1062,8 @@ def skipIfAsan(func):
10431062
def skipUnlessAArch64MTELinuxCompiler(func):
10441063
"""Decorate the item to skip test unless MTE is supported by the test compiler."""
10451064

1046-
def is_toolchain_with_mte(self):
1047-
compiler_path = self.getCompiler()
1065+
def is_toolchain_with_mte():
1066+
compiler_path = lldbplatformutil.getCompiler()
10481067
compiler = os.path.basename(compiler_path)
10491068
f = tempfile.NamedTemporaryFile()
10501069
if lldbplatformutil.getPlatform() == "windows":
@@ -1130,7 +1149,7 @@ def skipIfLLVMTargetMissing(target):
11301149

11311150
# Call sysctl on darwin to see if a specified hardware feature is available on this machine.
11321151
def skipUnlessFeature(feature):
1133-
def is_feature_enabled(self):
1152+
def is_feature_enabled():
11341153
if platform.system() == "Darwin":
11351154
try:
11361155
DEVNULL = open(os.devnull, "w")

lldb/packages/Python/lldbsuite/test/lldbplatformutil.py

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@
99
import subprocess
1010
import sys
1111
import os
12+
from distutils.version import LooseVersion
1213
from urllib.parse import urlparse
1314

1415
# LLDB modules
15-
from . import configuration
1616
import lldb
17+
from . import configuration
18+
from . import lldbtest_config
1719
import lldbsuite.test.lldbplatform as lldbplatform
20+
from lldbsuite.test.builders import get_builder
1821

1922

2023
def check_first_register_readable(test_case):
@@ -186,3 +189,144 @@ def hasChattyStderr(test_case):
186189
):
187190
return True # The dynamic linker on the device will complain about unknown DT entries
188191
return False
192+
193+
194+
def builder_module():
195+
return get_builder(sys.platform)
196+
197+
198+
def getArchitecture():
199+
"""Returns the architecture in effect the test suite is running with."""
200+
module = builder_module()
201+
arch = module.getArchitecture()
202+
if arch == "amd64":
203+
arch = "x86_64"
204+
if arch in ["armv7l", "armv8l"]:
205+
arch = "arm"
206+
return arch
207+
208+
209+
lldbArchitecture = None
210+
211+
212+
def getLLDBArchitecture():
213+
"""Returns the architecture of the lldb binary."""
214+
global lldbArchitecture
215+
if not lldbArchitecture:
216+
# These two target settings prevent lldb from doing setup that does
217+
# nothing but slow down the end goal of printing the architecture.
218+
command = [
219+
lldbtest_config.lldbExec,
220+
"-x",
221+
"-b",
222+
"-o",
223+
"settings set target.preload-symbols false",
224+
"-o",
225+
"settings set target.load-script-from-symbol-file false",
226+
"-o",
227+
"file " + lldbtest_config.lldbExec,
228+
]
229+
230+
output = subprocess.check_output(command)
231+
str = output.decode()
232+
233+
for line in str.splitlines():
234+
m = re.search(r"Current executable set to '.*' \((.*)\)\.", line)
235+
if m:
236+
lldbArchitecture = m.group(1)
237+
break
238+
239+
return lldbArchitecture
240+
241+
242+
def getCompiler():
243+
"""Returns the compiler in effect the test suite is running with."""
244+
module = builder_module()
245+
return module.getCompiler()
246+
247+
248+
def getCompilerBinary():
249+
"""Returns the compiler binary the test suite is running with."""
250+
return getCompiler().split()[0]
251+
252+
253+
def getCompilerVersion():
254+
"""Returns a string that represents the compiler version.
255+
Supports: llvm, clang.
256+
"""
257+
compiler = getCompilerBinary()
258+
version_output = subprocess.check_output([compiler, "--version"], errors="replace")
259+
m = re.search("version ([0-9.]+)", version_output)
260+
if m:
261+
return m.group(1)
262+
return "unknown"
263+
264+
265+
def getDwarfVersion():
266+
"""Returns the dwarf version generated by clang or '0'."""
267+
if configuration.dwarf_version:
268+
return str(configuration.dwarf_version)
269+
if "clang" in getCompiler():
270+
try:
271+
triple = builder_module().getTriple(getArchitecture())
272+
target = ["-target", triple] if triple else []
273+
driver_output = subprocess.check_output(
274+
[getCompiler()] + target + "-g -c -x c - -o - -###".split(),
275+
stderr=subprocess.STDOUT,
276+
)
277+
driver_output = driver_output.decode("utf-8")
278+
for line in driver_output.split(os.linesep):
279+
m = re.search("dwarf-version=([0-9])", line)
280+
if m:
281+
return m.group(1)
282+
except subprocess.CalledProcessError:
283+
pass
284+
return "0"
285+
286+
287+
def expectedCompilerVersion(compiler_version):
288+
"""Returns True iff compiler_version[1] matches the current compiler version.
289+
Use compiler_version[0] to specify the operator used to determine if a match has occurred.
290+
Any operator other than the following defaults to an equality test:
291+
'>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not'
292+
293+
If the current compiler version cannot be determined, we assume it is close to the top
294+
of trunk, so any less-than or equal-to comparisons will return False, and any
295+
greater-than or not-equal-to comparisons will return True.
296+
"""
297+
if compiler_version is None:
298+
return True
299+
operator = str(compiler_version[0])
300+
version = compiler_version[1]
301+
302+
if version is None:
303+
return True
304+
305+
test_compiler_version = getCompilerVersion()
306+
if test_compiler_version == "unknown":
307+
# Assume the compiler version is at or near the top of trunk.
308+
return operator in [">", ">=", "!", "!=", "not"]
309+
310+
if operator == ">":
311+
return LooseVersion(test_compiler_version) > LooseVersion(version)
312+
if operator == ">=" or operator == "=>":
313+
return LooseVersion(test_compiler_version) >= LooseVersion(version)
314+
if operator == "<":
315+
return LooseVersion(test_compiler_version) < LooseVersion(version)
316+
if operator == "<=" or operator == "=<":
317+
return LooseVersion(test_compiler_version) <= LooseVersion(version)
318+
if operator == "!=" or operator == "!" or operator == "not":
319+
return str(version) not in str(test_compiler_version)
320+
return str(version) in str(test_compiler_version)
321+
322+
323+
def expectedCompiler(compilers):
324+
"""Returns True iff any element of compilers is a sub-string of the current compiler."""
325+
if compilers is None:
326+
return True
327+
328+
for compiler in compilers:
329+
if compiler in getCompiler():
330+
return True
331+
332+
return False

0 commit comments

Comments
 (0)