Simplify the implementation of `run_tv_casting_test.py` for easier addition of future test sequences that verifies that Linux tv-casting-app continues to work with Linux tv-app. (#33855)

* Simplify the implementation of `run_tv_casting_test.py` for easier addition of future test sequences that verifies that Linux tv-casting-app continues to work with Linux tv-app.

* Addressed PR comments from @sharadb-amazon.

* Addressed @sharadb-amazon PR comments. Also restructured the code to prevent circular import issues.

* Run restyle.

* Fixed typo.
diff --git a/scripts/tests/linux/tv_casting_test_sequence_utils.py b/scripts/tests/linux/tv_casting_test_sequence_utils.py
new file mode 100644
index 0000000..e95bc43
--- /dev/null
+++ b/scripts/tests/linux/tv_casting_test_sequence_utils.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env -S python3 -B
+
+# Copyright (c) 2024 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from enum import Enum
+from typing import List, Optional
+
+"""
+This file defines the utility classes for creating and managing test sequences to validate the casting experience between 
+the Linux tv-casting-app and the Linux tv-app. It includes an enumeration for the supported applications (App), a class to 
+represent individual steps in a test sequence (Step), and a class to represent a complete test sequence (Sequence). 
+Additionally, it provides helper functions to retrieve specific test sequences or all defined test sequences.
+"""
+
+
+class App(Enum):
+    """An enumeration of the supported applications."""
+
+    TV_APP = 'tv-app'
+    TV_CASTING_APP = 'tv-casting-app'
+
+
+class Step:
+    """A class to represent a step in a test sequence for validation.
+
+    A `Step` object contains attributes relevant to a test step where each object contains:
+    - `app` subprocess to parse for `output_msg` or send `input_cmd`
+    - `timeout_sec` specified the timeout duration for parsing the `output_msg` (optional, defaults to DEFAULT_TIMEOUT_SEC)
+    - `output_msg` or `input_cmd` (mutually exclusive)
+
+    For output message blocks, define the start line, relevant lines, and the last line. If the last line contains trivial closing 
+    characters (e.g., closing brackets, braces, or commas), include the line before it with actual content. For example:
+        `Step(subprocess_='tv-casting-app', output_msg=['InvokeResponseMessage =', 'exampleData', 'InteractionModelRevision =', '},'])`
+
+    For input commands, define the command string with placeholders for variables that need to be updated. For example:
+        `Step(subprocess_='tv-casting-app', input_cmd='cast request 0\n')`
+    """
+
+    # The maximum default time to wait while parsing for output string(s).
+    DEFAULT_TIMEOUT_SEC = 10
+
+    def __init__(
+        self,
+        app: App,
+        timeout_sec: Optional[int] = DEFAULT_TIMEOUT_SEC,
+        output_msg: Optional[List[str]] = None,
+        input_cmd: Optional[str] = None,
+    ):
+        # Validate that either `output_msg` or `input_cmd` is provided, but not both.
+        if output_msg is not None and input_cmd is not None:
+            raise ValueError(
+                'Step cannot contain both `output_msg` and `input_cmd`. Either `output_msg` or `input_cmd` should be provided.')
+        elif output_msg is None and input_cmd is None:
+            raise ValueError('Step must contain either `output_msg` or `input_cmd`. Both are `None`.')
+
+        # Define either `App.TV_APP` or `App.TV_CASTING_APP` on which we need to parse for `output_msg` or send `input_cmd`.
+        self.app = app
+
+        # Define the maximum time in seconds for timeout while parsing for the `output_msg`. If not provided, then we use the DEFAULT_TIMEOUT_SEC.
+        self.timeout_sec = timeout_sec
+
+        # Define the `output_msg` that we need to parse for in a list format.
+        self.output_msg = output_msg
+
+        # Define the `input_cmd` that we need to send to either the `App.TV_APP` or `App.TV_CASTING_APP`.
+        self.input_cmd = input_cmd
+
+
+class Sequence:
+    """A class representing a sequence of steps for testing the casting experience between the Linux tv-casting-app and the tv-app.
+
+    A Sequence object needs to be defined with an appropriate test sequence `name` along with its list of `Step` objects that will
+    be used for validating the casting experience.
+    """
+
+    def __init__(self, name: str, steps: List[Step]):
+        self.name = name
+        self.steps = steps
+
+    @staticmethod
+    def get_test_sequence_by_name(test_sequences: List['Sequence'], test_sequence_name: str) -> Optional['Sequence']:
+        """Retrieve a test sequence from a list of sequences by its name."""
+
+        for sequence in test_sequences:
+            if sequence.name == test_sequence_name:
+                return sequence
+        return None
+
+    @staticmethod
+    def get_test_sequences() -> List['Sequence']:
+        """Retrieve all the test sequences to validate the casting experience between the Linux tv-casting-app and the Linux tv-app."""
+
+        from linux.tv_casting_test_sequences import test_sequences
+
+        return test_sequences
diff --git a/scripts/tests/linux/tv_casting_test_sequences.py b/scripts/tests/linux/tv_casting_test_sequences.py
new file mode 100644
index 0000000..9b65e64
--- /dev/null
+++ b/scripts/tests/linux/tv_casting_test_sequences.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env -S python3 -B
+
+# Copyright (c) 2024 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from linux.tv_casting_test_sequence_utils import App, Sequence, Step
+
+"""
+In this file, we define the test sequences with the relevant steps that will be used in the `scripts/tests/run_tv_casting_test.py` 
+for validating the casting experience between the Linux tv-casting-app and the Linux tv-app. 
+
+At the beginning of each test sequence we need to indicate the start up of the tv-app using the `START_APP` string as the `input_cmd`
+followed by the same for the tv-casting-app. On the other hand, at the end of each test sequence we need to ensure that each app will 
+be stopped by providing the `STOP_APP` string as the `input_cmd`. As noted in the example below of `example_test_sequence`, the first
+four steps pertain to starting the apps while the last two are for signaling stopping the apps. 
+
+Note: `START_APP` and `STOP_APP` are reserved for signaling the starting and stopping of apps.
+
+Example:
+     test_sequences = [
+          Sequence(
+               name='example_test_sequence',
+               step=[
+                    # Signal to start the tv-app.
+                    Step(app=App.TV_APP, input_cmd=START_APP),
+
+                    # Validate that the tv-app is up and running.
+                    Step(app=App.TV_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Started commissioner']),
+
+                    # Signal to start the tv-casting-app.
+                    Step(app=App.TV_CASTING_APP, input_cmd=START_APP),
+
+                    # Validate that the server is properly initialized in the tv-casting-app output.
+                    Step(app=App.TV_CASTING_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Server initialization complete']),
+
+                    # Additional steps for testing the casting experience.
+
+                    # Signal to stop the tv-casting-app as we finished validation.
+                    Step(app=App.TV_CASTING_APP, input_cmd=STOP_APP),
+
+                    # Signal to stop the tv-app as we finished validation.
+                    Step(app=App.TV_APP, input_cmd=STOP_APP)
+               ]
+          )
+     ]
+"""
+
+# Signal to start the app.
+START_APP = 'START'
+
+# Signal to stop the app.
+STOP_APP = 'STOP'
+
+# The maximum amount of time to wait for the Linux tv-app or Linux tv-casting-app to start before timeout.
+APP_MAX_START_WAIT_SEC = 2
+
+# Values that identify the Linux tv-app and are noted in the 'Device Configuration' in the Linux tv-app output
+# as well as under the 'Discovered Commissioner' details in the Linux tv-casting-app output.
+VENDOR_ID = 0xFFF1   # Spec 7.20.2.1 MEI code: test vendor IDs are 0xFFF1 to 0xFFF4
+PRODUCT_ID = 0x8001  # Test product id
+DEVICE_TYPE_CASTING_VIDEO_PLAYER = 0x23    # Device type library 10.3: Casting Video Player
+
+TEST_TV_CASTING_APP_DEVICE_NAME = 'Test TV casting app'  # Test device name for identifying the tv-casting-app
+
+# Values to verify the subscription state against from the `ReportDataMessage` in the Linux tv-casting-app output.
+CLUSTER_MEDIA_PLAYBACK = '0x506'  # Application Cluster Spec 6.10.3 Cluster ID: Media Playback
+ATTRIBUTE_CURRENT_PLAYBACK_STATE = '0x0000_0000'  # Application Cluster Spec 6.10.6 Attribute ID: Current State of Playback
+
+test_sequences = [
+    Sequence(
+        name='commissionee_generated_passcode_test',
+        steps=[
+             # Signal to start the tv-app.
+            Step(app=App.TV_APP, input_cmd=START_APP),
+
+            # Validate that the tv-app is up and running.
+            Step(app=App.TV_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Started commissioner']),
+
+            # Signal to start the tv-casting-app.
+            Step(app=App.TV_CASTING_APP, input_cmd=START_APP),
+
+            # Validate that the server is properly initialized in the tv-casting-app output.
+            Step(app=App.TV_CASTING_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Server initialization complete']),
+
+            # Validate that there is a valid discovered commissioner with {VENDOR_ID}, {PRODUCT_ID}, and {DEVICE_TYPE_CASTING_VIDEO_PLAYER} in the tv-casting-app output.
+            Step(app=App.TV_CASTING_APP, output_msg=['Discovered Commissioner #0', f'Vendor ID: {VENDOR_ID}', f'Product ID: {PRODUCT_ID}',
+                                                     f'Device Type: {DEVICE_TYPE_CASTING_VIDEO_PLAYER}', 'Supports Commissioner Generated Passcode: true']),
+
+            # Validate that we are ready to send `cast request` command to the tv-casting-app subprocess.
+            Step(app=App.TV_CASTING_APP, output_msg=['Example: cast request 0']),
+
+            # Send `cast request {valid_discovered_commissioner_number}\n` command to the tv-casting-app subprocess.
+            Step(app=App.TV_CASTING_APP, input_cmd='cast request 0\n'),
+
+            # Validate that the `Identification Declaration` message block in the tv-casting-app output has the expected values for `device Name`, `vendor id`, and `product id`.
+            Step(app=App.TV_CASTING_APP, output_msg=['Identification Declaration Start', f'device Name: {TEST_TV_CASTING_APP_DEVICE_NAME}',
+                                                     f'vendor id: {VENDOR_ID}', f'product id: {PRODUCT_ID}', 'Identification Declaration End']),
+
+            # Validate that the `Identification Declaration` message block in the tv-app output has the expected values for `device Name`, `vendor id`, and `product id`.
+            Step(app=App.TV_APP, output_msg=['Identification Declaration Start', f'device Name: {TEST_TV_CASTING_APP_DEVICE_NAME}',
+                                             f'vendor id: {VENDOR_ID}', f'product id: {PRODUCT_ID}', 'Identification Declaration End']),
+
+            # Validate that we received the cast request from the tv-casting-app on the tv-app output.
+            Step(app=App.TV_APP,
+                 output_msg=['PROMPT USER: Test TV casting app is requesting permission to cast to this TV, approve?']),
+
+            # Validate that we received the instructions on the tv-app output for sending the `controller ux ok` command.
+            Step(app=App.TV_APP, output_msg=['Via Shell Enter: controller ux ok|cancel']),
+
+            # Send `controller ux ok` command to the tv-app subprocess.
+            Step(app=App.TV_APP, input_cmd='controller ux ok\n'),
+
+            # Validate that pairing succeeded between the tv-casting-app and the tv-app.
+            Step(app=App.TV_APP, output_msg=['Secure Pairing Success']),
+
+            # Validate that commissioning succeeded in the tv-casting-app output.
+            Step(app=App.TV_CASTING_APP, output_msg=['Commissioning completed successfully']),
+
+            # Validate that commissioning succeeded in the tv-app output.
+            Step(app=App.TV_APP, output_msg=['------PROMPT USER: commissioning success']),
+
+            # Validate the subscription state by looking at the `Cluster` and `Attribute` values in the `ReportDataMessage` block in the tv-casting-app output.
+            Step(app=App.TV_CASTING_APP, output_msg=[
+                'ReportDataMessage =', f'Cluster = {CLUSTER_MEDIA_PLAYBACK}', f'Attribute = {ATTRIBUTE_CURRENT_PLAYBACK_STATE}', 'InteractionModelRevision =', '}']),
+
+            # Validate the LaunchURL in the tv-app output.
+            Step(app=App.TV_APP,
+                 output_msg=['ContentLauncherManager::HandleLaunchUrl TEST CASE ContentURL=https://www.test.com/videoid DisplayString=Test video']),
+
+            # Validate the LaunchURL in the tv-casting-app output.
+            Step(app=App.TV_CASTING_APP, output_msg=['InvokeResponseMessage =',
+                                                     'exampleData', 'InteractionModelRevision =', '},']),
+
+            # Signal to stop the tv-casting-app as we finished validation.
+            Step(app=App.TV_CASTING_APP, input_cmd=STOP_APP),
+
+            # Signal to stop the tv-app as we finished validation.
+            Step(app=App.TV_APP, input_cmd=STOP_APP)
+        ]
+    )
+]
diff --git a/scripts/tests/run_tv_casting_test.py b/scripts/tests/run_tv_casting_test.py
index 074d459..45cc617 100644
--- a/scripts/tests/run_tv_casting_test.py
+++ b/scripts/tests/run_tv_casting_test.py
@@ -16,44 +16,31 @@
 
 import logging
 import os
-import re
+import signal
 import subprocess
 import sys
 import tempfile
 import time
-from typing import List, Optional, TextIO, Tuple, Union
+from typing import List, TextIO, Tuple
 
 import click
+from linux.tv_casting_test_sequence_utils import App, Sequence, Step
+from linux.tv_casting_test_sequences import START_APP, STOP_APP
+
+"""
+This script can be used to validate the casting experience between the Linux tv-casting-app and the Linux tv-app.
+
+It runs a series of test sequences that check for expected output lines from the tv-casting-app and the tv-app in 
+a deterministic order. If these lines are not found, it indicates an issue with the casting experience.
+"""
 
 # Configure logging format.
 logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')
 
-# The maximum amount of time to wait for the Linux tv-app to start before timeout.
-TV_APP_MAX_START_WAIT_SEC = 2
-
-# The maximum amount of time to commission the Linux tv-casting-app and the tv-app before timeout.
-COMMISSIONING_STAGE_MAX_WAIT_SEC = 15
-
-# The maximum amount of time to test that the launchURL is sent from the Linux tv-casting-app and received on the tv-app before timeout.
-TEST_LAUNCHURL_MAX_WAIT_SEC = 10
-
-# The maximum amount of time to verify the subscription state in the Linux tv-casting-app output before timeout.
-VERIFY_SUBSCRIPTION_STATE_MAX_WAIT_SEC = 10
-
 # File names of logs for the Linux tv-casting-app and the Linux tv-app.
 LINUX_TV_APP_LOGS = 'Linux-tv-app-logs.txt'
 LINUX_TV_CASTING_APP_LOGS = 'Linux-tv-casting-app-logs.txt'
 
-# Values that identify the Linux tv-app and are noted in the 'Device Configuration' in the Linux tv-app output
-# as well as under the 'Discovered Commissioner' details in the Linux tv-casting-app output.
-VENDOR_ID = 0xFFF1   # Spec 7.20.2.1 MEI code: test vendor IDs are 0xFFF1 to 0xFFF4
-PRODUCT_ID = 0x8001  # Test product id
-DEVICE_TYPE_CASTING_VIDEO_PLAYER = 0x23    # Device type library 10.3: Casting Video Player
-
-# Values to verify the subscription state against from the `ReportDataMessage` in the Linux tv-casting-app output.
-CLUSTER_MEDIA_PLAYBACK = '0x506'  # Application Cluster Spec 6.10.3 Cluster ID: Media Playback
-ATTRIBUTE_CURRENT_PLAYBACK_STATE = '0x0000_0000'  # Application Cluster Spec 6.10.6 Attribute ID: Current State of Playback
-
 
 class ProcessManager:
     """A context manager for managing subprocesses.
@@ -76,26 +63,6 @@
         self.process.wait()
 
 
-class LogValueExtractor:
-    """A utility class for extracting values from log lines.
-
-    This class provides a centralized way to extract values from log lines and manage the error handling and logging process.
-    """
-
-    def __init__(self, casting_state: str, log_paths: List[str]):
-        self.casting_state = casting_state
-        self.log_paths = log_paths
-
-    def extract_from(self, line: str, value_name: str):
-        if value_name in line:
-            try:
-                return extract_value_from_string(line, value_name, self.casting_state, self.log_paths)
-            except ValueError:
-                logging.error(f'Failed to extract `{value_name}` value from line: {line}')
-                handle_casting_failure(self.casting_state, self.log_paths)
-        return None
-
-
 def dump_temporary_logs_to_console(log_file_path: str):
     """Dump log file to the console; log file will be removed once the function exits."""
     """Write the entire content of `log_file_path` to the console."""
@@ -106,508 +73,200 @@
             print(line.rstrip())
 
 
-def handle_casting_failure(casting_state: str, log_file_paths: List[str]):
-    """Log '{casting_state} failed!' as error, dump log files to console, exit on error."""
-    logging.error(f'{casting_state} failed!')
+def handle_casting_failure(test_sequence_name: str, log_file_paths: List[str]):
+    """Log failure of validation of test sequence as error, dump log files to console, exit on error."""
+    logging.error(f'{test_sequence_name} - Validation of test sequence failed.')
 
     for log_file_path in log_file_paths:
         try:
             dump_temporary_logs_to_console(log_file_path)
         except Exception as e:
-            logging.exception(f'Failed to dump {log_file_path}: {e}')
+            logging.exception(f'{test_sequence_name} - Failed to dump {log_file_path}: {e}')
 
     sys.exit(1)
 
 
-def extract_value_from_string(line: str, value_name: str, casting_state: str, log_paths) -> str:
-    """Extract and return value from given input string.
+def stop_app(test_sequence_name: str, app_name: str, app: subprocess.Popen):
+    """Stop the given `app` subprocess."""
 
-    Some string examples as they are received from the Linux tv-casting-app and/or tv-app output:
-    1. On 'darwin' machines:
-        \x1b[0;34m[1715206773402] [20056:2842184] [DMG]  Cluster = 0x506,\x1b[0m
-        The substring to be extracted here is '0x506'.
+    app.terminate()
+    app_exit_code = app.wait()
 
-        Or:
-        \x1b[0;32m[1714582264602] [77989:2286038] [SVR] Discovered Commissioner #0\x1b[0m
-        The integer value to be extracted here is '0'.
-
-        Or:
-        \x1b[0;34m[1713741926895] [7276:9521344] [DIS] Vendor ID: 65521\x1b[0m
-        The integer value to be extracted here is '65521'.
-
-        Or:
-        \x1b[0;34m[1714583616179] [7029:2386956] [SVR] 	device Name: Test TV casting app\x1b[0m
-        The substring to be extracted here is 'Test TV casting app'.
-
-    2. On 'linux' machines:
-        [1716224960.316809][6906:6906] CHIP:DMG: \t\t\t\t\tCluster = 0x506,\n
-        [1716224958.576320][6906:6906] CHIP:SVR: Discovered Commissioner #0
-        [1716224958.576407][6906:6906] CHIP:DIS: \tVendor ID: 65521\n
-        [1716224959.580746][6906:6906] CHIP:SVR: \tdevice Name: Test TV casting app\n
-    """
-    log_line_pattern = ''
-    if sys.platform == 'darwin':
-        log_line_pattern = r'\x1b\[0;\d+m\[\d+\] \[\d+:\d+\] \[[A-Z]{1,3}\] (.+)\x1b\[0m'
-    elif sys.platform == 'linux':
-        log_line_pattern = r'\[\d+\.\d+\]\[\d+:\d+\] [A-Z]{1,4}:[A-Z]{1,3}: (.+)'
-
-    log_line_match = re.search(log_line_pattern, line)
-
-    if log_line_match:
-        log_text_of_interest = log_line_match.group(1)
-
-        if '=' in log_text_of_interest:
-            delimiter = '='
-        elif '#' in log_text_of_interest:
-            delimiter = '#'
+    if app.poll() is None:
+        logging.error(f'{test_sequence_name}: Failed to stop running {app_name}. Process is still running.')
+    else:
+        if app_exit_code < 0:
+            signal_number = -app_exit_code
+            if signal_number == signal.SIGTERM.value:
+                logging.info(f'{test_sequence_name}: {app_name} stopped by {signal_number} (SIGTERM) signal.')
+                return True
+            else:
+                logging.error(
+                    f'{test_sequence_name}: {app_name} stopped by signal {signal_number} instead of {signal.SIGTERM.value} (SIGTERM).')
         else:
-            delimiter = ':'
+            logging.error(f'{test_sequence_name}: {app_name} exited with unexpected exit code {app_exit_code}.')
 
-        return log_text_of_interest.split(delimiter)[-1].strip(' ,')
+    return False
+
+
+def parse_output_msg_in_subprocess(
+    tv_casting_app_info: Tuple[subprocess.Popen, TextIO],
+    tv_app_info: Tuple[subprocess.Popen, TextIO],
+    log_paths: List[str],
+    test_sequence_name: str,
+    test_sequence_step: Step
+):
+    """Parse the output of a given `app` subprocess and validate its output against the expected `output_msg` in the given `Step`."""
+
+    if not test_sequence_step.output_msg:
+        logging.error(f'{test_sequence_name} - No output message provided in the test sequence step.')
+        return False
+
+    app_subprocess, app_log_file = (tv_casting_app_info if test_sequence_step.app == App.TV_CASTING_APP else tv_app_info)
+
+    start_wait_time = time.time()
+    msg_block = []
+
+    current_index = 0
+    while current_index < len(test_sequence_step.output_msg):
+        # Check if we exceeded the maximum wait time to parse for the output string(s).
+        if time.time() - start_wait_time > test_sequence_step.timeout_sec:
+            logging.error(
+                f'{test_sequence_name} - Did not find the expected output string(s) in the {test_sequence_step.app.value} subprocess within the timeout: {test_sequence_step.output_msg}')
+            return False
+
+        output_line = app_subprocess.stdout.readline()
+
+        if output_line:
+            app_log_file.write(output_line)
+            app_log_file.flush()
+
+            if (test_sequence_step.output_msg[current_index] in output_line):
+                msg_block.append(output_line.rstrip('\n'))
+                current_index += 1
+            elif msg_block:
+                msg_block.append(output_line.rstrip('\n'))
+                if (test_sequence_step.output_msg[0] in output_line):
+                    msg_block.clear()
+                    msg_block.append(output_line.rstrip('\n'))
+                    current_index = 1
+                # Sanity check that `Discovered Commissioner #0` is the valid commissioner.
+                elif 'Discovered Commissioner #' in output_line:
+                    logging.error(f'{test_sequence_name} - The valid discovered commissioner should be `Discovered Commissioner #0`.')
+                    handle_casting_failure(test_sequence_name, log_paths)
+
+            if current_index == len(test_sequence_step.output_msg):
+                logging.info(f'{test_sequence_name} - Found the expected output string(s) in the {test_sequence_step.app.value} subprocess:')
+                for line in msg_block:
+                    logging.info(f'{test_sequence_name} - {line}')
+
+                return True
+
+
+def send_input_cmd_to_subprocess(
+    tv_casting_app_info: Tuple[subprocess.Popen, TextIO],
+    tv_app_info: Tuple[subprocess.Popen, TextIO],
+    test_sequence_name: str,
+    test_sequence_step: Step
+):
+    """Send a given input command (`input_cmd`) from the `Step` to its given `app` subprocess."""
+
+    if not test_sequence_step.input_cmd:
+        logging.error(f'{test_sequence_name} - No input command provided in the test sequence step.')
+        return False
+
+    app_subprocess, app_log_file = (tv_casting_app_info if test_sequence_step.app == App.TV_CASTING_APP else tv_app_info)
+
+    app_subprocess.stdin.write(test_sequence_step.input_cmd)
+    app_subprocess.stdin.flush()
+
+    # Read in the next line which should be the `input_cmd` that was issued.
+    next_line = app_subprocess.stdout.readline()
+    app_log_file.write(next_line)
+    app_log_file.flush()
+    next_line = next_line.rstrip('\n')
+
+    logging.info(f'{test_sequence_name} - Sent `{next_line}` to the {test_sequence_step.app.value} subprocess.')
+
+    return True
+
+
+def handle_output_msg(
+    tv_casting_app_info: Tuple[subprocess.Popen, TextIO],
+    tv_app_info: Tuple[subprocess.Popen, TextIO],
+    log_paths: List[str],
+    test_sequence_name: str,
+    test_sequence_step: Step
+):
+    """Handle the output message (`output_msg`) from a test sequence step."""
+
+    if not parse_output_msg_in_subprocess(tv_casting_app_info, tv_app_info, log_paths, test_sequence_name, test_sequence_step):
+        handle_casting_failure(test_sequence_name, log_paths)
+
+
+def handle_input_cmd(
+    tv_casting_app_info: Tuple[subprocess.Popen, TextIO],
+    tv_app_info: Tuple[subprocess.Popen, TextIO],
+    log_paths: List[str],
+    test_sequence_name: str,
+    test_sequence_step: Step
+):
+    """Handle the input command (`input_cmd`) from a test sequence step."""
+
+    tv_casting_app_process, tv_casting_app_log_file = tv_casting_app_info
+    tv_app_process, tv_app_log_file = tv_app_info
+
+    if test_sequence_step.input_cmd == STOP_APP:
+        if test_sequence_step.app == App.TV_CASTING_APP:
+            # Stop the tv-casting-app subprocess.
+            if not stop_app(test_sequence_name, test_sequence_step.app.value, tv_casting_app_process):
+                handle_casting_failure(test_sequence_name, log_paths)
+        elif test_sequence_step.app == App.TV_APP:
+            # Stop the tv-app subprocess.
+            if not stop_app(test_sequence_name, test_sequence_step.app.value, tv_app_process):
+                handle_casting_failure(test_sequence_name, log_paths)
     else:
-        raise ValueError(f'Could not extract {value_name} from the following line: {line}')
+        if not send_input_cmd_to_subprocess(tv_casting_app_info, tv_app_info, test_sequence_name, test_sequence_step):
+            handle_casting_failure(test_sequence_name, log_paths)
 
 
-def validate_value(casting_state: str, expected_value: Union[str, int], log_paths: List[str], line: str, value_name: str) -> Optional[str]:
-    """Validate a value in a string against an expected value during a given casting state."""
-    log_value_extractor = LogValueExtractor(casting_state, log_paths)
-    value = log_value_extractor.extract_from(line, value_name)
-    if not value:
-        logging.error(f'Failed to extract {value_name} value from the following line: {line}')
-        logging.error(f'Failed to validate against the expected {value_name} value: {expected_value}!')
-        handle_casting_failure(casting_state, log_paths)
+def run_test_sequence_steps(
+    current_index: int,
+    test_sequence_name: str,
+    test_sequence_steps: List[Step],
+    tv_casting_app_info: Tuple[subprocess.Popen, TextIO],
+    tv_app_info: Tuple[subprocess.Popen, TextIO],
+    log_paths: List[str]
+):
+    """Run through the test steps from a test sequence starting from the current index and perform actions based on the presence of `output_msg` or `input_cmd`."""
 
-    if isinstance(expected_value, int):
-        value = int(value)
+    if test_sequence_steps is None:
+        logging.error('No test sequence steps provided.')
 
-    if value != expected_value:
-        logging.error(f'{value_name} does not match the expected value!')
-        logging.error(f'Expected {value_name}: {expected_value}')
-        logging.error(line.rstrip('\n'))
-        handle_casting_failure(casting_state, log_paths)
+    while current_index < len(test_sequence_steps):
+        # Current step in the list of steps.
+        test_sequence_step = test_sequence_steps[current_index]
 
-    # Return the line containing the valid value.
-    return line.rstrip('\n')
+        # A test sequence step contains either an output_msg or input_cmd entry.
+        if test_sequence_step.output_msg:
+            handle_output_msg(tv_casting_app_info, tv_app_info, log_paths, test_sequence_name, test_sequence_step)
+        elif test_sequence_step.input_cmd:
+            handle_input_cmd(tv_casting_app_info, tv_app_info, log_paths, test_sequence_name, test_sequence_step)
 
-
-def start_up_tv_app_success(tv_app_process: subprocess.Popen, linux_tv_app_log_file: TextIO) -> bool:
-    """Check if the Linux tv-app is able to successfully start or until timeout occurs."""
-    start_wait_time = time.time()
-
-    while True:
-        # Check if the time elapsed since the start wait time exceeds the maximum allowed startup time for the TV app.
-        if time.time() - start_wait_time > TV_APP_MAX_START_WAIT_SEC:
-            logging.error('The Linux tv-app process did not start successfully within the timeout.')
-            return False
-
-        tv_app_output_line = tv_app_process.stdout.readline()
-
-        linux_tv_app_log_file.write(tv_app_output_line)
-        linux_tv_app_log_file.flush()
-
-        # Check if the Linux tv-app started successfully.
-        if "Started commissioner" in tv_app_output_line:
-            logging.info('Linux tv-app is up and running!')
-            return True
-
-
-def initiate_cast_request_success(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], valid_discovered_commissioner_number: str) -> bool:
-    """Initiate commissioning between Linux tv-casting-app and tv-app by sending `cast request {valid_discovered_commissioner_number}` via Linux tv-casting-app process."""
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time for initiating 'cast request' from the Linux tv-casting-app to the Linux tv-app.
-        if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC:
-            logging.error(
-                f'The command `cast request {valid_discovered_commissioner_number}` was not issued to the Linux tv-casting-app process within the timeout.')
-            return False
-
-        tv_casting_app_output_line = tv_casting_app_process.stdout.readline()
-        if tv_casting_app_output_line:
-            linux_tv_casting_app_log_file.write(tv_casting_app_output_line)
-            linux_tv_casting_app_log_file.flush()
-
-            if 'cast request 0' in tv_casting_app_output_line:
-                tv_casting_app_process.stdin.write('cast request ' + valid_discovered_commissioner_number + '\n')
-                tv_casting_app_process.stdin.flush()
-                # Move to the next line otherwise we will keep entering this code block
-                next_line = tv_casting_app_process.stdout.readline()
-                linux_tv_casting_app_log_file.write(next_line)
-                linux_tv_casting_app_log_file.flush()
-                next_line = next_line.rstrip('\n')
-                logging.info(f'Sent `{next_line}` to the Linux tv-casting-app process.')
-
-                return True
-
-
-def extract_device_info_from_tv_casting_app(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], casting_state: str, log_paths: List[str]) -> Tuple[Optional[str], Optional[int], Optional[int]]:
-    """Extract device information from the 'Identification Declaration' block in the Linux tv-casting-app output."""
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-    log_value_extractor = LogValueExtractor(casting_state, log_paths)
-
-    device_name = None
-    vendor_id = None
-    product_id = None
-
-    for line in tv_casting_app_process.stdout:
-        linux_tv_casting_app_log_file.write(line)
-        linux_tv_casting_app_log_file.flush()
-
-        if value := log_value_extractor.extract_from(line, 'device Name'):
-            device_name = value
-        elif value := log_value_extractor.extract_from(line, 'vendor id'):
-            vendor_id = int(value)
-        elif value := log_value_extractor.extract_from(line, 'product id'):
-            product_id = int(value)
-
-        if device_name and vendor_id and product_id:
-            break
-
-    return device_name, vendor_id, product_id
-
-
-def validate_identification_declaration_message_on_tv_app(tv_app_info: Tuple[subprocess.Popen, TextIO], expected_device_name: str, expected_vendor_id: int, expected_product_id: int, log_paths: List[str]) -> bool:
-    """Validate device information from the 'Identification Declaration' block from the Linux tv-app output against the expected values."""
-    tv_app_process, linux_tv_app_log_file = tv_app_info
-
-    parsing_identification_block = False
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time for validating the device information from the Linux tv-app to the corresponding values from the Linux tv-app.
-        if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC:
-            logging.error(
-                'The device information from the Linux tv-app output was not validated against the corresponding values from the Linux tv-casting-app output within the timeout.')
-            return False
-
-        tv_app_line = tv_app_process.stdout.readline()
-
-        if tv_app_line:
-            linux_tv_app_log_file.write(tv_app_line)
-            linux_tv_app_log_file.flush()
-
-            if 'Identification Declaration Start' in tv_app_line:
-                logging.info('Found the `Identification Declaration` block in the Linux tv-app output:')
-                logging.info(tv_app_line.rstrip('\n'))
-                parsing_identification_block = True
-            elif parsing_identification_block:
-                logging.info(tv_app_line.rstrip('\n'))
-                if 'device Name' in tv_app_line:
-                    validate_value('Commissioning', expected_device_name, log_paths, tv_app_line, 'device Name')
-                elif 'vendor id' in tv_app_line:
-                    validate_value('Commissioning', expected_vendor_id, log_paths, tv_app_line, 'vendor id')
-                elif 'product id' in tv_app_line:
-                    validate_value('Commissioning', expected_product_id, log_paths, tv_app_line, 'product id')
-                elif 'Identification Declaration End' in tv_app_line:
-                    parsing_identification_block = False
-                    return True
-
-
-def validate_tv_casting_request_approval(tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]) -> bool:
-    """Validate that the TV casting request from the Linux tv-casting-app to the Linux tv-app is approved by sending `controller ux ok` via Linux tv-app process."""
-    tv_app_process, linux_tv_app_log_file = tv_app_info
-
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time for sending 'controller ux ok' from the Linux tv-app to the Linux tv-casting-app.
-        if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC:
-            logging.error('The cast request from the Linux tv-casting-app to the Linux tv-app was not approved within the timeout.')
-            return False
-
-        tv_app_line = tv_app_process.stdout.readline()
-
-        if tv_app_line:
-            linux_tv_app_log_file.write(tv_app_line)
-            linux_tv_app_log_file.flush()
-
-            if 'PROMPT USER: Test TV casting app is requesting permission to cast to this TV, approve?' in tv_app_line:
-                logging.info(tv_app_line.rstrip('\n'))
-            elif 'Via Shell Enter: controller ux ok|cancel' in tv_app_line:
-                logging.info(tv_app_line.rstrip('\n'))
-
-                tv_app_process.stdin.write('controller ux ok\n')
-                tv_app_process.stdin.flush()
-
-                tv_app_line = tv_app_process.stdout.readline()
-                linux_tv_app_log_file.write(tv_app_line)
-                linux_tv_app_log_file.flush()
-                tv_app_line = tv_app_line.rstrip('\n')
-                logging.info(f'Sent `{tv_app_line}` to the Linux tv-app process.')
-                return True
-
-
-def validate_commissioning_success(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]) -> bool:
-    """Parse output of Linux tv-casting-app and Linux tv-app output for strings indicating commissioning status."""
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-    tv_app_process, linux_tv_app_log_file = tv_app_info
-
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time for validating commissioning success between the Linux tv-casting-app and the Linux tv-app.
-        if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC:
-            logging.error(
-                'The commissioning between the Linux tv-casting-app process and the Linux tv-app process did not complete successfully within the timeout.')
-            return False
-
-        tv_casting_line = tv_casting_app_process.stdout.readline()
-        tv_app_line = tv_app_process.stdout.readline()
-
-        if tv_casting_line:
-            linux_tv_casting_app_log_file.write(tv_casting_line)
-            linux_tv_casting_app_log_file.flush()
-
-            if 'Commissioning completed successfully' in tv_casting_line:
-                logging.info('Commissioning success noted on the Linux tv-casting-app output:')
-                logging.info(tv_casting_line.rstrip('\n'))
-            elif 'Commissioning failed' in tv_casting_line:
-                logging.error('Commissioning failed noted on the Linux tv-casting-app output:')
-                logging.error(tv_casting_line.rstrip('\n'))
-                return False
-
-        if tv_app_line:
-            linux_tv_app_log_file.write(tv_app_line)
-            linux_tv_app_log_file.flush()
-
-            if 'PROMPT USER: commissioning success' in tv_app_line:
-                logging.info('Commissioning success noted on the Linux tv-app output:')
-                logging.info(tv_app_line)
-                return True
-
-
-def parse_tv_casting_app_for_report_data_msg(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Parse the Linux tv-casting-app for `ReportDataMessage` block and return the first message block with valid `Cluster` and `Attribute` values."""
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-    log_value_extractor = LogValueExtractor('Testing subscription', log_paths)
-
-    continue_parsing = False
-    report_data_message = []
-
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time to parse the Linux tv-casting-app output for `ReportDataMessage` block.
-        if time.time() - start_wait_time > VERIFY_SUBSCRIPTION_STATE_MAX_WAIT_SEC:
-            logging.error(
-                'The relevant `ReportDataMessage` block for the MediaPlayback:CurrentState subscription was not found in the Linux tv-casting-app process within the timeout.')
-            report_data_message.clear()
-            return report_data_message
-
-        tv_casting_line = tv_casting_app_process.stdout.readline()
-
-        if tv_casting_line:
-            linux_tv_casting_app_log_file.write(tv_casting_line)
-            linux_tv_casting_app_log_file.flush()
-
-            if 'ReportDataMessage =' in tv_casting_line:
-                report_data_message.append(tv_casting_line.rstrip('\n'))
-                continue_parsing = True
-            elif continue_parsing:
-                report_data_message.append(tv_casting_line.rstrip('\n'))
-
-                if cluster_value := log_value_extractor.extract_from(tv_casting_line, 'Cluster ='):
-                    if cluster_value != CLUSTER_MEDIA_PLAYBACK:
-                        report_data_message.clear()
-                        continue_parsing = False
-
-                elif attribute_value := log_value_extractor.extract_from(tv_casting_line, 'Attribute ='):
-                    if attribute_value != ATTRIBUTE_CURRENT_PLAYBACK_STATE:
-                        report_data_message.clear()
-                        continue_parsing = False
-
-                elif 'InteractionModelRevision' in tv_casting_line:
-                    # Capture the closing brace `}` of the `ReportDataMessage` block.
-                    tv_casting_line = tv_casting_app_process.stdout.readline()
-                    linux_tv_casting_app_log_file.write(tv_casting_line)
-                    linux_tv_casting_app_log_file.flush()
-                    report_data_message.append(tv_casting_line.rstrip('\n'))
-                    return report_data_message
-
-
-def parse_tv_app_output_for_launchUrl_msg_success(tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Parse the Linux tv-app output for the relevant string indicating that the launchUrl was received."""
-
-    tv_app_process, linux_tv_app_log_file = tv_app_info
-
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time to parse the Linux tv-app output for the string related to the launchUrl.
-        if time.time() - start_wait_time > COMMISSIONING_STAGE_MAX_WAIT_SEC:
-            logging.error(
-                'The relevant launchUrl string was not found in the Linux tv-app process within the timeout.')
-            return False
-
-        tv_app_line = tv_app_process.stdout.readline()
-
-        if tv_app_line:
-            linux_tv_app_log_file.write(tv_app_line)
-            linux_tv_app_log_file.flush()
-
-            if 'ContentLauncherManager::HandleLaunchUrl TEST CASE ContentURL=https://www.test.com/videoid DisplayString=Test video' in tv_app_line:
-                logging.info('Found the launchUrl in the Linux tv-app output:')
-                logging.info(tv_app_line.rstrip('\n'))
-                return True
-
-
-def parse_tv_casting_app_output_for_launchUrl_msg_success(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Parse the Linux tv-casting-app output for relevant strings indicating that the launchUrl was sent."""
-
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-
-    continue_parsing_invoke_response_msg_block = False
-    found_example_data_msg = False
-    start_wait_time = time.time()
-
-    while True:
-        # Check if we exceeded the maximum wait time to parse the Linux tv-casting-app output for strings related to the launchUrl.
-        if time.time() - start_wait_time > TEST_LAUNCHURL_MAX_WAIT_SEC:
-            logging.error(
-                'The relevant launchUrl strings were not found in the Linux tv-casting-app process within the timeout.')
-            return False
-
-        tv_casting_line = tv_casting_app_process.stdout.readline()
-
-        if tv_casting_line:
-            linux_tv_casting_app_log_file.write(tv_casting_line)
-            linux_tv_casting_app_log_file.flush()
-
-            if 'InvokeResponseMessage =' in tv_casting_line:
-                logging.info('Found the `InvokeResponseMessage` block in the Linux tv-casting-app output:')
-                logging.info(tv_casting_line.rstrip('\n'))
-                continue_parsing_invoke_response_msg_block = True
-
-            elif continue_parsing_invoke_response_msg_block:
-                # Sanity check for `exampleData` in the `InvokeResponseMessage` block.
-                if 'exampleData' in tv_casting_line:
-                    found_example_data_msg = True
-
-                elif 'Received Command Response Data' in tv_casting_line:
-                    if not found_example_data_msg:
-                        logging.error('The `exampleData` string was not found in the `InvokeResponseMessage` block.')
-                        return False
-
-                    logging.info('Found the `Received Command Response Data` string in the Linux tv-casting-app output:')
-                    logging.info(tv_casting_line.rstrip('\n'))
-                    return True
-
-                logging.info(tv_casting_line.rstrip('\n'))
-
-
-def test_discovery_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]) -> Optional[str]:
-    """Parse the output of the Linux tv-casting-app to find a valid commissioner."""
-    tv_casting_app_process, linux_tv_casting_app_log_file = tv_casting_app_info
-
-    valid_discovered_commissioner = None
-    valid_vendor_id = None
-    valid_product_id = None
-    valid_device_type = None
-
-    # Read the output as we receive it from the tv-casting-app subprocess.
-    for line in tv_casting_app_process.stdout:
-        linux_tv_casting_app_log_file.write(line)
-        linux_tv_casting_app_log_file.flush()
-
-        # Fail fast if "No commissioner discovered" string found.
-        if 'No commissioner discovered' in line:
-            logging.error(line.rstrip('\n'))
-            handle_casting_failure('Discovery', log_paths)
-
-        elif 'Discovered Commissioner' in line:
-            valid_discovered_commissioner = line.rstrip('\n')
-
-        elif valid_discovered_commissioner:
-            # Continue parsing the output for the information of interest under 'Discovered Commissioner'
-            if 'Vendor ID:' in line:
-                valid_vendor_id = validate_value('Discovery', VENDOR_ID, log_paths, line, 'Vendor ID')
-
-            elif 'Product ID:' in line:
-                valid_product_id = validate_value('Discovery', PRODUCT_ID, log_paths, line, 'Product ID')
-
-            elif 'Device Type:' in line:
-                valid_device_type = validate_value('Discovery', DEVICE_TYPE_CASTING_VIDEO_PLAYER, log_paths, line, 'Device Type')
-
-        # A valid commissioner has VENDOR_ID, PRODUCT_ID, and DEVICE TYPE in its list of entries.
-        if valid_vendor_id and valid_product_id and valid_device_type:
-            logging.info('Found a valid commissioner in the Linux tv-casting-app output:')
-            logging.info(valid_discovered_commissioner)
-            logging.info(valid_vendor_id)
-            logging.info(valid_product_id)
-            logging.info(valid_device_type)
-            logging.info('Discovery success!\n')
-            break
-
-    return valid_discovered_commissioner
-
-
-def test_commissioning_fn(valid_discovered_commissioner_number, tv_casting_app_info: Tuple[subprocess.Popen, TextIO], tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Test commissioning between Linux tv-casting-app and Linux tv-app."""
-
-    if not initiate_cast_request_success(tv_casting_app_info, valid_discovered_commissioner_number):
-        handle_casting_failure('Commissioning', log_paths)
-
-    # Extract the values from the 'Identification Declaration' block in the tv-casting-app output that we want to validate against.
-    expected_device_name, expected_vendor_id, expected_product_id = extract_device_info_from_tv_casting_app(
-        tv_casting_app_info, 'Commissioning', log_paths)
-
-    if not expected_device_name or not expected_vendor_id or not expected_product_id:
-        logging.error('There is an error with the expected device info values that were extracted from the `Identification Declaration` block.')
-        logging.error(
-            f'expected_device_name: {expected_device_name}, expected_vendor_id: {expected_vendor_id}, expected_product_id: {expected_product_id}')
-        handle_casting_failure('Commissioning', log_paths)
-
-    if not validate_identification_declaration_message_on_tv_app(tv_app_info, expected_device_name, expected_vendor_id, expected_product_id, log_paths):
-        handle_casting_failure('Commissioning', log_paths)
-
-    if not validate_tv_casting_request_approval(tv_app_info, log_paths):
-        handle_casting_failure('Commissioning', log_paths)
-
-    if not validate_commissioning_success(tv_casting_app_info, tv_app_info, log_paths):
-        handle_casting_failure('Commissioning', log_paths)
-
-
-def test_subscription_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Test the subscription state of the Linux tv-casting-app by validating the `ReportDataMessage` block."""
-
-    valid_report_data_msg = parse_tv_casting_app_for_report_data_msg(tv_casting_app_info, log_paths)
-
-    if valid_report_data_msg:
-        logging.info('Found the `ReportDataMessage` block in the Linux tv-casting-app output:')
-
-        for line in valid_report_data_msg:
-            logging.info(line)
-
-        logging.info('Testing subscription success!\n')
-        valid_report_data_msg.clear()
-    else:
-        handle_casting_failure('Testing subscription', log_paths)
-
-
-def test_launchUrl_fn(tv_casting_app_info: Tuple[subprocess.Popen, TextIO], tv_app_info: Tuple[subprocess.Popen, TextIO], log_paths: List[str]):
-    """Test that the Linux tv-casting-app sent the launchUrl and that the Linux tv-app received the launchUrl."""
-
-    if not parse_tv_app_output_for_launchUrl_msg_success(tv_app_info, log_paths):
-        handle_casting_failure('Testing launchUrl', log_paths)
-
-    if not parse_tv_casting_app_output_for_launchUrl_msg_success(tv_casting_app_info, log_paths):
-        handle_casting_failure('Testing launchUrl', log_paths)
-
-    logging.info('Testing launchUrl success!')
+        current_index += 1
 
 
 @click.command()
 @click.option('--tv-app-rel-path', type=str, default='out/tv-app/chip-tv-app', help='Path to the Linux tv-app executable.')
 @click.option('--tv-casting-app-rel-path', type=str, default='out/tv-casting-app/chip-tv-casting-app', help='Path to the Linux tv-casting-app executable.')
 def test_casting_fn(tv_app_rel_path, tv_casting_app_rel_path):
-    """Test if the Linux tv-casting-app is able to discover and commission the Linux tv-app as part of casting.
+    """Test if the casting experience between the Linux tv-casting-app and the Linux tv-app continues to work.
 
     Default paths for the executables are provided but can be overridden via command line arguments.
     For example: python3 run_tv_casting_test.py --tv-app-rel-path=path/to/tv-app
                  --tv-casting-app-rel-path=path/to/tv-casting-app
     """
+
     # Store the log files to a temporary directory.
     with tempfile.TemporaryDirectory() as temp_dir:
         linux_tv_app_log_path = os.path.join(temp_dir, LINUX_TV_APP_LOGS)
@@ -615,41 +274,65 @@
 
         with open(linux_tv_app_log_path, 'w') as linux_tv_app_log_file, open(linux_tv_casting_app_log_path, 'w') as linux_tv_casting_app_log_file:
 
+            # Get all the test sequences.
+            test_sequences = Sequence.get_test_sequences()
+
+            # Get the test sequence of interest.
+            test_sequence = Sequence.get_test_sequence_by_name(test_sequences, 'commissionee_generated_passcode_test')
+
+            if not test_sequence:
+                logging.error('No test sequence found by the test sequence name provided.')
+                handle_casting_failure(None, [])
+
+            # At this point, we have retrieved the test sequence of interest.
+            test_sequence_name = test_sequence.name
+            test_sequence_steps = test_sequence.steps
+
             # Configure command options to disable stdout buffering during tests.
             disable_stdout_buffering_cmd = []
             # On Unix-like systems, use stdbuf to disable stdout buffering.
             if sys.platform == 'darwin' or sys.platform == 'linux':
                 disable_stdout_buffering_cmd = ['stdbuf', '-o0', '-i0']
 
+            current_index = 0
+            if test_sequence_steps[current_index].input_cmd != START_APP:
+                raise ValueError(
+                    f'{test_sequence_name}: The first step in the test sequence must contain `START_APP` as `input_cmd` to indicate starting the tv-app.')
+            elif test_sequence_steps[current_index].app != App.TV_APP:
+                raise ValueError(f'{test_sequence_name}: The first step in the test sequence must be to start up the tv-app.')
+            current_index += 1
+
             tv_app_abs_path = os.path.abspath(tv_app_rel_path)
             # Run the Linux tv-app subprocess.
             with ProcessManager(disable_stdout_buffering_cmd + [tv_app_abs_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as tv_app_process:
+                tv_app_info = (tv_app_process, linux_tv_app_log_file)
 
-                if not start_up_tv_app_success(tv_app_process, linux_tv_app_log_file):
-                    handle_casting_failure('Discovery', [linux_tv_app_log_path])
+                # Verify that the tv-app is up and running.
+                handle_output_msg(None, tv_app_info, [linux_tv_app_log_path],
+                                  test_sequence_name, test_sequence_steps[current_index])
+                current_index += 1
+
+                if test_sequence_steps[current_index].input_cmd != START_APP:
+                    raise ValueError(
+                        f'{test_sequence_name}: The third step in the test sequence must contain `START_APP` as `input_cmd` to indicate starting the tv-casting-app.')
+                elif test_sequence_steps[current_index].app != App.TV_CASTING_APP:
+                    raise ValueError(
+                        f'{test_sequence_name}: The third step in the test sequence must be to start up the tv-casting-app.')
+                current_index += 1
 
                 tv_casting_app_abs_path = os.path.abspath(tv_casting_app_rel_path)
                 # Run the Linux tv-casting-app subprocess.
                 with ProcessManager(disable_stdout_buffering_cmd + [tv_casting_app_abs_path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as tv_casting_app_process:
                     log_paths = [linux_tv_app_log_path, linux_tv_casting_app_log_path]
                     tv_casting_app_info = (tv_casting_app_process, linux_tv_casting_app_log_file)
-                    tv_app_info = (tv_app_process, linux_tv_app_log_file)
-                    valid_discovered_commissioner = test_discovery_fn(tv_casting_app_info, log_paths)
 
-                    if not valid_discovered_commissioner:
-                        handle_casting_failure('Discovery', log_paths)
+                    # Verify that the server initialization is completed in the tv-casting-app output.
+                    handle_output_msg(tv_casting_app_info, tv_app_info, log_paths,
+                                      test_sequence_name, test_sequence_steps[current_index])
+                    current_index += 1
 
-                    # We need the valid discovered commissioner number to continue with commissioning.
-                    log_value_extractor = LogValueExtractor('Commissioning', log_paths)
-                    valid_discovered_commissioner_number = log_value_extractor.extract_from(
-                        valid_discovered_commissioner, 'Discovered Commissioner #')
-                    if not valid_discovered_commissioner_number:
-                        logging.error(f'Failed to find `Discovered Commissioner #` in line: {valid_discovered_commissioner}')
-                        handle_casting_failure('Commissioning', log_paths)
-
-                    test_commissioning_fn(valid_discovered_commissioner_number, tv_casting_app_info, tv_app_info, log_paths)
-                    test_subscription_fn(tv_casting_app_info, log_paths)
-                    test_launchUrl_fn(tv_casting_app_info, tv_app_info, log_paths)
+                    run_test_sequence_steps(current_index, test_sequence_name, test_sequence_steps,
+                                            tv_casting_app_info, tv_app_info, log_paths)
 
 
 if __name__ == '__main__':