[Lldb-commits] [lldb] [test] Switch LLDB API tests from vendored unittest2 to unittest (PR #79945)

Jordan Rupprecht via lldb-commits lldb-commits at lists.llvm.org
Mon Jan 29 21:08:38 PST 2024


https://github.com/rupprecht created https://github.com/llvm/llvm-project/pull/79945

This removes the dependency LLDB API tests have on lldb/third_party/Python/module/unittest2, and instead uses the standard one provided by Python.

This does not actually remove the vendored dep yet, nor update the docs. I'll do both those once this sticks.

Non-trivial changes to call out:
- expected failures (i.e. "bugnumber") don't have a reason anymore, so those params were removed
- `assertItemsEqual` is now called `assertCountEqual`
- When a test is marked xfail, our copy of unittest2 considers failures during teardown to be OK, but modern unittest does not. See TestThreadLocal.py. (Very likely could be a real bug/leak).
- Our copy of unittest2 was patched to print all test results, even ones that don't happen, e.g. `(5 passes, 0 failures, 1 errors, 0 skipped, ...)`, but standard unittest prints a terser message that omits test result types that didn't happen, e.g. `OK (skipped=1)`. Our lit integration parses this stderr and needs to be updated w/ that expectation.

I tested this w/ `ninja check-lldb-abi` on Linux. There's a good chance non-Linux tests have similar quirks, but I'm not able to uncover those.

>From fa34f52771a31a424503c2a4b33c1a31b4ecb597 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Mon, 29 Jan 2024 19:07:36 -0800
Subject: [PATCH 1/5] Blanket unittest2 -> unittest replacement (excluding
 docs)

---
 .../Python/lldbsuite/test/configuration.py    |  4 +--
 .../Python/lldbsuite/test/decorators.py       | 34 +++++++++----------
 lldb/packages/Python/lldbsuite/test/dotest.py | 12 +++----
 .../Python/lldbsuite/test/lldbtest.py         |  8 ++---
 .../Python/lldbsuite/test/test_result.py      |  4 +--
 .../API/commands/expression/test/TestExprs.py |  4 +--
 .../TestThreadPlanUserBreakpoint.py           |  2 +-
 .../jitloader_gdb/TestJITLoaderGDB.py         |  4 +--
 .../thread/state/TestThreadStates.py          |  6 ++--
 .../API/functionalities/tty/TestTerminal.py   |  6 ++--
 .../API/lang/c/shared_lib/TestSharedLib.py    |  4 +--
 .../TestSharedLibStrippedSymbols.py           |  4 +--
 .../lang/cpp/namespace/TestNamespaceLookup.py | 10 +++---
 .../TestCppReferenceToOuterClass.py           |  4 +--
 .../lang/objc/hidden-ivars/TestHiddenIvars.py |  4 +--
 .../API/macosx/universal/TestUniversal.py     |  8 ++---
 .../test/test_lldbgdbserverutils.py           |  4 +--
 17 files changed, 61 insertions(+), 61 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/configuration.py b/lldb/packages/Python/lldbsuite/test/configuration.py
index 2a4b9b3c070c7..685f491c85fe1 100644
--- a/lldb/packages/Python/lldbsuite/test/configuration.py
+++ b/lldb/packages/Python/lldbsuite/test/configuration.py
@@ -12,14 +12,14 @@
 
 
 # Third-party modules
-import unittest2
+import unittest
 
 # LLDB Modules
 import lldbsuite
 
 
 # The test suite.
-suite = unittest2.TestSuite()
+suite = unittest.TestSuite()
 
 # The list of categories we said we care about
 categories_list = None
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index 0fb146913388e..a5e1fa51cf6e6 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -11,7 +11,7 @@
 import subprocess
 
 # Third-party modules
-import unittest2
+import unittest
 
 # LLDB modules
 import lldb
@@ -115,11 +115,11 @@ def _compiler_supports(
 
 def expectedFailureIf(condition, bugnumber=None):
     def expectedFailure_impl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             raise Exception("Decorator can only be used to decorate a test method")
 
         if condition:
-            return unittest2.expectedFailure(func)
+            return unittest.expectedFailure(func)
         return func
 
     if callable(bugnumber):
@@ -130,14 +130,14 @@ def expectedFailure_impl(func):
 
 def expectedFailureIfFn(expected_fn, bugnumber=None):
     def expectedFailure_impl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             raise Exception("Decorator can only be used to decorate a test method")
 
         @wraps(func)
         def wrapper(*args, **kwargs):
             xfail_reason = expected_fn(*args, **kwargs)
             if xfail_reason is not None:
-                xfail_func = unittest2.expectedFailure(func)
+                xfail_func = unittest.expectedFailure(func)
                 xfail_func(*args, **kwargs)
             else:
                 func(*args, **kwargs)
@@ -157,11 +157,11 @@ def wrapper(*args, **kwargs):
 
 def skipTestIfFn(expected_fn, bugnumber=None):
     def skipTestIfFn_impl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             reason = expected_fn()
             # The return value is the reason (or None if we don't skip), so
             # reason is used for both args.
-            return unittest2.skipIf(condition=reason, reason=reason)(func)
+            return unittest.skipIf(condition=reason, reason=reason)(func)
 
         @wraps(func)
         def wrapper(*args, **kwargs):
@@ -191,7 +191,7 @@ def wrapper(*args, **kwargs):
 
 def _xfailForDebugInfo(expected_fn, bugnumber=None):
     def expectedFailure_impl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             raise Exception("Decorator can only be used to decorate a test method")
 
         func.__xfail_for_debug_info_cat_fn__ = expected_fn
@@ -205,7 +205,7 @@ def expectedFailure_impl(func):
 
 def _skipForDebugInfo(expected_fn, bugnumber=None):
     def skipImpl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             raise Exception("Decorator can only be used to decorate a test method")
 
         func.__skip_for_debug_info_cat_fn__ = expected_fn
@@ -434,7 +434,7 @@ def add_test_categories(cat):
     cat = test_categories.validate(cat, True)
 
     def impl(func):
-        if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+        if isinstance(func, type) and issubclass(func, unittest.TestCase):
             raise Exception(
                 "@add_test_categories can only be used to decorate a test method"
             )
@@ -465,7 +465,7 @@ def should_skip_benchmarks_test():
 def no_debug_info_test(func):
     """Decorate the item as a test what don't use any debug info. If this annotation is specified
     then the test runner won't generate a separate test for each debug info format."""
-    if isinstance(func, type) and issubclass(func, unittest2.TestCase):
+    if isinstance(func, type) and issubclass(func, unittest.TestCase):
         raise Exception(
             "@no_debug_info_test can only be used to decorate a test method"
         )
@@ -631,7 +631,7 @@ def is_out_of_tree_debugserver():
 
 def skipIfRemote(func):
     """Decorate the item to skip tests if testing remotely."""
-    return unittest2.skipIf(lldb.remote_platform, "skip on remote platform")(func)
+    return unittest.skipIf(lldb.remote_platform, "skip on remote platform")(func)
 
 
 def skipIfNoSBHeaders(func):
@@ -768,7 +768,7 @@ def skipUnlessDarwin(func):
 
 
 def skipUnlessTargetAndroid(func):
-    return unittest2.skipUnless(
+    return unittest.skipUnless(
         lldbplatformutil.target_is_android(), "requires target to be Android"
     )(func)
 
@@ -809,7 +809,7 @@ def skipIfPlatform(oslist):
     """Decorate the item to skip tests if running on one of the listed platforms."""
     # This decorator cannot be ported to `skipIf` yet because it is used on entire
     # classes, which `skipIf` explicitly forbids.
-    return unittest2.skipIf(
+    return unittest.skipIf(
         lldbplatformutil.getPlatform() in oslist, "skip on %s" % (", ".join(oslist))
     )
 
@@ -818,7 +818,7 @@ def skipUnlessPlatform(oslist):
     """Decorate the item to skip tests unless running on one of the listed platforms."""
     # This decorator cannot be ported to `skipIf` yet because it is used on entire
     # classes, which `skipIf` explicitly forbids.
-    return unittest2.skipUnless(
+    return unittest.skipUnless(
         lldbplatformutil.getPlatform() in oslist,
         "requires one of %s" % (", ".join(oslist)),
     )
@@ -1078,7 +1078,7 @@ def _get_bool_config(key, fail_value=True):
 
 def _get_bool_config_skip_if_decorator(key):
     have = _get_bool_config(key)
-    return unittest2.skipIf(not have, "requires " + key)
+    return unittest.skipIf(not have, "requires " + key)
 
 
 def skipIfCursesSupportMissing(func):
@@ -1110,7 +1110,7 @@ def skipIfLLVMTargetMissing(target):
             found = True
             break
 
-    return unittest2.skipIf(not found, "requires " + target)
+    return unittest.skipIf(not found, "requires " + target)
 
 
 # Call sysctl on darwin to see if a specified hardware feature is available on this machine.
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 4393e0caacaab..e6d9a2efb3159 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -33,7 +33,7 @@
 import tempfile
 
 # Third-party modules
-import unittest2
+import unittest
 
 # LLDB Modules
 import lldbsuite
@@ -658,7 +658,7 @@ def iter_filters():
     for filterspec in iter_filters():
         filtered = True
         print("adding filter spec %s to module %s" % (filterspec, repr(module)))
-        tests = unittest2.defaultTestLoader.loadTestsFromName(filterspec, module)
+        tests = unittest.defaultTestLoader.loadTestsFromName(filterspec, module)
         configuration.suite.addTests(tests)
 
     # Forgo this module if the (base, filterspec) combo is invalid
@@ -670,7 +670,7 @@ def iter_filters():
         # Also the fail-over case when the filterspec branch
         # (base, filterspec) combo doesn't make sense.
         configuration.suite.addTests(
-            unittest2.defaultTestLoader.loadTestsFromName(base)
+            unittest.defaultTestLoader.loadTestsFromName(base)
         )
 
 
@@ -1032,7 +1032,7 @@ def run_suite():
     #
 
     # Install the control-c handler.
-    unittest2.signals.installHandler()
+    unittest.signals.installHandler()
 
     #
     # Invoke the default TextTestRunner to run the test suite
@@ -1066,7 +1066,7 @@ def run_suite():
 
     # Invoke the test runner.
     if configuration.count == 1:
-        result = unittest2.TextTestRunner(
+        result = unittest.TextTestRunner(
             stream=sys.stderr,
             verbosity=configuration.verbose,
             resultclass=test_result.LLDBTestResult,
@@ -1077,7 +1077,7 @@ def run_suite():
         # not enforced.
         test_result.LLDBTestResult.__ignore_singleton__ = True
         for i in range(configuration.count):
-            result = unittest2.TextTestRunner(
+            result = unittest.TextTestRunner(
                 stream=sys.stderr,
                 verbosity=configuration.verbose,
                 resultclass=test_result.LLDBTestResult,
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index d944b09cbcc47..2c4446d8aebc5 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -44,7 +44,7 @@
 import traceback
 
 # Third-party modules
-import unittest2
+import unittest
 
 # LLDB modules
 import lldb
@@ -517,7 +517,7 @@ def builder_module():
     return lldbplatformutil.builder_module()
 
 
-class Base(unittest2.TestCase):
+class Base(unittest.TestCase):
     """
     Abstract base for performing lldb (see TestBase) or other generic tests (see
     BenchBase for one example).  lldbtest.Base works with the test driver to
@@ -1704,13 +1704,13 @@ def test_method(self, attrvalue=attrvalue):
 
                     xfail_reason = xfail_for_debug_info_cat_fn(cat)
                     if xfail_reason:
-                        test_method = unittest2.expectedFailure(xfail_reason)(
+                        test_method = unittest.expectedFailure(xfail_reason)(
                             test_method
                         )
 
                     skip_reason = skip_for_debug_info_cat_fn(cat)
                     if skip_reason:
-                        test_method = unittest2.skip(skip_reason)(test_method)
+                        test_method = unittest.skip(skip_reason)(test_method)
 
                     newattrs[method_name] = test_method
 
diff --git a/lldb/packages/Python/lldbsuite/test/test_result.py b/lldb/packages/Python/lldbsuite/test/test_result.py
index cb84c909c4196..2690906232b97 100644
--- a/lldb/packages/Python/lldbsuite/test/test_result.py
+++ b/lldb/packages/Python/lldbsuite/test/test_result.py
@@ -12,14 +12,14 @@
 import traceback
 
 # Third-party modules
-import unittest2
+import unittest
 
 # LLDB Modules
 from . import configuration
 from lldbsuite.test_event import build_exception
 
 
-class LLDBTestResult(unittest2.TextTestResult):
+class LLDBTestResult(unittest.TextTestResult):
     """
     Enforce a singleton pattern to allow introspection of test progress.
 
diff --git a/lldb/test/API/commands/expression/test/TestExprs.py b/lldb/test/API/commands/expression/test/TestExprs.py
index e95c76b7104c2..0e3d2e6cf41ff 100644
--- a/lldb/test/API/commands/expression/test/TestExprs.py
+++ b/lldb/test/API/commands/expression/test/TestExprs.py
@@ -12,7 +12,7 @@
 """
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -46,7 +46,7 @@ def build_and_run(self):
 
     # llvm.org/pr17135 <rdar://problem/14874559>
     # APFloat::toString does not identify the correct (i.e. least) precision.
-    @unittest2.expectedFailure
+    @unittest.expectedFailure
     def test_floating_point_expr_commands(self):
         self.build_and_run()
 
diff --git a/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py
index d9b7426b14844..ee597ad2b148c 100644
--- a/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py
+++ b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py
@@ -8,7 +8,7 @@
 """
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
diff --git a/lldb/test/API/functionalities/jitloader_gdb/TestJITLoaderGDB.py b/lldb/test/API/functionalities/jitloader_gdb/TestJITLoaderGDB.py
index a074215244787..98c0b149003df 100644
--- a/lldb/test/API/functionalities/jitloader_gdb/TestJITLoaderGDB.py
+++ b/lldb/test/API/functionalities/jitloader_gdb/TestJITLoaderGDB.py
@@ -1,7 +1,7 @@
 """Test for the JITLoaderGDB interface"""
 
 
-import unittest2
+import unittest
 import os
 import lldb
 from lldbsuite.test import lldbutil
@@ -14,7 +14,7 @@ class JITLoaderGDBTestCase(TestBase):
         lambda: "Skipped because the test crashes the test runner",
         bugnumber="llvm.org/pr24702",
     )
-    @unittest2.expectedFailure  # llvm.org/pr24702
+    @unittest.expectedFailure  # llvm.org/pr24702
     def test_bogus_values(self):
         """Test that we handle inferior misusing the GDB JIT interface"""
         self.build()
diff --git a/lldb/test/API/functionalities/thread/state/TestThreadStates.py b/lldb/test/API/functionalities/thread/state/TestThreadStates.py
index e128ca84977b4..56954c9f34c7e 100644
--- a/lldb/test/API/functionalities/thread/state/TestThreadStates.py
+++ b/lldb/test/API/functionalities/thread/state/TestThreadStates.py
@@ -3,7 +3,7 @@
 """
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -41,14 +41,14 @@ def test_state_after_continue(self):
     @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24660")
     @expectedFailureNetBSD
     # thread states not properly maintained
-    @unittest2.expectedFailure  # llvm.org/pr16712
+    @unittest.expectedFailure  # llvm.org/pr16712
     def test_state_after_expression(self):
         """Test thread state after expression."""
         self.build()
         self.thread_state_after_expression_test()
 
     # thread states not properly maintained
-    @unittest2.expectedFailure  # llvm.org/pr15824 and <rdar://problem/28557237>
+    @unittest.expectedFailure  # llvm.org/pr15824 and <rdar://problem/28557237>
     @expectedFailureAll(
         oslist=["windows"],
         bugnumber="llvm.org/pr24668: Breakpoints not resolved correctly",
diff --git a/lldb/test/API/functionalities/tty/TestTerminal.py b/lldb/test/API/functionalities/tty/TestTerminal.py
index 457abd7b4a89d..750cdb3fc8361 100644
--- a/lldb/test/API/functionalities/tty/TestTerminal.py
+++ b/lldb/test/API/functionalities/tty/TestTerminal.py
@@ -2,7 +2,7 @@
 Test lldb command aliases.
 """
 
-import unittest2
+import unittest
 import os
 import lldb
 from lldbsuite.test.decorators import *
@@ -17,13 +17,13 @@ class LaunchInTerminalTestCase(TestBase):
     @skipUnlessDarwin
     # If the test is being run under sudo, the spawned terminal won't retain that elevated
     # privilege so it can't open the socket to talk back to the test case
-    @unittest2.skipIf(
+    @unittest.skipIf(
         hasattr(os, "geteuid") and os.geteuid() == 0, "test cannot be run as root"
     )
     # Do we need to disable this test if the testsuite is being run on a remote system?
     # This env var is only defined when the shell is running in a local mac
     # terminal window
-    @unittest2.skipUnless(
+    @unittest.skipUnless(
         "TERM_PROGRAM" in os.environ, "test must be run on local system"
     )
     @no_debug_info_test
diff --git a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py
index 235b9b4ce3442..e0994aae76169 100644
--- a/lldb/test/API/lang/c/shared_lib/TestSharedLib.py
+++ b/lldb/test/API/lang/c/shared_lib/TestSharedLib.py
@@ -1,7 +1,7 @@
 """Test that types defined in shared libraries work correctly."""
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -35,7 +35,7 @@ def test_expr_no_preload(self):
         """Test that types work when defined in a shared library and forward-declared in the main executable, but with preloading disabled"""
         self.common_test_expr(False)
 
-    @unittest2.expectedFailure  # llvm.org/PR36712
+    @unittest.expectedFailure  # llvm.org/PR36712
     def test_frame_variable(self):
         """Test that types work when defined in a shared library and forward-declared in the main executable"""
         self.build()
diff --git a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py
index 070bd88d8db94..6971fc0fbc3fd 100644
--- a/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py
+++ b/lldb/test/API/lang/c/shared_lib_stripped_symbols/TestSharedLibStrippedSymbols.py
@@ -1,7 +1,7 @@
 """Test that types defined in shared libraries with stripped symbols work correctly."""
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -28,7 +28,7 @@ def test_expr(self):
         )
 
     @expectedFailureAll(oslist=["windows"])
-    @unittest2.expectedFailure  # llvm.org/PR36712
+    @unittest.expectedFailure  # llvm.org/PR36712
     def test_frame_variable(self):
         """Test that types work when defined in a shared library and forward-declared in the main executable"""
         self.build()
diff --git a/lldb/test/API/lang/cpp/namespace/TestNamespaceLookup.py b/lldb/test/API/lang/cpp/namespace/TestNamespaceLookup.py
index 44cfbd2df5f34..b5e8115160d20 100644
--- a/lldb/test/API/lang/cpp/namespace/TestNamespaceLookup.py
+++ b/lldb/test/API/lang/cpp/namespace/TestNamespaceLookup.py
@@ -3,7 +3,7 @@
 """
 
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -41,7 +41,7 @@ def runToBkpt(self, command):
         )
 
     @skipIfWindows  # This is flakey on Windows: llvm.org/pr38373
-    @unittest2.expectedFailure  # CU-local objects incorrectly scoped
+    @unittest.expectedFailure  # CU-local objects incorrectly scoped
     def test_scope_lookup_with_run_command_globals(self):
         """Test scope lookup of functions in lldb."""
         self.build()
@@ -211,7 +211,7 @@ def test_scope_lookup_with_run_command(self):
         # Evaluate B::func() - should call B::func()
         self.expect_expr("B::func()", result_type="int", result_value="4")
 
-    @unittest2.expectedFailure  # lldb scope lookup of functions bugs
+    @unittest.expectedFailure  # lldb scope lookup of functions bugs
     def test_function_scope_lookup_with_run_command(self):
         """Test scope lookup of functions in lldb."""
         self.build()
@@ -272,7 +272,7 @@ def test_scope_after_using_directive_lookup_with_run_command(self):
         # Evaluate func2() - should call A::func2()
         self.expect_expr("func2()", result_type="int", result_value="3")
 
-    @unittest2.expectedFailure  # lldb scope lookup after using declaration bugs
+    @unittest.expectedFailure  # lldb scope lookup after using declaration bugs
     # NOTE: this test may fail on older systems that don't emit import
     # emtries in DWARF - may need to add checks for compiler versions here.
     def test_scope_after_using_declaration_lookup_with_run_command(self):
@@ -294,7 +294,7 @@ def test_scope_after_using_declaration_lookup_with_run_command(self):
         # Evaluate func() - should call A::func()
         self.expect_expr("func()", result_type="int", result_value="3")
 
-    @unittest2.expectedFailure  # lldb scope lookup ambiguity after using bugs
+    @unittest.expectedFailure  # lldb scope lookup ambiguity after using bugs
     def test_scope_ambiguity_after_using_lookup_with_run_command(self):
         """Test scope lookup ambiguity after using in lldb."""
         self.build()
diff --git a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py
index 3172b5f2fe384..a6e419b7fcdfa 100644
--- a/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py
+++ b/lldb/test/API/lang/cpp/reference-to-outer-type/TestCppReferenceToOuterClass.py
@@ -1,4 +1,4 @@
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -6,7 +6,7 @@
 
 
 class TestCase(TestBase):
-    @unittest2.expectedFailure  # The fix for this was reverted due to llvm.org/PR52257
+    @unittest.expectedFailure  # The fix for this was reverted due to llvm.org/PR52257
     def test(self):
         self.build()
         self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
diff --git a/lldb/test/API/lang/objc/hidden-ivars/TestHiddenIvars.py b/lldb/test/API/lang/objc/hidden-ivars/TestHiddenIvars.py
index 479f4379a5302..f5e4eb1404ac8 100644
--- a/lldb/test/API/lang/objc/hidden-ivars/TestHiddenIvars.py
+++ b/lldb/test/API/lang/objc/hidden-ivars/TestHiddenIvars.py
@@ -3,7 +3,7 @@
 
 import subprocess
 
-import unittest2
+import unittest
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -58,7 +58,7 @@ def test_frame_variable(self):
             self.build()
             self.frame_var(False)
 
-    @unittest2.expectedFailure  # rdar://18683637
+    @unittest.expectedFailure  # rdar://18683637
     def test_frame_variable_across_modules(self):
         if self.getArchitecture() == "i386":
             self.skipTest("requires modern objc runtime")
diff --git a/lldb/test/API/macosx/universal/TestUniversal.py b/lldb/test/API/macosx/universal/TestUniversal.py
index 6e8c112efa096..d988cc0923b27 100644
--- a/lldb/test/API/macosx/universal/TestUniversal.py
+++ b/lldb/test/API/macosx/universal/TestUniversal.py
@@ -1,4 +1,4 @@
-import unittest2
+import unittest
 import os
 import lldb
 from lldbsuite.test.decorators import *
@@ -24,7 +24,7 @@ def setUp(self):
 
     @add_test_categories(["pyapi"])
     @skipUnlessDarwin
-    @unittest2.skipUnless(
+    @unittest.skipUnless(
         hasattr(os, "uname") and os.uname()[4] in ["x86_64"], "requires x86_64"
     )
     @skipIfDarwinEmbedded  # this test file assumes we're targetting an x86 system
@@ -50,7 +50,7 @@ def test_sbdebugger_create_target_with_file_and_target_triple(self):
         self.assertTrue(process, PROCESS_IS_VALID)
 
     @skipUnlessDarwin
-    @unittest2.skipUnless(
+    @unittest.skipUnless(
         hasattr(os, "uname") and os.uname()[4] in ["x86_64"], "requires x86_64"
     )
     @skipIfDarwinEmbedded  # this test file assumes we're targetting an x86 system
@@ -115,7 +115,7 @@ def test_process_launch_for_universal(self):
         self.runCmd("continue")
 
     @skipUnlessDarwin
-    @unittest2.skipUnless(
+    @unittest.skipUnless(
         hasattr(os, "uname") and os.uname()[4] in ["x86_64"], "requires x86_64"
     )
     @skipIfDarwinEmbedded  # this test file assumes we're targetting an x86 system
diff --git a/lldb/test/API/tools/lldb-server/test/test_lldbgdbserverutils.py b/lldb/test/API/tools/lldb-server/test/test_lldbgdbserverutils.py
index 6a6fd020de450..9c9a73cb4e64e 100644
--- a/lldb/test/API/tools/lldb-server/test/test_lldbgdbserverutils.py
+++ b/lldb/test/API/tools/lldb-server/test/test_lldbgdbserverutils.py
@@ -1,10 +1,10 @@
-import unittest2
+import unittest
 import re
 
 from lldbgdbserverutils import *
 
 
-class TestLldbGdbServerUtils(unittest2.TestCase):
+class TestLldbGdbServerUtils(unittest.TestCase):
     def test_entry_exact_payload_match(self):
         entry = GdbRemoteEntry(is_send_to_remote=False, exact_payload="$OK#9a")
         entry.assert_match(self, "$OK#9a")

>From 11eaf8f1fa3d268bb4fd51b09b4c348a9638fff7 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Mon, 29 Jan 2024 19:19:32 -0800
Subject: [PATCH 2/5] Remove bugnumber refs from xfail, update
 assertItemsEqual->assertCountEqual

---
 .../packages/Python/lldbsuite/test/lldbtest.py | 18 +++++-------------
 .../Python/lldbsuite/test/test_result.py       | 14 +++++++-------
 .../TestGdbRemoteLibrariesSvr4Support.py       |  2 +-
 3 files changed, 13 insertions(+), 21 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 2c4446d8aebc5..afe0ce237bd4a 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1090,17 +1090,14 @@ def markFailure(self):
             # Once by the Python unittest framework, and a second time by us.
             print("FAIL", file=sbuf)
 
-    def markExpectedFailure(self, err, bugnumber):
+    def markExpectedFailure(self, err):
         """Callback invoked when an expected failure/error occurred."""
         self.__expected__ = True
         with recording(self, False) as sbuf:
             # False because there's no need to write "expected failure" to the
             # stderr twice.
             # Once by the Python unittest framework, and a second time by us.
-            if bugnumber is None:
-                print("expected failure", file=sbuf)
-            else:
-                print("expected failure (problem id:" + str(bugnumber) + ")", file=sbuf)
+            print("expected failure", file=sbuf)
 
     def markSkippedTest(self):
         """Callback invoked when a test is skipped."""
@@ -1111,19 +1108,14 @@ def markSkippedTest(self):
             # Once by the Python unittest framework, and a second time by us.
             print("skipped test", file=sbuf)
 
-    def markUnexpectedSuccess(self, bugnumber):
+    def markUnexpectedSuccess(self):
         """Callback invoked when an unexpected success occurred."""
         self.__unexpected__ = True
         with recording(self, False) as sbuf:
             # False because there's no need to write "unexpected success" to the
             # stderr twice.
             # Once by the Python unittest framework, and a second time by us.
-            if bugnumber is None:
-                print("unexpected success", file=sbuf)
-            else:
-                print(
-                    "unexpected success (problem id:" + str(bugnumber) + ")", file=sbuf
-                )
+            print("unexpected success", file=sbuf)
 
     def getRerunArgs(self):
         return " -f %s.%s" % (self.__class__.__name__, self._testMethodName)
@@ -2226,7 +2218,7 @@ def completions_match(self, command, completions):
         match_strings = lldb.SBStringList()
         interp.HandleCompletion(command, len(command), 0, -1, match_strings)
         # match_strings is a 1-indexed list, so we have to slice...
-        self.assertItemsEqual(
+        self.assertCountEqual(
             completions, list(match_strings)[1:], "List of returned completion is wrong"
         )
 
diff --git a/lldb/packages/Python/lldbsuite/test/test_result.py b/lldb/packages/Python/lldbsuite/test/test_result.py
index 2690906232b97..20365f53a6754 100644
--- a/lldb/packages/Python/lldbsuite/test/test_result.py
+++ b/lldb/packages/Python/lldbsuite/test/test_result.py
@@ -243,7 +243,7 @@ def addFailure(self, test, err):
         if self.checkExclusion(
             configuration.xfail_tests, test.id()
         ) or self.checkCategoryExclusion(configuration.xfail_categories, test):
-            self.addExpectedFailure(test, err, None)
+            self.addExpectedFailure(test, err)
             return
 
         configuration.sdir_has_content = True
@@ -264,12 +264,12 @@ def addFailure(self, test, err):
                 else:
                     configuration.failures_per_category[category] = 1
 
-    def addExpectedFailure(self, test, err, bugnumber):
+    def addExpectedFailure(self, test, err):
         configuration.sdir_has_content = True
-        super(LLDBTestResult, self).addExpectedFailure(test, err, bugnumber)
+        super(LLDBTestResult, self).addExpectedFailure(test, err)
         method = getattr(test, "markExpectedFailure", None)
         if method:
-            method(err, bugnumber)
+            method(err)
         self.stream.write(
             "XFAIL: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))
         )
@@ -285,12 +285,12 @@ def addSkip(self, test, reason):
             % (self._config_string(test), str(test), reason)
         )
 
-    def addUnexpectedSuccess(self, test, bugnumber):
+    def addUnexpectedSuccess(self, test):
         configuration.sdir_has_content = True
-        super(LLDBTestResult, self).addUnexpectedSuccess(test, bugnumber)
+        super(LLDBTestResult, self).addUnexpectedSuccess(test)
         method = getattr(test, "markUnexpectedSuccess", None)
         if method:
-            method(bugnumber)
+            method()
         self.stream.write(
             "XPASS: LLDB (%s) :: %s\n" % (self._config_string(test), str(test))
         )
diff --git a/lldb/test/API/tools/lldb-server/libraries-svr4/TestGdbRemoteLibrariesSvr4Support.py b/lldb/test/API/tools/lldb-server/libraries-svr4/TestGdbRemoteLibrariesSvr4Support.py
index ac65d2d4c660f..846adade34402 100644
--- a/lldb/test/API/tools/lldb-server/libraries-svr4/TestGdbRemoteLibrariesSvr4Support.py
+++ b/lldb/test/API/tools/lldb-server/libraries-svr4/TestGdbRemoteLibrariesSvr4Support.py
@@ -72,7 +72,7 @@ def libraries_svr4_well_formed(self):
         self.assertEqual(xml_root.tag, "library-list-svr4")
         for child in xml_root:
             self.assertEqual(child.tag, "library")
-            self.assertItemsEqual(child.attrib.keys(), ["name", "lm", "l_addr", "l_ld"])
+            self.assertCountEqual(child.attrib.keys(), ["name", "lm", "l_addr", "l_ld"])
 
     def libraries_svr4_has_correct_load_addr(self):
         xml_root = self.get_libraries_svr4_xml()

>From 5354a57e2dc8d8f79bfbd6d97c462ad334f175ac Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Mon, 29 Jan 2024 20:26:26 -0800
Subject: [PATCH 3/5] Parse test runner output for lit integration

---
 lldb/test/API/lldbtest.py | 43 +++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/lldb/test/API/lldbtest.py b/lldb/test/API/lldbtest.py
index 2b44bb7f6f9f4..3b8f2a61822c8 100644
--- a/lldb/test/API/lldbtest.py
+++ b/lldb/test/API/lldbtest.py
@@ -86,20 +86,45 @@ def execute(self, test, litConfig):
         if timeoutInfo:
             return lit.Test.TIMEOUT, output
 
-        # Parse the dotest output from stderr.
-        result_regex = r"\((\d+) passes, (\d+) failures, (\d+) errors, (\d+) skipped, (\d+) expected failures, (\d+) unexpected successes\)"
-        results = re.search(result_regex, err)
+        # Parse the dotest output from stderr. First get the # of total tests, in order to infer the # of passes.
+        # Example: "Ran 5 tests in 0.042s"
+        num_ran_regex = r"^Ran (\d+) tests? in "
+        num_ran_results = re.search(num_ran_regex, err, re.MULTILINE)
+
+        # If parsing fails mark this test as unresolved.
+        if not num_ran_results:
+            return lit.Test.UNRESOLVED, output
+        num_ran = int(num_ran_results.group(1))
+
+        # Then look for a detailed summary, which is OK or FAILED followed by optional details.
+        # Example: "OK (skipped=1, expected failures=1)"
+        # Example: "FAILED (failures=3)"
+        # Example: "OK"
+        result_regex = r"^(?:OK|FAILED)(?: \((.*)\))?$"
+        results = re.search(result_regex, err, re.MULTILINE)
 
         # If parsing fails mark this test as unresolved.
         if not results:
             return lit.Test.UNRESOLVED, output
 
-        passes = int(results.group(1))
-        failures = int(results.group(2))
-        errors = int(results.group(3))
-        skipped = int(results.group(4))
-        expected_failures = int(results.group(5))
-        unexpected_successes = int(results.group(6))
+        details = results.group(1)
+        parsed_details = {}
+        if details:
+            for detail in details.split(", "):
+                detail_parts = detail.split("=")
+                if len(detail_parts) != 2:
+                    return lit.Test.UNRESOLVED, output
+                parsed_details[detail_parts[0]] = int(detail_parts[1])
+
+        failures = parsed_details.get("failures", 0)
+        errors = parsed_details.get("errors", 0)
+        skipped = parsed_details.get("skipped", 0)
+        expected_failures = parsed_details.get("expected failures", 0)
+        unexpected_successes = parsed_details.get("unexpected successes", 0)
+        non_pass = (
+            failures + errors + skipped + expected_failures + unexpected_successes
+        )
+        passes = num_ran - non_pass
 
         if exitCode:
             # Mark this test as FAIL if at least one test failed.

>From ca32f6a9eebace25f0b5d2ba22fb3823e023f452 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Mon, 29 Jan 2024 20:31:24 -0800
Subject: [PATCH 4/5] Remove one more xfail reason usage

---
 lldb/packages/Python/lldbsuite/test/lldbtest.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index afe0ce237bd4a..018f2a06980a8 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1696,9 +1696,7 @@ def test_method(self, attrvalue=attrvalue):
 
                     xfail_reason = xfail_for_debug_info_cat_fn(cat)
                     if xfail_reason:
-                        test_method = unittest.expectedFailure(xfail_reason)(
-                            test_method
-                        )
+                        test_method = unittest.expectedFailure(test_method)
 
                     skip_reason = skip_for_debug_info_cat_fn(cat)
                     if skip_reason:

>From edd3cfa6559acb37331945e45887d2d5069d5179 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Mon, 29 Jan 2024 20:38:06 -0800
Subject: [PATCH 5/5] Clear module in TestThreadLocal to prevent failure during
 teardown

---
 lldb/test/API/lang/cpp/thread_local/TestThreadLocal.py | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lldb/test/API/lang/cpp/thread_local/TestThreadLocal.py b/lldb/test/API/lang/cpp/thread_local/TestThreadLocal.py
index 9b128ba6097ac..0b63e15e876d6 100644
--- a/lldb/test/API/lang/cpp/thread_local/TestThreadLocal.py
+++ b/lldb/test/API/lang/cpp/thread_local/TestThreadLocal.py
@@ -39,6 +39,11 @@ def test_thread_local(self):
         process.Kill()
         lldbutil.run_to_breakpoint_do_run(self, target, main_bkpt)
 
+        # The test fails during tear down because the module isn't cleared.
+        # Even though this test case is marked as xfail, a failure during
+        # tear down still counts as an error.
+        main_module.Clear()
+
         self.expect(
             "expr tl_local_int",
             error=True,



More information about the lldb-commits mailing list