Skip to content

Commit 212a60e

Browse files
authored
[lldb][test] Remove self references from decorators (#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.
1 parent 71a7108 commit 212a60e

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
@@ -197,15 +197,17 @@ def _decorateTest(
197197
):
198198
def fn(self):
199199
skip_for_os = _match_decorator_property(
200-
lldbplatform.translate(oslist), self.getPlatform()
200+
lldbplatform.translate(oslist), lldbplatformutil.getPlatform()
201201
)
202202
skip_for_hostos = _match_decorator_property(
203203
lldbplatform.translate(hostoslist), lldbplatformutil.getHostPlatform()
204204
)
205205
skip_for_compiler = _match_decorator_property(
206-
compiler, self.getCompiler()
207-
) and self.expectedCompilerVersion(compiler_version)
208-
skip_for_arch = _match_decorator_property(archs, self.getArchitecture())
206+
compiler, lldbplatformutil.getCompiler()
207+
) and lldbplatformutil.expectedCompilerVersion(compiler_version)
208+
skip_for_arch = _match_decorator_property(
209+
archs, lldbplatformutil.getArchitecture()
210+
)
209211
skip_for_debug_info = _match_decorator_property(debug_info, self.getDebugInfo())
210212
skip_for_triple = _match_decorator_property(
211213
triple, lldb.selected_platform.GetTriple()
@@ -236,7 +238,7 @@ def fn(self):
236238
)
237239
skip_for_dwarf_version = (dwarf_version is None) or (
238240
_check_expected_version(
239-
dwarf_version[0], dwarf_version[1], self.getDwarfVersion()
241+
dwarf_version[0], dwarf_version[1], lldbplatformutil.getDwarfVersion()
240242
)
241243
)
242244
skip_for_setting = (setting is None) or (setting in configuration.settings)
@@ -375,7 +377,9 @@ def skipIf(
375377
def _skip_for_android(reason, api_levels, archs):
376378
def impl(obj):
377379
result = lldbplatformutil.match_android_device(
378-
obj.getArchitecture(), valid_archs=archs, valid_api_levels=api_levels
380+
lldbplatformutil.getArchitecture(),
381+
valid_archs=archs,
382+
valid_api_levels=api_levels,
379383
)
380384
return reason if result else None
381385

@@ -537,7 +541,10 @@ def wrapper(*args, **kwargs):
537541

538542
def expectedFlakeyOS(oslist, bugnumber=None, compilers=None):
539543
def fn(self):
540-
return self.getPlatform() in oslist and self.expectedCompiler(compilers)
544+
return (
545+
lldbplatformutil.getPlatform() in oslist
546+
and lldbplatformutil.expectedCompiler(compilers)
547+
)
541548

542549
return expectedFlakey(fn, bugnumber)
543550

@@ -618,9 +625,11 @@ def are_sb_headers_missing():
618625
def skipIfRosetta(bugnumber):
619626
"""Skip a test when running the testsuite on macOS under the Rosetta translation layer."""
620627

621-
def is_running_rosetta(self):
628+
def is_running_rosetta():
622629
if lldbplatformutil.getPlatform() in ["darwin", "macosx"]:
623-
if (platform.uname()[5] == "arm") and (self.getArchitecture() == "x86_64"):
630+
if (platform.uname()[5] == "arm") and (
631+
lldbplatformutil.getArchitecture() == "x86_64"
632+
):
624633
return "skipped under Rosetta"
625634
return None
626635

@@ -694,7 +703,7 @@ def skipIfWindows(func):
694703
def skipIfWindowsAndNonEnglish(func):
695704
"""Decorate the item to skip tests that should be skipped on non-English locales on Windows."""
696705

697-
def is_Windows_NonEnglish(self):
706+
def is_Windows_NonEnglish():
698707
if sys.platform != "win32":
699708
return None
700709
kernel = ctypes.windll.kernel32
@@ -724,11 +733,15 @@ def skipUnlessTargetAndroid(func):
724733
def skipIfHostIncompatibleWithRemote(func):
725734
"""Decorate the item to skip tests if binaries built on this host are incompatible."""
726735

727-
def is_host_incompatible_with_remote(self):
728-
host_arch = self.getLldbArchitecture()
736+
def is_host_incompatible_with_remote():
737+
host_arch = lldbplatformutil.getLLDBArchitecture()
729738
host_platform = lldbplatformutil.getHostPlatform()
730-
target_arch = self.getArchitecture()
731-
target_platform = "darwin" if self.platformIsDarwin() else self.getPlatform()
739+
target_arch = lldbplatformutil.getArchitecture()
740+
target_platform = (
741+
"darwin"
742+
if lldbplatformutil.platformIsDarwin()
743+
else lldbplatformutil.getPlatform()
744+
)
732745
if (
733746
not (target_arch == "x86_64" and host_arch == "i386")
734747
and host_arch != target_arch
@@ -771,8 +784,8 @@ def skipUnlessPlatform(oslist):
771784
def skipUnlessArch(arch):
772785
"""Decorate the item to skip tests unless running on the specified architecture."""
773786

774-
def arch_doesnt_match(self):
775-
target_arch = self.getArchitecture()
787+
def arch_doesnt_match():
788+
target_arch = lldbplatformutil.getArchitecture()
776789
if arch != target_arch:
777790
return "Test only runs on " + arch + ", but target arch is " + target_arch
778791
return None
@@ -797,8 +810,8 @@ def skipIfTargetAndroid(bugnumber=None, api_levels=None, archs=None):
797810
def skipUnlessAppleSilicon(func):
798811
"""Decorate the item to skip tests unless running on Apple Silicon."""
799812

800-
def not_apple_silicon(test):
801-
if platform.system() != "Darwin" or test.getArchitecture() not in [
813+
def not_apple_silicon():
814+
if platform.system() != "Darwin" or lldbplatformutil.getArchitecture() not in [
802815
"arm64",
803816
"arm64e",
804817
]:
@@ -811,10 +824,10 @@ def not_apple_silicon(test):
811824
def skipUnlessSupportedTypeAttribute(attr):
812825
"""Decorate the item to skip test unless Clang supports type __attribute__(attr)."""
813826

814-
def compiler_doesnt_support_struct_attribute(self):
815-
compiler_path = self.getCompiler()
827+
def compiler_doesnt_support_struct_attribute():
828+
compiler_path = lldbplatformutil.getCompiler()
816829
f = tempfile.NamedTemporaryFile()
817-
cmd = [self.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
830+
cmd = [lldbplatformutil.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
818831
p = subprocess.Popen(
819832
cmd,
820833
stdin=subprocess.PIPE,
@@ -833,8 +846,8 @@ def compiler_doesnt_support_struct_attribute(self):
833846
def skipUnlessHasCallSiteInfo(func):
834847
"""Decorate the function to skip testing unless call site info from clang is available."""
835848

836-
def is_compiler_clang_with_call_site_info(self):
837-
compiler_path = self.getCompiler()
849+
def is_compiler_clang_with_call_site_info():
850+
compiler_path = lldbplatformutil.getCompiler()
838851
compiler = os.path.basename(compiler_path)
839852
if not compiler.startswith("clang"):
840853
return "Test requires clang as compiler"
@@ -861,18 +874,21 @@ def is_compiler_clang_with_call_site_info(self):
861874
def skipUnlessThreadSanitizer(func):
862875
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
863876

864-
def is_compiler_clang_with_thread_sanitizer(self):
877+
def is_compiler_clang_with_thread_sanitizer():
865878
if is_running_under_asan():
866879
return "Thread sanitizer tests are disabled when runing under ASAN"
867880

868-
compiler_path = self.getCompiler()
881+
compiler_path = lldbplatformutil.getCompiler()
869882
compiler = os.path.basename(compiler_path)
870883
if not compiler.startswith("clang"):
871884
return "Test requires clang as compiler"
872885
if lldbplatformutil.getPlatform() == "windows":
873886
return "TSAN tests not compatible with 'windows'"
874887
# rdar://28659145 - TSAN tests don't look like they're supported on i386
875-
if self.getArchitecture() == "i386" and platform.system() == "Darwin":
888+
if (
889+
lldbplatformutil.getArchitecture() == "i386"
890+
and platform.system() == "Darwin"
891+
):
876892
return "TSAN tests not compatible with i386 targets"
877893
if not _compiler_supports(compiler_path, "-fsanitize=thread"):
878894
return "Compiler cannot compile with -fsanitize=thread"
@@ -884,7 +900,7 @@ def is_compiler_clang_with_thread_sanitizer(self):
884900
def skipUnlessUndefinedBehaviorSanitizer(func):
885901
"""Decorate the item to skip test unless -fsanitize=undefined is supported."""
886902

887-
def is_compiler_clang_with_ubsan(self):
903+
def is_compiler_clang_with_ubsan():
888904
if is_running_under_asan():
889905
return (
890906
"Undefined behavior sanitizer tests are disabled when runing under ASAN"
@@ -895,7 +911,7 @@ def is_compiler_clang_with_ubsan(self):
895911

896912
# Try to compile with ubsan turned on.
897913
if not _compiler_supports(
898-
self.getCompiler(),
914+
lldbplatformutil.getCompiler(),
899915
"-fsanitize=undefined",
900916
"int main() { int x = 0; return x / x; }",
901917
outputf,
@@ -910,7 +926,10 @@ def is_compiler_clang_with_ubsan(self):
910926

911927
# Find the ubsan dylib.
912928
# FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report.
913-
cmd = "%s -fsanitize=undefined -x c - -o - -### 2>&1" % self.getCompiler()
929+
cmd = (
930+
"%s -fsanitize=undefined -x c - -o - -### 2>&1"
931+
% lldbplatformutil.getCompiler()
932+
)
914933
with os.popen(cmd) as cc_output:
915934
driver_jobs = cc_output.read()
916935
m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs)
@@ -942,7 +961,7 @@ def is_running_under_asan():
942961
def skipUnlessAddressSanitizer(func):
943962
"""Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
944963

945-
def is_compiler_with_address_sanitizer(self):
964+
def is_compiler_with_address_sanitizer():
946965
# Also don't run tests that use address sanitizer inside an
947966
# address-sanitized LLDB. The tests don't support that
948967
# configuration.
@@ -951,7 +970,7 @@ def is_compiler_with_address_sanitizer(self):
951970

952971
if lldbplatformutil.getPlatform() == "windows":
953972
return "ASAN tests not compatible with 'windows'"
954-
if not _compiler_supports(self.getCompiler(), "-fsanitize=address"):
973+
if not _compiler_supports(lldbplatformutil.getCompiler(), "-fsanitize=address"):
955974
return "Compiler cannot compile with -fsanitize=address"
956975
return None
957976

@@ -966,8 +985,8 @@ def skipIfAsan(func):
966985
def skipUnlessAArch64MTELinuxCompiler(func):
967986
"""Decorate the item to skip test unless MTE is supported by the test compiler."""
968987

969-
def is_toolchain_with_mte(self):
970-
compiler_path = self.getCompiler()
988+
def is_toolchain_with_mte():
989+
compiler_path = lldbplatformutil.getCompiler()
971990
compiler = os.path.basename(compiler_path)
972991
f = tempfile.NamedTemporaryFile()
973992
if lldbplatformutil.getPlatform() == "windows":
@@ -1053,7 +1072,7 @@ def skipIfLLVMTargetMissing(target):
10531072

10541073
# Call sysctl on darwin to see if a specified hardware feature is available on this machine.
10551074
def skipUnlessFeature(feature):
1056-
def is_feature_enabled(self):
1075+
def is_feature_enabled():
10571076
if platform.system() == "Darwin":
10581077
try:
10591078
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
@@ -7,12 +7,15 @@
77
import subprocess
88
import sys
99
import os
10+
from distutils.version import LooseVersion
1011
from urllib.parse import urlparse
1112

1213
# LLDB modules
13-
from . import configuration
1414
import lldb
15+
from . import configuration
16+
from . import lldbtest_config
1517
import lldbsuite.test.lldbplatform as lldbplatform
18+
from lldbsuite.test.builders import get_builder
1619

1720

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

0 commit comments

Comments
 (0)