xref: /llvm-project/lldb/packages/Python/lldbsuite/test/decorators.py (revision 32c744ae339dc356060636bcdf75a5e2a67fca00)
1# System modules
2from functools import wraps
3from packaging import version
4import ctypes
5import locale
6import os
7import platform
8import re
9import sys
10import tempfile
11import subprocess
12
13# Third-party modules
14import unittest
15
16# LLDB modules
17import lldb
18from . import configuration
19from . import test_categories
20from . import lldbtest_config
21from lldbsuite.support import funcutils
22from lldbsuite.test import lldbplatform
23from lldbsuite.test import lldbplatformutil
24
25
26class DecorateMode:
27    Skip, Xfail = range(2)
28
29
30# You can use no_match to reverse the test of the conditional that is used to match keyword
31# arguments in the skip / xfail decorators.  If oslist=["windows", "linux"] skips windows
32# and linux, oslist=no_match(["windows", "linux"]) skips *unless* windows
33# or linux.
34class no_match:
35    def __init__(self, item):
36        self.item = item
37
38
39def _check_expected_version(comparison, expected, actual):
40    def fn_leq(x, y):
41        return x <= y
42
43    def fn_less(x, y):
44        return x < y
45
46    def fn_geq(x, y):
47        return x >= y
48
49    def fn_greater(x, y):
50        return x > y
51
52    def fn_eq(x, y):
53        return x == y
54
55    def fn_neq(x, y):
56        return x != y
57
58    op_lookup = {
59        "==": fn_eq,
60        "=": fn_eq,
61        "!=": fn_neq,
62        "<>": fn_neq,
63        ">": fn_greater,
64        "<": fn_less,
65        ">=": fn_geq,
66        "<=": fn_leq,
67    }
68
69    return op_lookup[comparison](version.parse(actual), version.parse(expected))
70
71
72def _match_decorator_property(expected, actual):
73    if expected is None:
74        return True
75
76    if actual is None:
77        return False
78
79    if isinstance(expected, no_match):
80        return not _match_decorator_property(expected.item, actual)
81
82    # Python 3.6 doesn't declare a `re.Pattern` type, get the dynamic type.
83    pattern_type = type(re.compile(""))
84    if isinstance(expected, (pattern_type, str)):
85        return re.search(expected, actual) is not None
86
87    if hasattr(expected, "__iter__"):
88        return any(
89            [x is not None and _match_decorator_property(x, actual) for x in expected]
90        )
91
92    return expected == actual
93
94
95def _compiler_supports(
96    compiler, flag, source="int main() {}", output_file=tempfile.NamedTemporaryFile()
97):
98    """Test whether the compiler supports the given flag."""
99    if platform.system() == "Darwin":
100        compiler = "xcrun " + compiler
101    try:
102        cmd = "echo '%s' | %s %s -x c -o %s -" % (
103            source,
104            compiler,
105            flag,
106            output_file.name,
107        )
108        subprocess.check_call(cmd, shell=True)
109    except subprocess.CalledProcessError:
110        return False
111    return True
112
113
114def expectedFailureIf(condition, bugnumber=None):
115    def expectedFailure_impl(func):
116        if isinstance(func, type) and issubclass(func, unittest.TestCase):
117            raise Exception("Decorator can only be used to decorate a test method")
118
119        if condition:
120            return unittest.expectedFailure(func)
121        return func
122
123    if callable(bugnumber):
124        return expectedFailure_impl(bugnumber)
125    else:
126        return expectedFailure_impl
127
128
129def skipTestIfFn(expected_fn, bugnumber=None):
130    def skipTestIfFn_impl(func):
131        if isinstance(func, type) and issubclass(func, unittest.TestCase):
132            reason = expected_fn()
133            # The return value is the reason (or None if we don't skip), so
134            # reason is used for both args.
135            return unittest.skipIf(condition=reason, reason=reason)(func)
136
137        @wraps(func)
138        def wrapper(*args, **kwargs):
139            self = args[0]
140            if funcutils.requires_self(expected_fn):
141                reason = expected_fn(self)
142            else:
143                reason = expected_fn()
144
145            if reason is not None:
146                self.skipTest(reason)
147            else:
148                return func(*args, **kwargs)
149
150        return wrapper
151
152    # Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
153    # or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])).  When called
154    # the first way, the first argument will be the actual function because decorators are
155    # weird like that.  So this is basically a check that says "how was the
156    # decorator used"
157    if callable(bugnumber):
158        return skipTestIfFn_impl(bugnumber)
159    else:
160        return skipTestIfFn_impl
161
162
163def _xfailForDebugInfo(expected_fn, bugnumber=None):
164    def expectedFailure_impl(func):
165        if isinstance(func, type) and issubclass(func, unittest.TestCase):
166            raise Exception("Decorator can only be used to decorate a test method")
167
168        func.__xfail_for_debug_info_cat_fn__ = expected_fn
169        return func
170
171    if callable(bugnumber):
172        return expectedFailure_impl(bugnumber)
173    else:
174        return expectedFailure_impl
175
176
177def _skipForDebugInfo(expected_fn, bugnumber=None):
178    def skipImpl(func):
179        if isinstance(func, type) and issubclass(func, unittest.TestCase):
180            raise Exception("Decorator can only be used to decorate a test method")
181
182        func.__skip_for_debug_info_cat_fn__ = expected_fn
183        return func
184
185    if callable(bugnumber):
186        return skipImpl(bugnumber)
187    else:
188        return skipImpl
189
190
191def _decorateTest(
192    mode,
193    bugnumber=None,
194    oslist=None,
195    hostoslist=None,
196    compiler=None,
197    compiler_version=None,
198    archs=None,
199    triple=None,
200    debug_info=None,
201    swig_version=None,
202    py_version=None,
203    macos_version=None,
204    remote=None,
205    dwarf_version=None,
206    setting=None,
207    asan=None,
208):
209    def fn(actual_debug_info=None):
210        skip_for_os = _match_decorator_property(
211            lldbplatform.translate(oslist), lldbplatformutil.getPlatform()
212        )
213        skip_for_hostos = _match_decorator_property(
214            lldbplatform.translate(hostoslist), lldbplatformutil.getHostPlatform()
215        )
216        skip_for_compiler = _match_decorator_property(
217            compiler, lldbplatformutil.getCompiler()
218        ) and lldbplatformutil.expectedCompilerVersion(compiler_version)
219        skip_for_arch = _match_decorator_property(
220            archs, lldbplatformutil.getArchitecture()
221        )
222        skip_for_debug_info = _match_decorator_property(debug_info, actual_debug_info)
223        skip_for_triple = _match_decorator_property(
224            triple, lldb.selected_platform.GetTriple()
225        )
226        skip_for_remote = _match_decorator_property(
227            remote, lldb.remote_platform is not None
228        )
229
230        skip_for_swig_version = (
231            (swig_version is None)
232            or (not hasattr(lldb, "swig_version"))
233            or (
234                _check_expected_version(
235                    swig_version[0], swig_version[1], lldb.swig_version
236                )
237            )
238        )
239        skip_for_py_version = (py_version is None) or _check_expected_version(
240            py_version[0],
241            py_version[1],
242            "{}.{}".format(sys.version_info.major, sys.version_info.minor),
243        )
244        skip_for_macos_version = (macos_version is None) or (
245            (platform.mac_ver()[0] != "")
246            and (
247                _check_expected_version(
248                    macos_version[0], macos_version[1], platform.mac_ver()[0]
249                )
250            )
251        )
252        skip_for_dwarf_version = (dwarf_version is None) or (
253            _check_expected_version(
254                dwarf_version[0], dwarf_version[1], lldbplatformutil.getDwarfVersion()
255            )
256        )
257        skip_for_setting = (setting is None) or (setting in configuration.settings)
258        skip_for_asan = (asan is None) or is_running_under_asan()
259
260        # For the test to be skipped, all specified (e.g. not None) parameters must be True.
261        # An unspecified parameter means "any", so those are marked skip by default.  And we skip
262        # the final test if all conditions are True.
263        conditions = [
264            (oslist, skip_for_os, "target o/s"),
265            (hostoslist, skip_for_hostos, "host o/s"),
266            (compiler, skip_for_compiler, "compiler or version"),
267            (archs, skip_for_arch, "architecture"),
268            (debug_info, skip_for_debug_info, "debug info format"),
269            (triple, skip_for_triple, "target triple"),
270            (swig_version, skip_for_swig_version, "swig version"),
271            (py_version, skip_for_py_version, "python version"),
272            (macos_version, skip_for_macos_version, "macOS version"),
273            (remote, skip_for_remote, "platform locality (remote/local)"),
274            (dwarf_version, skip_for_dwarf_version, "dwarf version"),
275            (setting, skip_for_setting, "setting"),
276            (asan, skip_for_asan, "running under asan"),
277        ]
278        reasons = []
279        final_skip_result = True
280        for this_condition in conditions:
281            final_skip_result = final_skip_result and this_condition[1]
282            if this_condition[0] is not None and this_condition[1]:
283                reasons.append(this_condition[2])
284        reason_str = None
285        if final_skip_result:
286            mode_str = {DecorateMode.Skip: "skipping", DecorateMode.Xfail: "xfailing"}[
287                mode
288            ]
289            if len(reasons) > 0:
290                reason_str = ",".join(reasons)
291                reason_str = "{} due to the following parameter(s): {}".format(
292                    mode_str, reason_str
293                )
294            else:
295                reason_str = "{} unconditionally".format(mode_str)
296            if bugnumber is not None and not callable(bugnumber):
297                reason_str = reason_str + " [" + str(bugnumber) + "]"
298        return reason_str
299
300    if mode == DecorateMode.Skip:
301        if debug_info:
302            return _skipForDebugInfo(fn, bugnumber)
303        return skipTestIfFn(fn, bugnumber)
304    elif mode == DecorateMode.Xfail:
305        if debug_info:
306            return _xfailForDebugInfo(fn, bugnumber)
307        return expectedFailureIf(fn(), bugnumber)
308    else:
309        return None
310
311
312# provide a function to xfail on defined oslist, compiler version, and archs
313# if none is specified for any argument, that argument won't be checked and thus means for all
314# for example,
315# @expectedFailureAll, xfail for all platform/compiler/arch,
316# @expectedFailureAll(compiler='gcc'), xfail for gcc on all platform/architecture
317# @expectedFailureAll(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), xfail for gcc>=4.9 on linux with i386
318
319
320def expectedFailureAll(
321    bugnumber=None,
322    oslist=None,
323    hostoslist=None,
324    compiler=None,
325    compiler_version=None,
326    archs=None,
327    triple=None,
328    debug_info=None,
329    swig_version=None,
330    py_version=None,
331    macos_version=None,
332    remote=None,
333    dwarf_version=None,
334    setting=None,
335    asan=None,
336):
337    return _decorateTest(
338        DecorateMode.Xfail,
339        bugnumber=bugnumber,
340        oslist=oslist,
341        hostoslist=hostoslist,
342        compiler=compiler,
343        compiler_version=compiler_version,
344        archs=archs,
345        triple=triple,
346        debug_info=debug_info,
347        swig_version=swig_version,
348        py_version=py_version,
349        macos_version=macos_version,
350        remote=remote,
351        dwarf_version=dwarf_version,
352        setting=setting,
353        asan=asan,
354    )
355
356
357# provide a function to skip on defined oslist, compiler version, and archs
358# if none is specified for any argument, that argument won't be checked and thus means for all
359# for example,
360# @skipIf, skip for all platform/compiler/arch,
361# @skipIf(compiler='gcc'), skip for gcc on all platform/architecture
362# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 (all conditions must be true)
363def skipIf(
364    bugnumber=None,
365    oslist=None,
366    hostoslist=None,
367    compiler=None,
368    compiler_version=None,
369    archs=None,
370    triple=None,
371    debug_info=None,
372    swig_version=None,
373    py_version=None,
374    macos_version=None,
375    remote=None,
376    dwarf_version=None,
377    setting=None,
378    asan=None,
379):
380    return _decorateTest(
381        DecorateMode.Skip,
382        bugnumber=bugnumber,
383        oslist=oslist,
384        hostoslist=hostoslist,
385        compiler=compiler,
386        compiler_version=compiler_version,
387        archs=archs,
388        triple=triple,
389        debug_info=debug_info,
390        swig_version=swig_version,
391        py_version=py_version,
392        macos_version=macos_version,
393        remote=remote,
394        dwarf_version=dwarf_version,
395        setting=setting,
396        asan=asan,
397    )
398
399
400def _skip_fn_for_android(reason, api_levels, archs):
401    def impl():
402        result = lldbplatformutil.match_android_device(
403            lldbplatformutil.getArchitecture(),
404            valid_archs=archs,
405            valid_api_levels=api_levels,
406        )
407        return reason if result else None
408
409    return impl
410
411
412def add_test_categories(cat):
413    """Add test categories to a TestCase method"""
414    cat = test_categories.validate(cat, True)
415
416    def impl(func):
417        try:
418            if hasattr(func, "categories"):
419                cat.extend(func.categories)
420            setattr(func, "categories", cat)
421        except AttributeError:
422            raise Exception("Cannot assign categories to inline tests.")
423
424        return func
425
426    return impl
427
428
429def no_debug_info_test(func):
430    """Decorate the item as a test what don't use any debug info. If this annotation is specified
431    then the test runner won't generate a separate test for each debug info format."""
432    if isinstance(func, type) and issubclass(func, unittest.TestCase):
433        raise Exception(
434            "@no_debug_info_test can only be used to decorate a test method"
435        )
436
437    @wraps(func)
438    def wrapper(self, *args, **kwargs):
439        return func(self, *args, **kwargs)
440
441    # Mark this function as such to separate them from the regular tests.
442    wrapper.__no_debug_info_test__ = True
443    return wrapper
444
445
446def apple_simulator_test(platform):
447    """
448    Decorate the test as a test requiring a simulator for a specific platform.
449
450    Consider that a simulator is available if you have the corresponding SDK installed.
451    The SDK identifiers for simulators are iphonesimulator, appletvsimulator, watchsimulator
452    """
453
454    def should_skip_simulator_test():
455        if lldbplatformutil.getHostPlatform() not in ["darwin", "macosx"]:
456            return "simulator tests are run only on darwin hosts."
457        try:
458            output = subprocess.check_output(
459                ["xcodebuild", "-showsdks"], stderr=subprocess.DEVNULL
460            ).decode("utf-8")
461            if re.search("%ssimulator" % platform, output):
462                return None
463            else:
464                return "%s simulator is not supported on this system." % platform
465        except subprocess.CalledProcessError:
466            return "Simulators are unsupported on this system (xcodebuild failed)"
467
468    return skipTestIfFn(should_skip_simulator_test)
469
470
471def debugserver_test(func):
472    """Decorate the item as a debugserver test."""
473    return add_test_categories(["debugserver"])(func)
474
475
476def llgs_test(func):
477    """Decorate the item as a lldb-server test."""
478    return add_test_categories(["llgs"])(func)
479
480
481def expectedFailureOS(
482    oslist, bugnumber=None, compilers=None, debug_info=None, archs=None
483):
484    return expectedFailureAll(
485        oslist=oslist,
486        bugnumber=bugnumber,
487        compiler=compilers,
488        archs=archs,
489        debug_info=debug_info,
490    )
491
492
493def expectedFailureDarwin(bugnumber=None, compilers=None, debug_info=None, archs=None):
494    # For legacy reasons, we support both "darwin" and "macosx" as OS X
495    # triples.
496    return expectedFailureOS(
497        lldbplatform.darwin_all,
498        bugnumber,
499        compilers,
500        debug_info=debug_info,
501        archs=archs,
502    )
503
504
505def expectedFailureAndroid(bugnumber=None, api_levels=None, archs=None):
506    """Mark a test as xfail for Android.
507
508    Arguments:
509        bugnumber - The LLVM pr associated with the problem.
510        api_levels - A sequence of numbers specifying the Android API levels
511            for which a test is expected to fail. None means all API level.
512        arch - A sequence of architecture names specifying the architectures
513            for which a test is expected to fail. None means all architectures.
514    """
515    return expectedFailureIf(
516        _skip_fn_for_android("xfailing on android", api_levels, archs)(), bugnumber
517    )
518
519
520def expectedFailureNetBSD(bugnumber=None):
521    return expectedFailureOS(["netbsd"], bugnumber)
522
523
524def expectedFailureWindows(bugnumber=None):
525    return expectedFailureOS(["windows"], bugnumber)
526
527
528# TODO: This decorator does not do anything. Remove it.
529def expectedFlakey(expected_fn, bugnumber=None):
530    def expectedFailure_impl(func):
531        @wraps(func)
532        def wrapper(*args, **kwargs):
533            func(*args, **kwargs)
534
535        return wrapper
536
537    # Some decorators can be called both with no arguments (e.g. @expectedFailureWindows)
538    # or with arguments (e.g. @expectedFailureWindows(compilers=['gcc'])).  When called
539    # the first way, the first argument will be the actual function because decorators are
540    # weird like that.  So this is basically a check that says "which syntax was the original
541    # function decorated with?"
542    if callable(bugnumber):
543        return expectedFailure_impl(bugnumber)
544    else:
545        return expectedFailure_impl
546
547
548def expectedFlakeyOS(oslist, bugnumber=None, compilers=None):
549    def fn(self):
550        return (
551            lldbplatformutil.getPlatform() in oslist
552            and lldbplatformutil.expectedCompiler(compilers)
553        )
554
555    return expectedFlakey(fn, bugnumber)
556
557
558def expectedFlakeyDarwin(bugnumber=None, compilers=None):
559    # For legacy reasons, we support both "darwin" and "macosx" as OS X
560    # triples.
561    return expectedFlakeyOS(lldbplatformutil.getDarwinOSTriples(), bugnumber, compilers)
562
563
564def expectedFlakeyFreeBSD(bugnumber=None, compilers=None):
565    return expectedFlakeyOS(["freebsd"], bugnumber, compilers)
566
567
568def expectedFlakeyLinux(bugnumber=None, compilers=None):
569    return expectedFlakeyOS(["linux"], bugnumber, compilers)
570
571
572def expectedFlakeyNetBSD(bugnumber=None, compilers=None):
573    return expectedFlakeyOS(["netbsd"], bugnumber, compilers)
574
575
576def expectedFlakeyAndroid(bugnumber=None, api_levels=None, archs=None):
577    return expectedFlakey(
578        _skip_fn_for_android("flakey on android", api_levels, archs), bugnumber
579    )
580
581
582def skipIfOutOfTreeDebugserver(func):
583    """Decorate the item to skip tests if using an out-of-tree debugserver."""
584
585    def is_out_of_tree_debugserver():
586        return (
587            "out-of-tree debugserver"
588            if lldbtest_config.out_of_tree_debugserver
589            else None
590        )
591
592    return skipTestIfFn(is_out_of_tree_debugserver)(func)
593
594
595def skipIfRemote(func):
596    """Decorate the item to skip tests if testing remotely."""
597    return unittest.skipIf(lldb.remote_platform, "skip on remote platform")(func)
598
599
600def skipIfNoSBHeaders(func):
601    """Decorate the item to mark tests that should be skipped when LLDB is built with no SB API headers."""
602
603    def are_sb_headers_missing():
604        if lldb.remote_platform:
605            return "skip because SBHeaders tests make no sense remotely"
606
607        if (
608            lldbplatformutil.getHostPlatform() == "darwin"
609            and configuration.lldb_framework_path
610        ):
611            header = os.path.join(
612                configuration.lldb_framework_path,
613                "Versions",
614                "Current",
615                "Headers",
616                "LLDB.h",
617            )
618            if os.path.exists(header):
619                return None
620
621        header = os.path.join(
622            os.environ["LLDB_SRC"], "include", "lldb", "API", "LLDB.h"
623        )
624        if not os.path.exists(header):
625            return "skip because LLDB.h header not found"
626        return None
627
628    return skipTestIfFn(are_sb_headers_missing)(func)
629
630
631def skipIfRosetta(bugnumber):
632    """Skip a test when running the testsuite on macOS under the Rosetta translation layer."""
633
634    def is_running_rosetta():
635        if lldbplatformutil.getPlatform() in ["darwin", "macosx"]:
636            if (platform.uname()[5] == "arm") and (
637                lldbplatformutil.getArchitecture() == "x86_64"
638            ):
639                return "skipped under Rosetta"
640        return None
641
642    return skipTestIfFn(is_running_rosetta)
643
644
645def skipIfiOSSimulator(func):
646    """Decorate the item to skip tests that should be skipped on the iOS Simulator."""
647
648    def is_ios_simulator():
649        return (
650            "skip on the iOS Simulator"
651            if configuration.lldb_platform_name == "ios-simulator"
652            else None
653        )
654
655    return skipTestIfFn(is_ios_simulator)(func)
656
657
658def skipIfiOS(func):
659    return skipIfPlatform(lldbplatform.translate(lldbplatform.ios))(func)
660
661
662def skipIftvOS(func):
663    return skipIfPlatform(lldbplatform.translate(lldbplatform.tvos))(func)
664
665
666def skipIfwatchOS(func):
667    return skipIfPlatform(lldbplatform.translate(lldbplatform.watchos))(func)
668
669
670def skipIfbridgeOS(func):
671    return skipIfPlatform(lldbplatform.translate(lldbplatform.bridgeos))(func)
672
673
674def skipIfDarwinEmbedded(func):
675    """Decorate the item to skip tests that should be skipped on Darwin armv7/arm64 targets."""
676    return skipIfPlatform(lldbplatform.translate(lldbplatform.darwin_embedded))(func)
677
678
679def skipIfDarwinSimulator(func):
680    """Decorate the item to skip tests that should be skipped on Darwin simulator targets."""
681    return skipIfPlatform(lldbplatform.translate(lldbplatform.darwin_simulator))(func)
682
683
684def skipIfFreeBSD(func):
685    """Decorate the item to skip tests that should be skipped on FreeBSD."""
686    return skipIfPlatform(["freebsd"])(func)
687
688
689def skipIfNetBSD(func):
690    """Decorate the item to skip tests that should be skipped on NetBSD."""
691    return skipIfPlatform(["netbsd"])(func)
692
693
694def skipIfDarwin(func):
695    """Decorate the item to skip tests that should be skipped on Darwin."""
696    return skipIfPlatform(lldbplatform.translate(lldbplatform.darwin_all))(func)
697
698
699def skipIfLinux(func):
700    """Decorate the item to skip tests that should be skipped on Linux."""
701    return skipIfPlatform(["linux"])(func)
702
703
704def skipIfWindows(func):
705    """Decorate the item to skip tests that should be skipped on Windows."""
706    return skipIfPlatform(["windows"])(func)
707
708
709def skipIfWindowsAndNonEnglish(func):
710    """Decorate the item to skip tests that should be skipped on non-English locales on Windows."""
711
712    def is_Windows_NonEnglish():
713        if sys.platform != "win32":
714            return None
715        kernel = ctypes.windll.kernel32
716        if locale.windows_locale[kernel.GetUserDefaultUILanguage()] == "en_US":
717            return None
718        return "skipping non-English Windows locale"
719
720    return skipTestIfFn(is_Windows_NonEnglish)(func)
721
722
723def skipUnlessWindows(func):
724    """Decorate the item to skip tests that should be skipped on any non-Windows platform."""
725    return skipUnlessPlatform(["windows"])(func)
726
727
728def skipUnlessDarwin(func):
729    """Decorate the item to skip tests that should be skipped on any non Darwin platform."""
730    return skipUnlessPlatform(lldbplatformutil.getDarwinOSTriples())(func)
731
732
733def skipUnlessTargetAndroid(func):
734    return unittest.skipUnless(
735        lldbplatformutil.target_is_android(), "requires target to be Android"
736    )(func)
737
738
739def skipIfHostIncompatibleWithTarget(func):
740    """Decorate the item to skip tests when the host and target are incompatible."""
741
742    def is_host_incompatible_with_remote():
743        host_arch = lldbplatformutil.getLLDBArchitecture()
744        host_platform = lldbplatformutil.getHostPlatform()
745        target_arch = lldbplatformutil.getArchitecture()
746        target_platform = lldbplatformutil.getPlatform()
747        if (
748            not (target_arch == "x86_64" and host_arch == "i386")
749            and host_arch != target_arch
750        ):
751            return (
752                "skipping because target %s is not compatible with host architecture %s"
753                % (target_arch, host_arch)
754            )
755        if target_platform != host_platform:
756            return "skipping because target is %s but host is %s" % (
757                target_platform,
758                host_platform,
759            )
760        if lldbplatformutil.match_android_device(target_arch):
761            return "skipping because target is android"
762        return None
763
764    return skipTestIfFn(is_host_incompatible_with_remote)(func)
765
766
767def skipIfPlatform(oslist):
768    """Decorate the item to skip tests if running on one of the listed platforms."""
769    # This decorator cannot be ported to `skipIf` yet because it is used on entire
770    # classes, which `skipIf` explicitly forbids.
771    return unittest.skipIf(
772        lldbplatformutil.getPlatform() in oslist, "skip on %s" % (", ".join(oslist))
773    )
774
775
776def skipUnlessPlatform(oslist):
777    """Decorate the item to skip tests unless running on one of the listed platforms."""
778    # This decorator cannot be ported to `skipIf` yet because it is used on entire
779    # classes, which `skipIf` explicitly forbids.
780    return unittest.skipUnless(
781        lldbplatformutil.getPlatform() in oslist,
782        "requires one of %s" % (", ".join(oslist)),
783    )
784
785
786def skipUnlessArch(arch):
787    """Decorate the item to skip tests unless running on the specified architecture."""
788
789    def arch_doesnt_match():
790        target_arch = lldbplatformutil.getArchitecture()
791        if arch != target_arch:
792            return "Test only runs on " + arch + ", but target arch is " + target_arch
793        return None
794
795    return skipTestIfFn(arch_doesnt_match)
796
797
798def skipIfTargetAndroid(bugnumber=None, api_levels=None, archs=None):
799    """Decorator to skip tests when the target is Android.
800
801    Arguments:
802        api_levels - The API levels for which the test should be skipped. If
803            it is None, then the test will be skipped for all API levels.
804        arch - A sequence of architecture names specifying the architectures
805            for which a test is skipped. None means all architectures.
806    """
807    return skipTestIfFn(
808        _skip_fn_for_android("skipping for android", api_levels, archs), bugnumber
809    )
810
811
812def skipUnlessAppleSilicon(func):
813    """Decorate the item to skip tests unless running on Apple Silicon."""
814
815    def not_apple_silicon():
816        if platform.system() != "Darwin" or lldbplatformutil.getArchitecture() not in [
817            "arm64",
818            "arm64e",
819        ]:
820            return "Test only runs on Apple Silicon"
821        return None
822
823    return skipTestIfFn(not_apple_silicon)(func)
824
825
826def skipUnlessSupportedTypeAttribute(attr):
827    """Decorate the item to skip test unless Clang supports type __attribute__(attr)."""
828
829    def compiler_doesnt_support_struct_attribute():
830        compiler_path = lldbplatformutil.getCompiler()
831        f = tempfile.NamedTemporaryFile()
832        cmd = [lldbplatformutil.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"]
833        p = subprocess.Popen(
834            cmd,
835            stdin=subprocess.PIPE,
836            stdout=subprocess.PIPE,
837            stderr=subprocess.PIPE,
838            universal_newlines=True,
839        )
840        stdout, stderr = p.communicate("struct __attribute__((%s)) Test {};" % attr)
841        if attr in stderr:
842            return "Compiler does not support attribute %s" % (attr)
843        return None
844
845    return skipTestIfFn(compiler_doesnt_support_struct_attribute)
846
847
848def skipUnlessHasCallSiteInfo(func):
849    """Decorate the function to skip testing unless call site info from clang is available."""
850
851    def is_compiler_clang_with_call_site_info():
852        compiler_path = lldbplatformutil.getCompiler()
853        compiler = os.path.basename(compiler_path)
854        if not compiler.startswith("clang"):
855            return "Test requires clang as compiler"
856
857        f = tempfile.NamedTemporaryFile()
858        cmd = (
859            "echo 'int main() {}' | "
860            "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.name)
861        )
862        if os.popen(cmd).close() is not None:
863            return "Compiler can't compile with call site info enabled"
864
865        with open(f.name, "r") as ir_output_file:
866            buf = ir_output_file.read()
867
868        if "DIFlagAllCallsDescribed" not in buf:
869            return "Compiler did not introduce DIFlagAllCallsDescribed IR flag"
870
871        return None
872
873    return skipTestIfFn(is_compiler_clang_with_call_site_info)(func)
874
875
876def skipUnlessThreadSanitizer(func):
877    """Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
878
879    def is_compiler_clang_with_thread_sanitizer():
880        if is_running_under_asan():
881            return "Thread sanitizer tests are disabled when runing under ASAN"
882
883        compiler_path = lldbplatformutil.getCompiler()
884        compiler = os.path.basename(compiler_path)
885        if not compiler.startswith("clang"):
886            return "Test requires clang as compiler"
887        if lldbplatformutil.getPlatform() == "windows":
888            return "TSAN tests not compatible with 'windows'"
889        # rdar://28659145 - TSAN tests don't look like they're supported on i386
890        if (
891            lldbplatformutil.getArchitecture() == "i386"
892            and platform.system() == "Darwin"
893        ):
894            return "TSAN tests not compatible with i386 targets"
895        if not _compiler_supports(compiler_path, "-fsanitize=thread"):
896            return "Compiler cannot compile with -fsanitize=thread"
897        return None
898
899    return skipTestIfFn(is_compiler_clang_with_thread_sanitizer)(func)
900
901
902def skipUnlessUndefinedBehaviorSanitizer(func):
903    """Decorate the item to skip test unless -fsanitize=undefined is supported."""
904
905    def is_compiler_clang_with_ubsan():
906        if is_running_under_asan():
907            return (
908                "Undefined behavior sanitizer tests are disabled when runing under ASAN"
909            )
910
911        # We need to write out the object into a named temp file for inspection.
912        outputf = tempfile.NamedTemporaryFile()
913
914        # Try to compile with ubsan turned on.
915        if not _compiler_supports(
916            lldbplatformutil.getCompiler(),
917            "-fsanitize=undefined",
918            "int main() { int x = 0; return x / x; }",
919            outputf,
920        ):
921            return "Compiler cannot compile with -fsanitize=undefined"
922
923        # Check that we actually see ubsan instrumentation in the binary.
924        cmd = "nm %s" % outputf.name
925        with os.popen(cmd) as nm_output:
926            if "___ubsan_handle_divrem_overflow" not in nm_output.read():
927                return "Division by zero instrumentation is missing"
928
929        # Find the ubsan dylib.
930        # FIXME: This check should go away once compiler-rt gains support for __ubsan_on_report.
931        cmd = (
932            "%s -fsanitize=undefined -x c - -o - -### 2>&1"
933            % lldbplatformutil.getCompiler()
934        )
935        with os.popen(cmd) as cc_output:
936            driver_jobs = cc_output.read()
937            m = re.search(r'"([^"]+libclang_rt.ubsan_osx_dynamic.dylib)"', driver_jobs)
938            if not m:
939                return "Could not find the ubsan dylib used by the driver"
940            ubsan_dylib = m.group(1)
941
942        # Check that the ubsan dylib has special monitor hooks.
943        cmd = "nm -gU %s" % ubsan_dylib
944        with os.popen(cmd) as nm_output:
945            syms = nm_output.read()
946            if "___ubsan_on_report" not in syms:
947                return "Missing ___ubsan_on_report"
948            if "___ubsan_get_current_report_data" not in syms:
949                return "Missing ___ubsan_get_current_report_data"
950
951        # OK, this dylib + compiler works for us.
952        return None
953
954    return skipTestIfFn(is_compiler_clang_with_ubsan)(func)
955
956
957def is_running_under_asan():
958    if "ASAN_OPTIONS" in os.environ:
959        return "ASAN unsupported"
960    return None
961
962
963def skipUnlessAddressSanitizer(func):
964    """Decorate the item to skip test unless Clang -fsanitize=thread is supported."""
965
966    def is_compiler_with_address_sanitizer():
967        # Also don't run tests that use address sanitizer inside an
968        # address-sanitized LLDB. The tests don't support that
969        # configuration.
970        if is_running_under_asan():
971            return "Address sanitizer tests are disabled when runing under ASAN"
972
973        if lldbplatformutil.getPlatform() == "windows":
974            return "ASAN tests not compatible with 'windows'"
975        if not _compiler_supports(lldbplatformutil.getCompiler(), "-fsanitize=address"):
976            return "Compiler cannot compile with -fsanitize=address"
977        return None
978
979    return skipTestIfFn(is_compiler_with_address_sanitizer)(func)
980
981
982def skipIfAsan(func):
983    """Skip this test if the environment is set up to run LLDB *itself* under ASAN."""
984    return skipTestIfFn(is_running_under_asan)(func)
985
986
987def skipUnlessAArch64MTELinuxCompiler(func):
988    """Decorate the item to skip test unless MTE is supported by the test compiler."""
989
990    def is_toolchain_with_mte():
991        compiler_path = lldbplatformutil.getCompiler()
992        f = tempfile.NamedTemporaryFile(delete=False)
993        if lldbplatformutil.getPlatform() == "windows":
994            return "MTE tests are not compatible with 'windows'"
995
996        # Note hostos may be Windows.
997        f.close()
998
999        cmd = f"{compiler_path} -x c -o {f.name} -"
1000        if (
1001            subprocess.run(cmd, shell=True, input="int main() {}".encode()).returncode
1002            != 0
1003        ):
1004            os.remove(f.name)
1005            # Cannot compile at all, don't skip the test
1006            # so that we report the broken compiler normally.
1007            return None
1008
1009        # We need the Linux headers and ACLE MTE intrinsics
1010        test_src = """
1011            #include <asm/hwcap.h>
1012            #include <arm_acle.h>
1013            #ifndef HWCAP2_MTE
1014            #error
1015            #endif
1016            int main() {
1017                void* ptr = __arm_mte_create_random_tag((void*)(0), 0);
1018            }"""
1019        cmd = f"{compiler_path} -march=armv8.5-a+memtag -x c -o {f.name} -"
1020        res = subprocess.run(cmd, shell=True, input=test_src.encode())
1021        os.remove(f.name)
1022        if res.returncode != 0:
1023            return "Toolchain does not support MTE"
1024        return None
1025
1026    return skipTestIfFn(is_toolchain_with_mte)(func)
1027
1028
1029def _get_bool_config(key, fail_value=True):
1030    """
1031    Returns the current LLDB's build config value.
1032    :param key The key to lookup in LLDB's build configuration.
1033    :param fail_value The error value to return when the key can't be found.
1034           Defaults to true so that if an unknown key is lookup up we rather
1035           enable more tests (that then fail) than silently skipping them.
1036    """
1037    config = lldb.SBDebugger.GetBuildConfiguration()
1038    value_node = config.GetValueForKey(key)
1039    return value_node.GetValueForKey("value").GetBooleanValue(fail_value)
1040
1041
1042def _get_bool_config_skip_if_decorator(key):
1043    have = _get_bool_config(key)
1044    return unittest.skipIf(not have, "requires " + key)
1045
1046
1047def skipIfCurlSupportMissing(func):
1048    return _get_bool_config_skip_if_decorator("curl")(func)
1049
1050
1051def skipIfCursesSupportMissing(func):
1052    return _get_bool_config_skip_if_decorator("curses")(func)
1053
1054
1055def skipIfXmlSupportMissing(func):
1056    return _get_bool_config_skip_if_decorator("xml")(func)
1057
1058
1059def skipIfEditlineSupportMissing(func):
1060    return _get_bool_config_skip_if_decorator("editline")(func)
1061
1062
1063def skipIfEditlineWideCharSupportMissing(func):
1064    return _get_bool_config_skip_if_decorator("editline_wchar")(func)
1065
1066
1067def skipIfFBSDVMCoreSupportMissing(func):
1068    return _get_bool_config_skip_if_decorator("fbsdvmcore")(func)
1069
1070
1071def skipIfLLVMTargetMissing(target):
1072    config = lldb.SBDebugger.GetBuildConfiguration()
1073    targets = config.GetValueForKey("targets").GetValueForKey("value")
1074    found = False
1075    for i in range(targets.GetSize()):
1076        if targets.GetItemAtIndex(i).GetStringValue(99) == target:
1077            found = True
1078            break
1079
1080    return unittest.skipIf(not found, "requires " + target)
1081
1082
1083# Call sysctl on darwin to see if a specified hardware feature is available on this machine.
1084def skipUnlessFeature(feature):
1085    def is_feature_enabled():
1086        if platform.system() == "Darwin":
1087            try:
1088                output = subprocess.check_output(
1089                    ["/usr/sbin/sysctl", feature], stderr=subprocess.DEVNULL
1090                ).decode("utf-8")
1091                # If 'feature: 1' was output, then this feature is available and
1092                # the test should not be skipped.
1093                if re.match(r"%s: 1\s*" % feature, output):
1094                    return None
1095                else:
1096                    return "%s is not supported on this system." % feature
1097            except subprocess.CalledProcessError:
1098                return "%s is not supported on this system." % feature
1099
1100    return skipTestIfFn(is_feature_enabled)
1101