[lldb-dap] Migrate DAP launch tests (#207023)

Migrate all the tests in `lldb-dap/launch` base directory only. Fixes
any grammar if needed.
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_args.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_args.py
index 6774f05..98091e6 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_args.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_args.py
@@ -2,11 +2,11 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import expectedFailureWindows
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_args(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_args(DAPTestCaseBase):
     """
     Tests launch of a simple program with arguments
     """
@@ -14,20 +14,21 @@
     def test(self):
         program = self.getBuildArtifact("a.out")
         args = ["one", "with space", "'with single quotes'", '"with double quotes"']
-        self.build_and_launch(program, args=args)
-        self.continue_to_exit()
+        session = self.build_and_create_session()
+        session.launch(LaunchArgs(program=program, args=args))
+        session.verify_process_exited()
 
-        # Now get the STDOUT and verify our arguments got passed correctly
-        output = self.get_stdout()
+        output = session.get_stdout()
         self.assertTrue(output and len(output) > 0, "expect program output")
         lines = output.splitlines()
-        # Skip the first argument that contains the program name
+        # Skip the first argument that contains the program name.
         lines.pop(0)
-        # Make sure arguments we specified are correct
-        for i, arg in enumerate(args):
-            quoted_arg = '"%s"' % (arg)
+        # Make sure arguments we specified are correct.
+        args_and_lines = zip(args, lines)
+        for i, (arg, line) in enumerate(args_and_lines, start=1):
+            quoted_arg = f'"{arg}"'
             self.assertIn(
                 quoted_arg,
-                lines[i],
-                'arg[%i] "%s" not in "%s"' % (i + 1, quoted_arg, lines[i]),
+                line,
+                f'arg[{i}] "{arg}" not in {line!r}',
             )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_commands.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_commands.py
index 48398e5..21a3f61 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_commands.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_commands.py
@@ -4,10 +4,11 @@
 
 from lldbsuite.test.decorators import skipIf
 from lldbsuite.test.lldbtest import line_number
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_commands(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_commands(DAPTestCaseBase):
     """
     Tests the "initCommands", "preRunCommands", "stopCommands",
     "terminateCommands" and "exitCommands" that can be passed during
@@ -34,7 +35,9 @@
         stopCommands = ["frame variable", "bt"]
         exitCommands = ["expr 2+3", "expr 3+4"]
         terminateCommands = ["expr 4+2"]
-        self.build_and_launch(
+        session = self.build_and_create_session()
+
+        launch_args = LaunchArgs(
             program,
             initCommands=initCommands,
             preRunCommands=preRunCommands,
@@ -43,49 +46,52 @@
             exitCommands=exitCommands,
             terminateCommands=terminateCommands,
         )
-        self.dap_server.wait_for_initialized()
+        with session.configure(launch_args) as ctx:
+            # Get output from the console. This should contain the
+            # "initCommands", "preRunCommands", and "postRunCommands".
+            coutput = session.collect_console(
+                after=ctx.init_response, until=postRunCommands[-1]
+            )
+            output = coutput.seen_texts
+            session.verify_commands("initCommands", output, initCommands)
+            session.verify_commands("preRunCommands", output, preRunCommands)
+            session.verify_commands("postRunCommands", output, postRunCommands)
 
-        # Get output from the console. This should contain both the
-        # "initCommands" and the "preRunCommands".
-        output = self.collect_console(pattern=postRunCommands[-1])
-        # Verify all "initCommands" were found in console output
-        self.verify_commands("initCommands", output, initCommands)
-        # Verify all "preRunCommands" were found in console output
-        self.verify_commands("preRunCommands", output, preRunCommands)
-        # Verify all "postRunCommands" were found in console output
-        self.verify_commands("postRunCommands", output, postRunCommands)
+            source = "main.c"
+            first_line = line_number(source, "// breakpoint 1")
+            second_line = line_number(source, "// breakpoint 2")
+            lines = [first_line, second_line]
 
-        source = "main.c"
-        first_line = line_number(source, "// breakpoint 1")
-        second_line = line_number(source, "// breakpoint 2")
-        lines = [first_line, second_line]
+            # Set 2 breakpoints so we can verify that "stopCommands" get run as the
+            # breakpoints get hit.
+            [first_bp, second_bp] = session.resolve_source_breakpoints(source, lines)
 
-        # Set 2 breakpoints so we can verify that "stopCommands" get run as the
-        # breakpoints get hit
-        breakpoint_ids = self.set_source_breakpoints(source, lines)
-        self.assertEqual(
-            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
-        )
+        launch_response = ctx.launch_or_attach_response
 
         # Continue after launch and hit the first breakpoint.
-        # Get output from the console. This should contain both the
-        # "stopCommands" that were run after the first breakpoint was hit
-        self.continue_to_breakpoints(breakpoint_ids)
-        output = self.collect_console(pattern=stopCommands[-1])
-        self.verify_commands("stopCommands", output, stopCommands)
+        # Get output from the console. This should contain the
+        # "stopCommands" that were run after the first breakpoint was hit.
+        session.verify_stopped_on_breakpoint(first_bp, after=ctx.process_event)
+        coutput = session.collect_console(after=launch_response, until=stopCommands[-1])
+        output = coutput.seen_texts
+        session.verify_commands("stopCommands", output, stopCommands)
 
         # Continue again and hit the second breakpoint.
-        # Get output from the console. This should contain both the
-        # "stopCommands" that were run after the second breakpoint was hit
-        self.continue_to_breakpoints(breakpoint_ids)
-        output = self.collect_console(pattern=stopCommands[-1])
-        self.verify_commands("stopCommands", output, stopCommands)
+        # Get output from the console. This should contain the
+        # "stopCommands" that were run after the second breakpoint was hit.
+        session.continue_to_breakpoint(second_bp)
+        coutput = session.collect_console(after=coutput.event, until=stopCommands[-1])
+        output = coutput.seen_texts
+        session.verify_commands("stopCommands", output, stopCommands)
 
-        # Continue until the program exits
-        self.continue_to_exit()
-        # Get output from the console. This should contain both the
-        # "exitCommands" that were run after the second breakpoint was hit
-        # and the "terminateCommands" due to the debugging session ending
-        output = self.collect_console(pattern=terminateCommands[0])
-        self.verify_commands("exitCommands", output, exitCommands)
-        self.verify_commands("terminateCommands", output, terminateCommands)
+        # Continue until the program exits.
+        # Get output from the console. This should contain the
+        # "exitCommands" run on process exit and the "terminateCommands"
+        # run when the debugging session ends.
+        session.continue_to_exit()
+        coutput = session.collect_console(
+            after=coutput.event, until=terminateCommands[0]
+        )
+        output = coutput.seen_texts
+        session.verify_commands("exitCommands", output, exitCommands)
+        session.verify_commands("terminateCommands", output, terminateCommands)
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_cwd.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_cwd.py
index c2b2b986..a7e4ffa 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_cwd.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_cwd.py
@@ -2,35 +2,37 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import skipIfWindows
-import lldbdap_testcase
 import os
 
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_cwd(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_cwd(DAPTestCaseBase):
     """
     Tests the default launch of a simple program with a current working
     directory.
     """
 
-    @skipIfWindows
     def test(self):
         program = self.getBuildArtifact("a.out")
         program_parent_dir = os.path.realpath(os.path.dirname(os.path.dirname(program)))
-        self.build_and_launch(program, cwd=program_parent_dir)
-        self.continue_to_exit()
-        # Now get the STDOUT and verify our program argument is correct
-        output = self.get_stdout()
+        session = self.build_and_create_session()
+        session.launch(LaunchArgs(program=program, cwd=program_parent_dir))
+        session.verify_process_exited()
+
+        # Now get the STDOUT and verify our program's working directory is correct
+        output = session.get_stdout()
         self.assertTrue(output and len(output) > 0, "expect program output")
+
         lines = output.splitlines()
-        found = False
-        for line in lines:
-            if line.startswith('cwd = "'):
-                quote_path = '"%s"' % (program_parent_dir)
-                found = True
-                self.assertIn(
-                    quote_path,
-                    line,
-                    "working directory '%s' not in '%s'" % (program_parent_dir, line),
-                )
-        self.assertTrue(found, "verified program working directory")
+        cwd_lines = [line for line in lines if line.startswith('cwd = "')]
+        self.assertEqual(len(cwd_lines), 1, "verified program working directory")
+        cwd_line = cwd_lines[0]
+
+        quote_path = f'"{program_parent_dir}"'
+        self.assertIn(
+            quote_path,
+            cwd_line,
+            f"working directory '{program_parent_dir}' not in '{cwd_line}'",
+        )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_debuggerRoot.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_debuggerRoot.py
index deeab23..6bf470e 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_debuggerRoot.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_debuggerRoot.py
@@ -2,13 +2,14 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import expectedFailureWindows
-from lldbsuite.test import lldbplatformutil
-import lldbdap_testcase
 import os
 
+from lldbsuite.test import lldbplatformutil
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_debuggerRoot(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_debuggerRoot(DAPTestCaseBase):
     """
     Tests the "debuggerRoot" will change the working directory of
     the lldb-dap debug adapter.
@@ -19,24 +20,29 @@
         program_parent_dir = os.path.realpath(os.path.dirname(os.path.dirname(program)))
 
         var = "%cd%" if lldbplatformutil.getHostPlatform() == "windows" else "$PWD"
-        commands = [f"platform shell echo cwd = {var}"]
+        init_commands = [f"platform shell echo cwd = {var}"]
 
-        self.build_and_launch(
-            program, debuggerRoot=program_parent_dir, initCommands=commands
+        session = self.build_and_create_session()
+        process_event = session.launch(
+            LaunchArgs(
+                program=program,
+                debuggerRoot=program_parent_dir,
+                initCommands=init_commands,
+            )
         )
-        self.continue_to_exit()
-        output = self.get_console()
+        session.verify_process_exited(after=process_event)
+
+        output = session.get_console()
         self.assertTrue(output and len(output) > 0, "expect console output")
-        lines = output.splitlines()
+
         prefix = "cwd = "
-        found = False
-        for line in lines:
-            if line.startswith(prefix):
-                found = True
-                self.assertEqual(
-                    program_parent_dir,
-                    line.strip()[len(prefix) :],
-                    "lldb-dap working dir '%s' == '%s'"
-                    % (program_parent_dir, line[len(prefix) :]),
-                )
-        self.assertTrue(found, "verified lldb-dap working directory")
+        cwd_lines = [line for line in output.splitlines() if line.startswith(prefix)]
+        self.assertEqual(
+            len(cwd_lines), 1, "expected exactly one cwd line in console output"
+        )
+        self.assertEqual(
+            cwd_lines[0].strip()[len(prefix) :],
+            program_parent_dir,
+            f"lldb-dap working dir mismatch: expected '{program_parent_dir}', "
+            f"got '{cwd_lines[0]}'",
+        )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_disableSTDIO.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_disableSTDIO.py
index 0a780f5..39d350f 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_disableSTDIO.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_disableSTDIO.py
@@ -3,10 +3,11 @@
 """
 
 from lldbsuite.test.decorators import skipIfWindows
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_disableSTDIO(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_disableSTDIO(DAPTestCaseBase):
     """
     Tests the default launch of a simple program with STDIO disabled.
     """
@@ -14,8 +15,10 @@
     @skipIfWindows
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.build_and_launch(program, disableSTDIO=True)
-        self.continue_to_exit()
-        # Now get the STDOUT and verify our program argument is correct
-        output = self.get_stdout()
+        session = self.build_and_create_session()
+        session.launch(LaunchArgs(program=program, disableSTDIO=True))
+        session.verify_process_exited()
+
+        # Now get the STDOUT and verify our program argument is correct.
+        output = session.get_stdout()
         self.assertEqual(output, "", "expect no program output")
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_array.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_array.py
index 1595f4a..2e96a11 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_array.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_array.py
@@ -3,10 +3,11 @@
 """
 
 from lldbsuite.test.decorators import skipIfWindows
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_environment_with_array(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_environment_with_array(DAPTestCaseBase):
     """
     Tests launch of a simple program with environment variables
     """
@@ -14,26 +15,28 @@
     @skipIfWindows
     def test(self):
         program = self.getBuildArtifact("a.out")
-        env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", "SPACE=Hello World"]
+        expected_env = [
+            "NO_VALUE",
+            "WITH_VALUE=BAR",
+            "EMPTY_VALUE=",
+            "SPACE=Hello World",
+        ]
 
-        self.build_and_launch(program, env=env)
-        self.continue_to_exit()
+        session = self.build_and_create_session()
+        process_event = session.launch(LaunchArgs(program=program, env=expected_env))
+        session.verify_process_exited(after=process_event)
 
-        # Now get the STDOUT and verify our arguments got passed correctly
-        output = self.get_stdout()
-        self.assertTrue(output and len(output) > 0, "expect program output")
-        lines = output.splitlines()
-        # Skip the all arguments so we have only environment vars left
-        while len(lines) and lines[0].startswith("arg["):
-            lines.pop(0)
-        # Make sure each environment variable in "env" is actually set in the
-        # program environment that was printed to STDOUT
-        for var in env:
-            found = False
-            for program_var in lines:
-                if var in program_var:
-                    found = True
-                    break
-            self.assertTrue(
-                found, '"%s" must exist in program environment (%s)' % (var, lines)
+        # Now get the STDOUT and verify our arguments got passed correctly.
+        output = session.get_stdout()
+        self.assertTrue(output, "expect program output")
+
+        # Collect environment lines.
+        env_output = "\n".join(l for l in output.splitlines() if l.startswith("env["))
+        # Make sure each environment variable in "expected_env" is actually set in the
+        # program environment and contains the right value.
+        for expected_str in expected_env:
+            self.assertIn(
+                expected_str,
+                env_output,
+                f"\n{expected_str} must exist in program's environment \n{env_output}",
             )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_object.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_object.py
index 8c7994e..a853bbc 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_object.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_environment_with_object.py
@@ -2,41 +2,43 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import expectedFailureWindows
-import lldbdap_testcase
+from lldbsuite.test.decorators import skipIfWindows
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
-    def test_environment_with_object(self):
-        """
-        Tests launch of a simple program with environment variables
-        """
+class TestDAP_launch_environment_with_object(DAPTestCaseBase):
+    """
+    Tests launch of a simple program with environment variables
+    """
+
+    @skipIfWindows
+    def test(self):
         program = self.getBuildArtifact("a.out")
-        env = {
-            "NO_VALUE": "",
+        expected_env = {
+            "NO_VALUE": None,
             "WITH_VALUE": "BAR",
             "EMPTY_VALUE": "",
             "SPACE": "Hello World",
         }
 
-        self.build_and_launch(program, env=env)
-        self.continue_to_exit()
+        session = self.build_and_create_session()
+        process_event = session.launch(LaunchArgs(program=program, env=expected_env))
+        session.verify_process_exited(after=process_event)
 
-        # Now get the STDOUT and verify our arguments got passed correctly
-        output = self.get_stdout()
-        self.assertTrue(output and len(output) > 0, "expect program output")
-        lines = output.splitlines()
-        # Skip the all arguments so we have only environment vars left
-        while len(lines) and lines[0].startswith("arg["):
-            lines.pop(0)
-        # Make sure each environment variable in "env" is actually set in the
-        # program environment that was printed to STDOUT
-        for var in env:
-            found = False
-            for program_var in lines:
-                if var in program_var:
-                    found = True
-                    break
-            self.assertTrue(
-                found, '"%s" must exist in program environment (%s)' % (var, lines)
+        # Now get the STDOUT and verify our arguments got passed correctly.
+        output = session.get_stdout()
+        self.assertTrue(output, "expect program output")
+
+        # Collect environment lines.
+        env_output = "\n".join(l for l in output.splitlines() if l.startswith("env["))
+        # Make sure each environment variable in "expected_env" is actually set in the
+        # program environment and contains the right value.
+        for variable, value in expected_env.items():
+            expected_value = value or ""
+            expected_str = f'"{variable}={expected_value}"'
+            self.assertIn(
+                expected_str,
+                env_output,
+                f"\n{expected_str} must exist in program's environment \n{env_output}",
             )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_extra_launch_commands.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_extra_launch_commands.py
index ad48af6..92c481a 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_extra_launch_commands.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_extra_launch_commands.py
@@ -4,10 +4,11 @@
 
 from lldbsuite.test.decorators import skipIf
 from lldbsuite.test.lldbtest import line_number
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_extra_launch_commands(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_extra_launch_commands(DAPTestCaseBase):
     """
     Tests the "launchCommands" with extra launching settings
     """
@@ -15,18 +16,17 @@
     # Flakey on 32-bit Arm Linux.
     @skipIf(oslist=["linux"], archs=["arm$"])
     def test(self):
-        self.build_and_create_debug_adapter()
         program = self.getBuildArtifact("a.out")
-
-        source = "main.c"
+        session = self.build_and_create_session()
+        source = self.getSourcePath("main.c")
         first_line = line_number(source, "// breakpoint 1")
         second_line = line_number(source, "// breakpoint 2")
-        # Set target binary and 2 breakpoints
-        # then we can verify the "launchCommands" get run
-        # also we can verify that "stopCommands" get run as the
-        # breakpoints get hit
+
+        # Set target binary and 2 breakpoints, so we can verify the
+        # "launchCommands" get run, and verify "stopCommands" get run
+        # as the breakpoints get hit.
         launchCommands = [
-            'target create "%s"' % (program),
+            f'target create "{program}"',
             "process launch --stop-at-entry",
         ]
         initCommands = ["target list", "platform list"]
@@ -34,51 +34,48 @@
         postRunCommands = ['script print("hello world")']
         stopCommands = ["frame variable", "bt"]
         exitCommands = ["expr 2+3", "expr 3+4"]
-        self.launch(
-            program,
-            initCommands=initCommands,
-            preRunCommands=preRunCommands,
-            postRunCommands=postRunCommands,
-            stopCommands=stopCommands,
-            exitCommands=exitCommands,
-            launchCommands=launchCommands,
-        )
-        self.set_source_breakpoints("main.c", [first_line, second_line])
 
-        # Get output from the console. This should contain both the
-        # "initCommands" and the "preRunCommands".
-        output = self.get_console()
-        # Verify all "initCommands" were found in console output
-        self.verify_commands("initCommands", output, initCommands)
-        # Verify all "preRunCommands" were found in console output
-        self.verify_commands("preRunCommands", output, preRunCommands)
+        with session.configure(
+            LaunchArgs(
+                program=program,
+                launchCommands=launchCommands,
+                initCommands=initCommands,
+                preRunCommands=preRunCommands,
+                postRunCommands=postRunCommands,
+                stopCommands=stopCommands,
+                exitCommands=exitCommands,
+            )
+        ) as ctx:
+            session.resolve_source_breakpoints(source, [first_line, second_line])
+        process_event = ctx.process_event
+        # The launchCommands stop the process at entry, but lldb-dap auto-continues
+        # after configurationDone (since stopOnEntry isn't set), so the first
+        # observable stop is hitting the first breakpoint, not entry.
+        first_stop = session.verify_stopped_on_breakpoint(after=process_event)
 
-        # Verify all "launchCommands" were found in console output
-        # After execution, program should launch
-        self.verify_commands("launchCommands", output, launchCommands)
-        self.verify_commands("postRunCommands", output, postRunCommands)
+        # Get output from the console. This should contain the
+        # "initCommands", "preRunCommands", "launchCommands", and "postRunCommands".
+        output = session.get_console()
+        session.verify_commands("initCommands", output, initCommands)
+        session.verify_commands("preRunCommands", output, preRunCommands)
+        session.verify_commands("launchCommands", output, launchCommands)
+        session.verify_commands("postRunCommands", output, postRunCommands)
 
-        # Finish configuration and continue target
-        self.verify_configuration_done()
+        # Check that we got module events from target.
+        session.wait_for_module_event(after=ctx.init_response)
 
-        # Check that we got module events from target
-        modules = self.dap_server.wait_for_module_events()
-        self.assertGreater(len(modules), 0)
+        # Verify the "stopCommands" ran on the first breakpoint hit.
+        # Wait until the last stopCommand's output arrives to avoid racing it.
+        output = session.collect_console(after=first_stop, until=stopCommands[-1])
+        session.verify_commands("stopCommands", output.seen_texts, stopCommands)
 
-        # Verify the "stopCommands" here
-        output = self.get_console()
-        self.verify_commands("stopCommands", output, stopCommands)
+        # Continue and hit the second breakpoint, then verify "stopCommands"
+        # ran again.
+        session.continue_to_next_stop()
+        output = session.collect_console(after=output.event, until=stopCommands[-1])
+        session.verify_commands("stopCommands", output.seen_texts, stopCommands)
 
-        # Continue and hit the second breakpoint.
-        # Get output from the console. This should contain both the
-        # "stopCommands" that were run after the first breakpoint was hit
-        self.continue_to_next_stop()
-        output = self.get_console()
-        self.verify_commands("stopCommands", output, stopCommands)
-
-        # Continue until the program exits
-        self.continue_to_exit()
-        # Get output from the console. This should contain both the
-        # "exitCommands" that were run after the second breakpoint was hit
-        output = self.get_console()
-        self.verify_commands("exitCommands", output, exitCommands)
+        # Continue until the program exits, then verify "exitCommands" ran.
+        session.continue_to_exit()
+        output = session.collect_console(after=output.event, until=exitCommands[-1])
+        session.verify_commands("exitCommands", output.seen_texts, exitCommands)
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_console.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_console.py
index c59a5fe..c4dc50a 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_console.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_console.py
@@ -2,22 +2,31 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_failing_console(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_failing_console(DAPTestCaseBase):
     """
     Tests launching in console with an invalid terminal type.
     """
 
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.create_debug_adapter()
-        launch_seq = self.launch(program, console="invalid")
-        response = self.dap_server.receive_response(launch_seq)
-        self.assertFalse(response["success"])
-        self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))
+        # No build needed: the launch request should be rejected during arg
+        # validation, before lldb-dap touches the program path.
+        session = self.create_session()
+        session.initialize_sequence(session.initialize_args)
+
+        err_response = session.send_request(
+            LaunchArgs(program=program, console="invalid")
+        ).error()
+        error_msg = self.expect_not_none(
+            err_response.body and err_response.body.error,
+            "expected an error message in the launch response",
+        )
+        self.assertTrue(error_msg.showUser, "expected showUser=true")
         self.assertRegex(
-            response["body"]["error"]["format"],
-            r"unexpected value, expected 'internalConsole\', 'integratedTerminal\' or 'externalTerminal\' at arguments.console",
+            error_msg.format,
+            r"unexpected value, expected 'internalConsole', 'integratedTerminal' or 'externalTerminal' at arguments.console",
         )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_launch_commands.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_launch_commands.py
index ad48fb9..13730ed 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_launch_commands.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_failing_launch_commands.py
@@ -2,50 +2,55 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-import lldbdap_testcase
 import os
 import re
 
+from lldbsuite.test.tools.lldb_dap.dap_types import InitializedEvent, LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_failing_launch_commands(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_failing_launch_commands(DAPTestCaseBase):
     """
     Tests "launchCommands" failures prevents a launch.
     """
 
     def test(self):
-        self.build_and_create_debug_adapter()
+        session = self.build_and_create_session()
         program = self.getBuildArtifact("a.out")
+        init_response = session.initialize_sequence(session.initialize_args)
 
         # Run an invalid launch command, in this case a bad path.
         bad_path = os.path.join("bad", "path")
-        launchCommands = ['!target create "%s%s"' % (bad_path, program)]
-
+        launchCommands = [f'!target create "{bad_path}{program}"']
         initCommands = ["target list", "platform list"]
         preRunCommands = ["image list a.out", "image dump sections a.out"]
-        response = self.launch_and_configurationDone(
-            program,
-            initCommands=initCommands,
-            preRunCommands=preRunCommands,
-            launchCommands=launchCommands,
-        )
 
-        self.assertFalse(response["success"])
+        launch_handle = session.send_request(
+            LaunchArgs(
+                program=program,
+                initCommands=initCommands,
+                preRunCommands=preRunCommands,
+                launchCommands=launchCommands,
+            )
+        )
+        session.wait_for_event(InitializedEvent, after=init_response)
+        session.configuration_done().result_or_error()
+        err_response = launch_handle.error()
+
+        error_msg = self.expect_not_none(
+            err_response.body and err_response.body.error,
+            "expected an error message in the launch response",
+        )
         self.assertRegex(
-            response["body"]["error"]["format"],
+            error_msg.format,
             r"Failed to run launch commands\. See the Debug Console for more details",
         )
 
-        # Get output from the console. This should contain both the
-        # "initCommands" and the "preRunCommands".
-        output = self.get_console()
-        # Verify all "initCommands" were found in console output
-        self.verify_commands("initCommands", output, initCommands)
-        # Verify all "preRunCommands" were found in console output
-        self.verify_commands("preRunCommands", output, preRunCommands)
-
-        # Verify all "launchCommands" were founc in console output
+        # Get output from the console. This should contain the
+        # "initCommands", "preRunCommands", and "launchCommands".
+        output = session.get_console()
+        session.verify_commands("initCommands", output, initCommands)
+        session.verify_commands("preRunCommands", output, preRunCommands)
+        session.verify_commands("launchCommands", output, launchCommands)
         # The launch should fail due to the invalid command.
-        self.verify_commands("launchCommands", output, launchCommands)
         self.assertRegex(output, re.escape(bad_path) + r".*does not exist")
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_launch_commands_and_console.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_launch_commands_and_console.py
index 14d7dbe..dfd28e6 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_launch_commands_and_console.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_launch_commands_and_console.py
@@ -2,26 +2,35 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import Console, LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_failing_launch_commands_and_console(
-    lldbdap_testcase.DAPTestCaseBase
-):
+class TestDAP_launch_invalid_launch_commands_and_console(DAPTestCaseBase):
     """
     Tests launching with launch commands in an integrated terminal.
     """
 
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.create_debug_adapter()
-        launch_seq = self.launch(
-            program, launchCommands=["a b c"], console="integratedTerminal"
+        # No build needed: the launch request should be rejected during
+        # argument validation, before lldb-dap touches the program path.
+        session = self.create_session()
+        session.initialize_sequence(session.initialize_args)
+
+        err_response = session.send_request(
+            LaunchArgs(
+                program=program,
+                launchCommands=["a b c"],
+                console=Console.INTEGRATED_TERMINAL,
+            )
+        ).error()
+        error_msg = self.expect_not_none(
+            err_response.body and err_response.body.error,
+            "expected an error message in the launch response",
         )
-        response = self.dap_server.receive_response(launch_seq)
-        self.assertFalse(response["success"])
-        self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))
+        self.assertTrue(error_msg.showUser, "expected showUser=true")
         self.assertIn(
             "'launchCommands' and non-internal 'console' are mutually exclusive",
-            self.get_dict_value(response, ["body", "error", "format"]),
+            error_msg.format,
         )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_program.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_program.py
index d70426f..6074659 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_program.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_invalid_program.py
@@ -2,22 +2,26 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_invalid_program(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_invalid_program(DAPTestCaseBase):
     """
     Tests launching with an invalid program.
     """
 
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.create_debug_adapter()
-        launch_seq = self.launch(program)
-        self.dap_server.wait_for_initialized()
-        self.dap_server.request_configurationDone()
-        response = self.dap_server.receive_response(launch_seq)
-        self.assertFalse(response["success"])
-        self.assertEqual(
-            "'{0}' does not exist".format(program), response["body"]["error"]["format"]
+        session = self.create_session()
+        session.initialize_sequence(session.initialize_args)
+        launch_handle = session.send_request(LaunchArgs(program=program))
+        session.ensure_initialized()
+        session.verify_configuration_done(expected_success=False)
+
+        err_response = launch_handle.error()
+        error_msg = self.expect_not_none(
+            err_response.body and err_response.body.error,
+            "expected an error message in the launch response",
         )
+        self.assertEqual(error_msg.format, f"'{program}' does not exist")
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_no_lldbinit_flag.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_no_lldbinit_flag.py
index 8a48075..22cfcf3 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_no_lldbinit_flag.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_no_lldbinit_flag.py
@@ -2,16 +2,21 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
 import os
 import tempfile
 
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
+from lldbsuite.test.tools.lldb_dap.utils import DebugAdapterOptions
 
-class TestDAP_launch_no_lldbinit_flag(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_no_lldbinit_flag(DAPTestCaseBase):
     """
     Test that the --no-lldbinit flag prevents sourcing .lldbinit files.
     """
 
+    USE_DEFAULT_DEBUG_ADAPTER = False
+
     def test(self):
         # Create a temporary .lldbinit file in the home directory
         with tempfile.TemporaryDirectory() as temp_home:
@@ -22,29 +27,35 @@
                 f.write("settings set stop-disassembly-display never\n")
                 f.write("settings set target.x86-disassembly-flavor intel\n")
 
-            # Test with --no-lldbinit flag (should NOT source .lldbinit)
-            self.build_and_create_debug_adapter(
-                lldbDAPEnv={"HOME": temp_home}, additional_args=["--no-lldbinit"]
-            )
+            # Build, then spin up a debug adapter with HOME pointing at the
+            # temp dir and the --no-lldbinit flag.
+            self.build()
             program = self.getBuildArtifact("a.out")
+            adapter = self.create_stdio_debug_adapter(
+                DebugAdapterOptions(
+                    env={"HOME": temp_home},
+                    args=["--no-lldbinit"],
+                )
+            )
+            session = self.create_session(adapter=adapter)
 
-            # Use initCommands to check if .lldbinit was sourced
+            # Use initCommands to check if .lldbinit was sourced.
             initCommands = ["settings show stop-disassembly-display"]
+            process_event = session.launch(
+                LaunchArgs(program=program, initCommands=initCommands)
+            )
+            session.verify_process_exited(after=process_event)
 
-            # Launch with initCommands to check the setting
-            self.launch(program, initCommands=initCommands)
-            self.continue_to_exit()
-
-            # Get console output to verify the setting was NOT set from .lldbinit
-            output = self.get_console()
+            # Get console output to verify the setting was NOT set from .lldbinit.
+            output = session.get_console()
             self.assertTrue(output and len(output) > 0, "expect console output")
 
-            # Verify the setting has default value, not "never" from .lldbinit
+            # Verify the setting has default value, not "never" from .lldbinit.
             self.assertNotIn(
                 "never",
                 output,
                 "Setting should have default value when --no-lldbinit is used",
             )
 
-            # Verify the initCommands were executed
-            self.verify_commands("initCommands", output, initCommands)
+            # Verify the initCommands were executed.
+            session.verify_commands("initCommands", output, initCommands)
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_disabled.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_disabled.py
index d7b8579..3815784 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_disabled.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_disabled.py
@@ -2,12 +2,13 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import expectedFailureWindows
-import lldbdap_testcase
 import os
 
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_shellExpandArguments_disabled(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_shellExpandArguments_disabled(DAPTestCaseBase):
     """
     Tests the default launch of a simple program with shell expansion
     disabled.
@@ -17,15 +18,16 @@
         program = self.getBuildArtifact("a.out")
         program_dir = os.path.dirname(program)
         glob = os.path.join(program_dir, "*.out")
-        self.build_and_launch(program, args=[glob], shellExpandArguments=False)
-        self.continue_to_exit()
+        session = self.build_and_create_session()
+        process_event = session.launch(
+            LaunchArgs(program=program, args=[glob], shellExpandArguments=False)
+        )
+        session.verify_process_exited(after=process_event)
+
         # Now get the STDOUT and verify our program argument is correct
-        output = self.get_stdout()
-        self.assertTrue(output and len(output) > 0, "expect no program output")
-        lines = output.splitlines()
-        for line in lines:
-            quote_path = '"%s"' % (glob)
+        output = session.get_stdout()
+        self.assertTrue(output and len(output) > 0, "expect program output")
+        for line in output.splitlines():
             if line.startswith("arg[1] ="):
-                self.assertIn(
-                    quote_path, line, 'verify "%s" stayed to "%s"' % (glob, glob)
-                )
+                quote_path = f'"{glob}"'
+                self.assertIn(quote_path, line, f'verify "{glob}" stayed as "{glob}"')
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_enabled.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_enabled.py
index 7ddde21..e5ba6a4 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_enabled.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_shellExpandArguments_enabled.py
@@ -2,16 +2,14 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import (
-    skipIfLinux,
-    expectedFailureWindows,
-    expectedFailureAll,
-)
-import lldbdap_testcase
 import os
 
+from lldbsuite.test.decorators import expectedFailureAll, skipIfLinux
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_shellExpandArguments_enabled(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_shellExpandArguments_enabled(DAPTestCaseBase):
     """
     Tests the default launch of a simple program with shell expansion
     enabled.
@@ -25,15 +23,20 @@
         program = self.getBuildArtifact("a.out")
         program_dir = os.path.dirname(program)
         glob = os.path.join(program_dir, "*.out")
-        self.build_and_launch(program, args=[glob], shellExpandArguments=True)
-        self.continue_to_exit()
+        session = self.build_and_create_session()
+        process_event = session.launch(
+            LaunchArgs(program=program, args=[glob], shellExpandArguments=True)
+        )
+        session.verify_process_exited(after=process_event)
+
         # Now get the STDOUT and verify our program argument is correct
-        output = self.get_stdout()
-        self.assertTrue(output and len(output) > 0, "expect no program output")
-        lines = output.splitlines()
-        for line in lines:
-            quote_path = '"%s"' % (program)
+        output = session.get_stdout()
+        self.assertTrue(output and len(output) > 0, "expect program output")
+        for line in output.splitlines():
             if line.startswith("arg[1] ="):
+                quote_path = f'"{program}"'
                 self.assertIn(
-                    quote_path, line, 'verify "%s" expanded to "%s"' % (glob, program)
+                    quote_path,
+                    line,
+                    f'verify "{glob}" expanded to "{program}"',
                 )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_sourcePath.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_sourcePath.py
index 4e09816..b62276c 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_sourcePath.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_sourcePath.py
@@ -2,11 +2,13 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
 import os
 
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_sourcePath(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_sourcePath(DAPTestCaseBase):
     """
     Tests the "sourcePath" will set the target.source-map.
     """
@@ -14,20 +16,23 @@
     def test(self):
         program = self.getBuildArtifact("a.out")
         program_dir = os.path.dirname(program)
-        self.build_and_launch(program, sourcePath=program_dir)
-        self.continue_to_exit()
-        output = self.get_console()
+        session = self.build_and_create_session()
+        process_event = session.launch(
+            LaunchArgs(program=program, sourcePath=program_dir)
+        )
+        session.verify_process_exited(after=process_event)
+
+        output = session.get_console()
         self.assertTrue(output and len(output) > 0, "expect console output")
-        lines = output.splitlines()
         prefix = '(lldb) settings set target.source-map "." '
         found = False
-        for line in lines:
+        for line in output.splitlines():
             if line.startswith(prefix):
                 found = True
-                quoted_path = '"%s"' % (program_dir)
+                quoted_path = f'"{program_dir}"'
                 self.assertEqual(
                     quoted_path,
                     line[len(prefix) :],
-                    "lldb-dap working dir %s == %s" % (quoted_path, line[6:]),
+                    f"lldb-dap working dir {quoted_path} == {line[6:]}",
                 )
         self.assertTrue(found, 'found "sourcePath" in console output')
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection.py
index 8150170..21d3110 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection.py
@@ -2,22 +2,26 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
 import tempfile
 
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_stdio_redirection(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_stdio_redirection(DAPTestCaseBase):
     """
     Test stdio redirection.
     """
 
     def test(self):
-        self.build_and_create_debug_adapter()
         program = self.getBuildArtifact("a.out")
+        session = self.build_and_create_session()
 
         with tempfile.NamedTemporaryFile("rt") as f:
-            self.launch_and_configurationDone(program, stdio=[None, f.name])
-            self.verify_process_exited()
+            process_event = session.launch(
+                LaunchArgs(program=program, stdio=[None, f.name])
+            )
+            session.verify_process_exited(after=process_event)
             lines = f.readlines()
             self.assertIn(
                 program, lines[0], "make sure program path is in first argument"
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection_and_console.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection_and_console.py
index f4512a5..fa503c2 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection_and_console.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stdio_redirection_and_console.py
@@ -2,18 +2,20 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import (
-    skipIfAsan,
-    skipIf,
-    skipIfBuildType,
-    no_match,
-    skipIfWindows,
-)
-import lldbdap_testcase
 import tempfile
 
+from lldbsuite.test.decorators import (
+    no_match,
+    skipIf,
+    skipIfAsan,
+    skipIfBuildType,
+    skipIfWindows,
+)
+from lldbsuite.test.tools.lldb_dap.dap_types import Console, LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
-class TestDAP_launch_stdio_redirection_and_console(lldbdap_testcase.DAPTestCaseBase):
+
+class TestDAP_launch_stdio_redirection_and_console(DAPTestCaseBase):
     """
     Test stdio redirection and console.
     """
@@ -23,14 +25,18 @@
     @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     @skipIfBuildType(["debug"])
     def test(self):
-        self.build_and_create_debug_adapter()
         program = self.getBuildArtifact("a.out")
+        session = self.build_and_create_session()
 
         with tempfile.NamedTemporaryFile("rt") as f:
-            self.launch_and_configurationDone(
-                program, console="integratedTerminal", stdio=[None, f.name, None]
+            process_event = session.launch(
+                LaunchArgs(
+                    program=program,
+                    console=Console.INTEGRATED_TERMINAL,
+                    stdio=[None, f.name, None],
+                )
             )
-            self.verify_process_exited()
+            session.verify_process_exited(after=process_event)
             lines = f.readlines()
             self.assertIn(
                 program, lines[0], "make sure program path is in first argument"
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stopOnEntry.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stopOnEntry.py
index 24ceaf3..bbeefc5 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stopOnEntry.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_stopOnEntry.py
@@ -2,10 +2,15 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import (
+    ExitedEvent,
+    LaunchArgs,
+    StoppedEvent,
+)
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_stopOnEntry(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_stopOnEntry(DAPTestCaseBase):
     """
     Tests the default launch of a simple program that stops at the
     entry point instead of continuing.
@@ -13,16 +18,25 @@
 
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.build_and_launch(program, stopOnEntry=True)
-        self.dap_server.request_configurationDone()
-        self.dap_server.wait_for_stopped()
-        self.assertTrue(
-            len(self.dap_server.thread_stop_reasons) > 0,
-            "expected stopped event during launch",
+        session = self.build_and_create_session()
+        process_event = session.launch(LaunchArgs(program, stopOnEntry=True))
+        stop_event = session.verify_stopped_on_entry(after=process_event)
+        exit_event = session.continue_to_exit()
+
+        seen_stop_events = []
+
+        def matches_exit_event(evt):
+            if isinstance(evt, StoppedEvent):
+                seen_stop_events.append(evt)
+            return evt.seq == exit_event.seq
+
+        # Collect stopped events until the ExitEvent.
+        session.wait_for_any_event(
+            (StoppedEvent, ExitedEvent), after=stop_event, until=matches_exit_event
         )
-        for _, body in self.dap_server.thread_stop_reasons.items():
-            if "reason" in body:
-                reason = body["reason"]
-                self.assertNotEqual(
-                    reason, "breakpoint", 'verify stop isn\'t "main" breakpoint'
-                )
+        # Verify we did not receive any other stop event.
+        self.assertEqual(
+            len(seen_stop_events),
+            0,
+            f"expected no new stopped events. seen events: {seen_stop_events}",
+        )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_terminate_commands.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_terminate_commands.py
index e2eed86..7e89a38 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_terminate_commands.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_terminate_commands.py
@@ -2,32 +2,35 @@
 Test lldb-dap launch request.
 """
 
-from lldbsuite.test.decorators import skipIfNetBSD, skipIf
-import lldbdap_testcase
+from lldbsuite.test.decorators import skipIf, skipIfNetBSD
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_terminate_commands(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_terminate_commands(DAPTestCaseBase):
     """
-    Tests that the "terminateCommands", that can be passed during
-    launch, are run when the debugger is disconnected.
+    Tests that the "terminateCommands", that can be passed during launch,
+    are run when the debugger is disconnected.
     """
 
     @skipIfNetBSD  # Hangs on NetBSD as well
     @skipIf(archs=["arm$", "aarch64"], oslist=["linux"])
     def test(self):
-        self.build_and_create_debug_adapter()
         program = self.getBuildArtifact("a.out")
+        session = self.build_and_create_session(disconnect_automatically=False)
 
-        terminateCommands = ["expr 4+2"]
-        self.launch(
-            program,
-            stopOnEntry=True,
-            terminateCommands=terminateCommands,
-            disconnectAutomatically=False,
+        terminate_commands = ["history"]
+        process_event = session.launch(
+            LaunchArgs(
+                program=program,
+                stopOnEntry=True,
+                terminateCommands=terminate_commands,
+            )
         )
-        self.get_console()
-        # Once it's disconnected the console should contain the
-        # "terminateCommands"
-        self.dap_server.request_disconnect(terminateDebuggee=True)
-        output = self.collect_console(pattern=terminateCommands[0])
-        self.verify_commands("terminateCommands", output, terminateCommands)
+        stop_event = session.verify_stopped_on_entry(after=process_event)
+        # Once it's disconnected the console should contain the "terminateCommands".
+        session.disconnect(terminateDebuggee=True)
+        output = session.collect_console(after=stop_event, until=terminate_commands[0])
+        session.verify_commands(
+            "terminateCommands", output.seen_texts, terminate_commands
+        )
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_termination.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_termination.py
index 71e5a45..35cad17 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_termination.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_termination.py
@@ -2,25 +2,41 @@
 Test lldb-dap launch request.
 """
 
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
+from lldbsuite.test.tools.lldb_dap.utils import DebugAdapter
 
 
-class TestDAP_launch_termination(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_termination(DAPTestCaseBase):
     """
     Tests the correct termination of lldb-dap upon a 'disconnect' request.
     """
 
-    def test(self):
-        self.create_debug_adapter()
-        # The underlying lldb-dap process must be alive
-        self.assertEqual(self.dap_server.process.poll(), None)
+    USE_DEFAULT_DEBUG_ADAPTER = False
 
+    def test_termination_socket(self):
+        adapter = self.create_server_debug_adapter(
+            connection="listen://localhost:0",
+            connection_timeout=1,
+        )
+        self.do_test_termination(adapter)
+
+    def test_termination_stdio(self):
+        adapter = self.create_stdio_debug_adapter()
+        self.do_test_termination(adapter)
+
+    def do_test_termination(self, adapter: DebugAdapter):
+        # The underlying lldb-dap process must be alive.
+        self.assertTrue(adapter.is_alive, f"adapter is dead: {adapter.process.args}")
+        session = self.create_session(adapter, disconnect_automatically=False)
+
+        session.initialize_sequence(session.initialize_args)
         # The lldb-dap process should finish even though
-        # we didn't close the communication socket explicitly
-        self.dap_server.request_disconnect()
+        # we didn't close the communication socket explicitly.
+        session.disconnect()
 
         # Wait until the underlying lldb-dap process dies.
-        self.dap_server.process.wait(timeout=self.DEFAULT_TIMEOUT)
+        adapter.process.wait(timeout=self.DEFAULT_TIMEOUT)
+        self.assertFalse(session.is_running(), f"expected ended session.")
 
-        # Check the return code
-        self.assertEqual(self.dap_server.process.poll(), 0)
+        # Check the return code.
+        self.assertEqual(adapter.process.poll(), 0)
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_version.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_version.py
index fca1530..0c3609f 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_version.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch_version.py
@@ -1,13 +1,8 @@
-"""
-Test lldb-dap launch request.
-"""
-
-from lldbsuite.test.decorators import expectedFailureWindows
-from lldbsuite.test.lldbtest import line_number
-import lldbdap_testcase
+from lldbsuite.test.tools.lldb_dap.dap_types import LaunchArgs
+from lldbsuite.test.tools.lldb_dap.lldb_dap_testcase import DAPTestCaseBase
 
 
-class TestDAP_launch_version(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_launch_version(DAPTestCaseBase):
     """
     Tests that "initialize" response contains the "version" string the same
     as the one returned by "version" command.
@@ -15,21 +10,14 @@
 
     def test(self):
         program = self.getBuildArtifact("a.out")
-        self.build_and_launch(program)
+        session = self.build_and_create_session()
 
-        source = "main.c"
-        breakpoint_line = line_number(source, "// breakpoint 1")
-        lines = [breakpoint_line]
-        # Set breakpoint in the thread function so we can step the threads
-        breakpoint_ids = self.set_source_breakpoints(source, lines)
-        self.continue_to_breakpoints(breakpoint_ids)
+        process_event = session.launch(LaunchArgs(program=program, stopOnEntry=True))
+        session.verify_stopped_on_entry(after=process_event)
 
-        version_eval_response = self.dap_server.request_evaluate(
-            "`version", context="repl"
-        )
-        version_eval_output = version_eval_response["body"]["result"]
+        version_eval_output = session.evaluate("`version", context="repl").result
+        version_string = self.expect_not_none(session.capabilities().lldb_version)
 
-        version_string = self.dap_server.get_capability("$__lldb_version")
         self.assertEqual(
             version_eval_output.splitlines(),
             version_string.splitlines(),
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 75f3bc3..078f607 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -16,6 +16,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Base64.h"
 #include "llvm/Support/JSON.h"
+#include <optional>
 #include <utility>
 
 using namespace llvm;
@@ -35,13 +36,15 @@
     return true;
 
   if (const json::Object *env_obj = value->getAsObject()) {
-    for (const auto &kv : *env_obj) {
-      const std::optional<StringRef> value = kv.second.getAsString();
-      if (!value) {
-        P.field("env").field(kv.first).report("expected string value");
+    for (const auto &[key, val] : *env_obj) {
+      if (const std::optional<StringRef> val_str = val.getAsString()) {
+        env.insert({key.str(), val_str->str()});
+      } else if (val.getAsNull()) {
+        env.insert({key.str(), ""});
+      } else {
+        P.field("env").field(key).report("expected string value");
         return false;
       }
-      env.insert({kv.first.str(), value->str()});
     }
     return true;
   }