Rename metadata_parser module to chip.testing (#35615)

* Rename metadata_parser module to chip.testing

* MCORE_FS_1_4: Use Subprocess from chip.testing

* MCORE_FS_1_3: Use Subprocess from chip.testing

* Cleanup unused arguments

* Restyled by isort

* Fix test case to work on CI
diff --git a/BUILD.gn b/BUILD.gn
index 9a0654a..e05f143 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -81,7 +81,7 @@
     "//examples/common/pigweed/rpc_console/py:chip_rpc",
     "//integrations/mobly:chip_mobly",
     "//scripts/py_matter_yamltests:matter_yamltests",
-    "//src/python_testing/matter_testing_infrastructure:metadata_parser",
+    "//src/python_testing/matter_testing_infrastructure:chip-testing",
   ]
 
   pw_python_venv("matter_build_venv") {
@@ -122,7 +122,7 @@
       deps = [
         "${chip_root}/scripts:matter_yamltests_distribution.wheel",
         "${chip_root}/src/controller/python:chip-repl",
-        "${chip_root}/src/python_testing/matter_testing_infrastructure:metadata_parser.wheel",
+        "${chip_root}/src/python_testing/matter_testing_infrastructure:chip-testing.wheel",
       ]
     }
   }
@@ -249,7 +249,7 @@
         "//scripts/py_matter_idl:matter_idl.tests",
         "//scripts/py_matter_yamltests:matter_yamltests.tests",
         "//src:tests_run",
-        "//src/python_testing/matter_testing_infrastructure:metadata_parser.tests",
+        "//src/python_testing/matter_testing_infrastructure:chip-testing.tests",
       ]
 
       if (current_os == "linux" || current_os == "mac") {
diff --git a/examples/fabric-admin/scripts/fabric-sync-app.py b/examples/fabric-admin/scripts/fabric-sync-app.py
index 78e8f84..3967f97 100755
--- a/examples/fabric-admin/scripts/fabric-sync-app.py
+++ b/examples/fabric-admin/scripts/fabric-sync-app.py
@@ -79,12 +79,11 @@
 
 class Subprocess:
 
-    def __init__(self, tag: str, program: str, *args, stdout_cb=None):
+    def __init__(self, tag: str, program: str, *args):
         self.event = asyncio.Event()
         self.tag = tag.encode()
         self.program = program
         self.args = args
-        self.stdout_cb = stdout_cb
         self.expected_output = None
 
     def _check_output(self, line: bytes):
@@ -122,8 +121,7 @@
         self.p.terminate()
 
 
-async def run_admin(program, stdout_cb=None, storage_dir=None,
-                    rpc_admin_port=None, rpc_bridge_port=None,
+async def run_admin(program, storage_dir=None, rpc_admin_port=None, rpc_bridge_port=None,
                     paa_trust_store_path=None, commissioner_name=None,
                     commissioner_node_id=None, commissioner_vendor_id=None):
     args = []
@@ -141,8 +139,7 @@
         args.extend(["--commissioner-nodeid", str(commissioner_node_id)])
     if commissioner_vendor_id is not None:
         args.extend(["--commissioner-vendor-id", str(commissioner_vendor_id)])
-    p = Subprocess("[FS-ADMIN]", program, "interactive", "start", *args,
-                   stdout_cb=stdout_cb)
+    p = Subprocess("[FS-ADMIN]", program, "interactive", "start", *args)
     await p.run()
     return p
 
diff --git a/scripts/build_python.sh b/scripts/build_python.sh
index eaf1ad9..da3fe0c 100755
--- a/scripts/build_python.sh
+++ b/scripts/build_python.sh
@@ -198,7 +198,7 @@
 WHEEL=("$OUTPUT_ROOT"/controller/python/chip*.whl)
 
 # Add the matter_testing_infrastructure wheel
-WHEEL+=("$OUTPUT_ROOT"/python/obj/src/python_testing/matter_testing_infrastructure/metadata_parser._build_wheel/metadata_parser-*.whl)
+WHEEL+=("$OUTPUT_ROOT"/python/obj/src/python_testing/matter_testing_infrastructure/chip-testing._build_wheel/chip_testing-*.whl)
 
 if [ -n "$extra_packages" ]; then
     WHEEL+=("$extra_packages")
diff --git a/scripts/tests/run_python_test.py b/scripts/tests/run_python_test.py
index eb8f063..6474819 100755
--- a/scripts/tests/run_python_test.py
+++ b/scripts/tests/run_python_test.py
@@ -31,8 +31,8 @@
 
 import click
 import coloredlogs
+from chip.testing.metadata import Metadata, MetadataReader
 from colorama import Fore, Style
-from metadata_parser.metadata import Metadata, MetadataReader
 
 DEFAULT_CHIP_ROOT = os.path.abspath(
     os.path.join(os.path.dirname(__file__), '..', '..'))
diff --git a/src/python_testing/TC_MCORE_FS_1_3.py b/src/python_testing/TC_MCORE_FS_1_3.py
index 2a21c97..1270b56 100644
--- a/src/python_testing/TC_MCORE_FS_1_3.py
+++ b/src/python_testing/TC_MCORE_FS_1_3.py
@@ -36,67 +36,33 @@
 import logging
 import os
 import random
-import subprocess
-import sys
 import tempfile
-import threading
 
 import chip.clusters as Clusters
 from chip import ChipDeviceCtrl
 from chip.interaction_model import Status
+from chip.testing.tasks import Subprocess
 from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, type_matches
 from mobly import asserts
 
 
-# TODO: Make this class more generic. Issue #35348
-class Subprocess(threading.Thread):
-
-    def __init__(self, args: list = [], tag="", **kw):
-        super().__init__(**kw)
-        self.tag = f"[{tag}] " if tag else ""
-        self.args = args
-
-    def forward_f(self, f_in, f_out):
-        while True:
-            line = f_in.readline()
-            if not line:
-                break
-            f_out.write(f"{self.tag}{line}")
-            f_out.flush()
-
-    def run(self):
-        logging.info("RUN: %s", " ".join(self.args))
-        self.p = subprocess.Popen(self.args, errors="ignore", stdin=subprocess.PIPE,
-                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        # Forward stdout and stderr with a tag attached.
-        t1 = threading.Thread(target=self.forward_f, args=[self.p.stdout, sys.stdout])
-        t1.start()
-        t2 = threading.Thread(target=self.forward_f, args=[self.p.stderr, sys.stderr])
-        t2.start()
-        # Wait for the process to finish.
-        self.p.wait()
-        t1.join()
-        t2.join()
-
-    def stop(self):
-        self.p.terminate()
-        self.join()
-
-
 class AppServer:
 
     def __init__(self, app, storage_dir, port=None, discriminator=None, passcode=None):
-
-        args = [app]
-        args.extend(["--KVS", tempfile.mkstemp(dir=storage_dir, prefix="kvs-app-")[1]])
+        args = [
+            "--KVS", tempfile.mkstemp(dir=storage_dir, prefix="kvs-app-")[1],
+        ]
         args.extend(['--secured-device-port', str(port)])
         args.extend(["--discriminator", str(discriminator)])
         args.extend(["--passcode", str(passcode)])
-        self.app = Subprocess(args, tag="SERVER")
-        self.app.start()
+        self.app = Subprocess(app, *args, prefix="[SERVER]")
 
-    def stop(self):
-        self.app.stop()
+    def start(self):
+        # Start process and block until it prints the expected output.
+        self.app.start(expected_output="Server initialization complete")
+
+    def terminate(self):
+        self.app.terminate()
 
 
 class TC_MCORE_FS_1_3(MatterBaseTest):
@@ -134,10 +100,11 @@
             port=self.th_server_port,
             discriminator=self.th_server_discriminator,
             passcode=self.th_server_passcode)
+        self.th_server.start()
 
     def teardown_class(self):
         if self.th_server is not None:
-            self.th_server.stop()
+            self.th_server.terminate()
         if self.storage is not None:
             self.storage.cleanup()
         super().teardown_class()
diff --git a/src/python_testing/TC_MCORE_FS_1_4.py b/src/python_testing/TC_MCORE_FS_1_4.py
index c776bd2..7b101bb 100644
--- a/src/python_testing/TC_MCORE_FS_1_4.py
+++ b/src/python_testing/TC_MCORE_FS_1_4.py
@@ -36,86 +36,32 @@
 import logging
 import os
 import random
-import subprocess
-import sys
 import tempfile
-import threading
 
 import chip.clusters as Clusters
 from chip import ChipDeviceCtrl
 from chip.interaction_model import Status
+from chip.testing.tasks import Subprocess
 from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main, type_matches
 from mobly import asserts
 
-# TODO: Make this class more generic. Issue #35348
-
-
-class Subprocess(threading.Thread):
-
-    def __init__(self, args: list = [], stdout_cb=None, tag="", **kw):
-        super().__init__(**kw)
-        self.tag = f"[{tag}] " if tag else ""
-        self.stdout_cb = stdout_cb
-        self.args = args
-
-    def forward_f(self, f_in, f_out):
-        while True:
-            line = f_in.readline()
-            if not line:
-                break
-            f_out.write(f"{self.tag}{line}")
-            f_out.flush()
-            if self.stdout_cb is not None:
-                self.stdout_cb(line)
-
-    def run(self):
-        logging.info("RUN: %s", " ".join(self.args))
-        self.p = subprocess.Popen(self.args, errors="ignore", stdin=subprocess.PIPE,
-                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        # Forward stdout and stderr with a tag attached.
-        forwarding_stdout_thread = threading.Thread(target=self.forward_f, args=[self.p.stdout, sys.stdout])
-        forwarding_stdout_thread.start()
-        forwarding_stderr_thread = threading.Thread(target=self.forward_f, args=[self.p.stderr, sys.stderr])
-        forwarding_stderr_thread.start()
-        # Wait for the process to finish.
-        self.p.wait()
-        forwarding_stdout_thread.join()
-        forwarding_stderr_thread.join()
-
-    def stop(self):
-        self.p.terminate()
-        self.join()
-
 
 class FabricSyncApp:
 
-    def _process_admin_output(self, line):
-        if self.wait_for_text_text is not None and self.wait_for_text_text in line:
-            self.wait_for_text_event.set()
-
-    def wait_for_text(self, timeout=30):
-        if not self.wait_for_text_event.wait(timeout=timeout):
-            raise Exception(f"Timeout waiting for text: {self.wait_for_text_text}")
-        self.wait_for_text_event.clear()
-        self.wait_for_text_text = None
-
     def __init__(self, fabric_sync_app_path, fabric_admin_app_path, fabric_bridge_app_path,
                  storage_dir, fabric_name=None, node_id=None, vendor_id=None,
                  paa_trust_store_path=None, bridge_port=None, bridge_discriminator=None,
                  bridge_passcode=None):
-
-        self.wait_for_text_event = threading.Event()
-        self.wait_for_text_text = None
-
-        args = [fabric_sync_app_path]
-        args.append(f"--app-admin={fabric_admin_app_path}")
-        args.append(f"--app-bridge={fabric_bridge_app_path}")
-        # Override default ports, so it will be possible to run
-        # our TH_FSA alongside the DUT_FSA during CI testing.
-        args.append("--app-admin-rpc-port=44000")
-        args.append("--app-bridge-rpc-port=44001")
-        # Keep the storage directory in a temporary location.
-        args.append(f"--storage-dir={storage_dir}")
+        args = [
+            f"--app-admin={fabric_admin_app_path}",
+            f"--app-bridge={fabric_bridge_app_path}",
+            # Override default ports, so it will be possible to run
+            # our TH_FSA alongside the DUT_FSA during CI testing.
+            "--app-admin-rpc-port=44000",
+            "--app-bridge-rpc-port=44001",
+            # Keep the storage directory in a temporary location.
+            f"--storage-dir={storage_dir}",
+        ]
         if paa_trust_store_path is not None:
             args.append(f"--paa-trust-store-path={paa_trust_store_path}")
         if fabric_name is not None:
@@ -127,55 +73,38 @@
         args.append(f"--discriminator={bridge_discriminator}")
         args.append(f"--passcode={bridge_passcode}")
 
-        self.fabric_sync_app = Subprocess(args, stdout_cb=self._process_admin_output)
-        self.wait_for_text_text = "Successfully opened pairing window on the device"
-        self.fabric_sync_app.start()
+        self.fabric_sync_app = Subprocess(fabric_sync_app_path, *args)
 
-        # Wait for the fabric-sync-app to be ready.
-        self.wait_for_text()
+    def start(self):
+        # Start process and block until it prints the expected output.
+        self.fabric_sync_app.start(expected_output="Successfully opened pairing window on the device")
+
+    def terminate(self):
+        self.fabric_sync_app.terminate()
 
     def commission_on_network(self, node_id, setup_pin_code=None, filter_type=None, filter=None):
-        self.wait_for_text_text = f"Commissioning complete for node ID {node_id:#018x}: success"
-        # Send the commissioning command to the admin.
-        self.fabric_sync_app.p.stdin.write(f"pairing onnetwork {node_id} {setup_pin_code}\n")
-        self.fabric_sync_app.p.stdin.flush()
-        # Wait for success message.
-        self.wait_for_text()
-
-    def stop(self):
-        self.fabric_sync_app.stop()
+        self.fabric_sync_app.send(
+            f"pairing onnetwork {node_id} {setup_pin_code}",
+            expected_output=f"Commissioning complete for node ID {node_id:#018x}: success")
 
 
 class AppServer:
 
-    def _process_admin_output(self, line):
-        if self.wait_for_text_text is not None and self.wait_for_text_text in line:
-            self.wait_for_text_event.set()
-
-    def wait_for_text(self, timeout=30):
-        if not self.wait_for_text_event.wait(timeout=timeout):
-            raise Exception(f"Timeout waiting for text: {self.wait_for_text_text}")
-        self.wait_for_text_event.clear()
-        self.wait_for_text_text = None
-
     def __init__(self, app, storage_dir, port=None, discriminator=None, passcode=None):
-        self.wait_for_text_event = threading.Event()
-        self.wait_for_text_text = None
-
-        args = [app]
-        args.extend(["--KVS", tempfile.mkstemp(dir=storage_dir, prefix="kvs-app-")[1]])
+        args = [
+            "--KVS", tempfile.mkstemp(dir=storage_dir, prefix="kvs-app-")[1],
+        ]
         args.extend(['--secured-device-port', str(port)])
         args.extend(["--discriminator", str(discriminator)])
         args.extend(["--passcode", str(passcode)])
-        self.app = Subprocess(args, stdout_cb=self._process_admin_output, tag="SERVER")
-        self.wait_for_text_text = "Server initialization complete"
-        self.app.start()
+        self.app = Subprocess(app, *args, prefix="[SERVER]")
 
-        # Wait for the server-app to be ready.
-        self.wait_for_text()
+    def start(self):
+        # Start process and block until it prints the expected output.
+        self.app.start(expected_output="Server initialization complete")
 
-    def stop(self):
-        self.app.stop()
+    def terminate(self):
+        self.app.terminate()
 
 
 class TC_MCORE_FS_1_4(MatterBaseTest):
@@ -237,6 +166,7 @@
             bridge_discriminator=self.th_fsa_bridge_discriminator,
             bridge_passcode=self.th_fsa_bridge_passcode,
             vendor_id=0xFFF1)
+        self.th_fsa_controller.start()
 
         # Get the named pipe path for the DUT_FSA app input from the user params.
         dut_fsa_stdin_pipe = self.user_params.get("dut_fsa_stdin_pipe", None)
@@ -254,12 +184,13 @@
             port=self.th_server_port,
             discriminator=self.th_server_discriminator,
             passcode=self.th_server_passcode)
+        self.th_server.start()
 
     def teardown_class(self):
         if self.th_fsa_controller is not None:
-            self.th_fsa_controller.stop()
+            self.th_fsa_controller.terminate()
         if self.th_server is not None:
-            self.th_server.stop()
+            self.th_server.terminate()
         if self.storage is not None:
             self.storage.cleanup()
         super().teardown_class()
diff --git a/src/python_testing/matter_testing_infrastructure/BUILD.gn b/src/python_testing/matter_testing_infrastructure/BUILD.gn
index f972040..c8d54fb 100644
--- a/src/python_testing/matter_testing_infrastructure/BUILD.gn
+++ b/src/python_testing/matter_testing_infrastructure/BUILD.gn
@@ -18,7 +18,8 @@
 import("//build_overrides/pigweed.gni")
 import("$dir_pw_build/python.gni")
 
-pw_python_package("metadata_parser") {
+# Python package for CHIP testing support.
+pw_python_package("chip-testing") {
   setup = [
     "setup.py",
     "setup.cfg",
@@ -28,9 +29,13 @@
   inputs = [ "env_test.yaml" ]
 
   sources = [
-    "metadata_parser/__init__.py",
-    "metadata_parser/metadata.py",
+    "chip/testing/__init__.py",
+    "chip/testing/metadata.py",
+    "chip/testing/tasks.py",
   ]
 
-  tests = [ "metadata_parser/test_metadata.py" ]
+  tests = [
+    "chip/testing/test_metadata.py",
+    "chip/testing/test_tasks.py",
+  ]
 }
diff --git a/src/python_testing/matter_testing_infrastructure/metadata_parser/__init__.py b/src/python_testing/matter_testing_infrastructure/chip/testing/__init__.py
similarity index 100%
rename from src/python_testing/matter_testing_infrastructure/metadata_parser/__init__.py
rename to src/python_testing/matter_testing_infrastructure/chip/testing/__init__.py
diff --git a/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py b/src/python_testing/matter_testing_infrastructure/chip/testing/metadata.py
similarity index 99%
rename from src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py
rename to src/python_testing/matter_testing_infrastructure/chip/testing/metadata.py
index f16d3e9..aa829c4 100644
--- a/src/python_testing/matter_testing_infrastructure/metadata_parser/metadata.py
+++ b/src/python_testing/matter_testing_infrastructure/chip/testing/metadata.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python3
 # Copyright (c) 2024 Project CHIP Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/tasks.py b/src/python_testing/matter_testing_infrastructure/chip/testing/tasks.py
new file mode 100644
index 0000000..873cec5
--- /dev/null
+++ b/src/python_testing/matter_testing_infrastructure/chip/testing/tasks.py
@@ -0,0 +1,124 @@
+# 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.
+
+import logging
+import subprocess
+import sys
+import threading
+import typing
+
+
+def forward_f(prefix: bytes,
+              f_in: typing.BinaryIO,
+              f_out: typing.BinaryIO,
+              cb: typing.Optional[typing.Callable[[bytes, bool], None]] = None,
+              is_stderr: bool = False):
+    """Forward f_in to f_out with a prefix attached.
+
+    This function can optionally feed received lines to a callback function.
+    """
+    while True:
+        line = f_in.readline()
+        if not line:
+            break
+        if cb is not None:
+            cb(line, is_stderr)
+        f_out.buffer.write(prefix)
+        f_out.buffer.write(line)
+        f_out.flush()
+
+
+class Subprocess(threading.Thread):
+    """Run a subprocess and optionally prefix its output."""
+
+    def __init__(self, program: str, *args: typing.List[str], prefix: str = "",
+                 output_cb: typing.Optional[typing.Callable[[bytes, bool], None]] = None):
+        """Initialize the subprocess.
+
+        Args:
+            program: The program to run.
+            args: The arguments to the program.
+            prefix: A prefix to attach to the output.
+            output_cb: A callback function to process the output. It should take two
+                arguments: the output line bytes and the boolean indicating if the
+                output comes from stderr.
+        """
+        super().__init__()
+        self.event = threading.Event()
+        self.prefix = prefix.encode()
+        self.program = program
+        self.args = args
+        self.output_cb = output_cb
+        self.expected_output = None
+
+    def _check_output(self, line: bytes, is_stderr: bool):
+        if self.output_cb is not None:
+            self.output_cb(line, is_stderr)
+        if self.expected_output is not None and self.expected_output in line:
+            self.event.set()
+
+    def run(self):
+        """Thread entry point."""
+
+        logging.info("RUN: %s %s", self.program, " ".join(self.args))
+        self.p = subprocess.Popen([self.program] + list(self.args),
+                                  stdin=subprocess.PIPE,
+                                  stdout=subprocess.PIPE,
+                                  stderr=subprocess.PIPE)
+
+        # Forward stdout and stderr with a tag attached.
+        forwarding_stdout_thread = threading.Thread(
+            target=forward_f,
+            args=(self.prefix, self.p.stdout, sys.stdout, self._check_output))
+        forwarding_stdout_thread.start()
+        forwarding_stderr_thread = threading.Thread(
+            target=forward_f,
+            args=(self.prefix, self.p.stderr, sys.stderr, self._check_output, True))
+        forwarding_stderr_thread.start()
+
+        # Wait for the process to finish.
+        self.p.wait()
+
+        forwarding_stdout_thread.join()
+        forwarding_stderr_thread.join()
+
+    def start(self, expected_output: str = None, timeout: float = None):
+        """Start a subprocess and optionally wait for a specific output."""
+        if expected_output is not None:
+            self.expected_output = expected_output.encode()
+            self.event.clear()
+        super().start()
+        if expected_output is not None:
+            if self.event.wait(timeout) is False:
+                raise TimeoutError("Expected output not found")
+            self.expected_output = None
+
+    def send(self, message: str, end: str = "\n",
+             expected_output: str = None, timeout: float = None):
+        """Send a message to a process and optionally wait for a response."""
+
+        if expected_output is not None:
+            self.expected_output = expected_output.encode()
+            self.event.clear()
+
+        self.p.stdin.write((message + end).encode())
+        self.p.stdin.flush()
+
+        if expected_output is not None:
+            if self.event.wait(timeout) is False:
+                raise TimeoutError("Expected output not found")
+            self.expected_output = None
+
+    def terminate(self):
+        self.p.terminate()
diff --git a/src/python_testing/matter_testing_infrastructure/metadata_parser/test_metadata.py b/src/python_testing/matter_testing_infrastructure/chip/testing/test_metadata.py
similarity index 100%
rename from src/python_testing/matter_testing_infrastructure/metadata_parser/test_metadata.py
rename to src/python_testing/matter_testing_infrastructure/chip/testing/test_metadata.py
diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/test_tasks.py b/src/python_testing/matter_testing_infrastructure/chip/testing/test_tasks.py
new file mode 100644
index 0000000..5e91a89
--- /dev/null
+++ b/src/python_testing/matter_testing_infrastructure/chip/testing/test_tasks.py
@@ -0,0 +1,35 @@
+# 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.
+
+import unittest
+
+from tasks import Subprocess
+
+
+class TestSubprocess(unittest.TestCase):
+
+    def test_expected_output(self):
+        p = Subprocess("python3", "-c", "print('Hello, World!')")
+        p.start(expected_output="Hello, World!", timeout=1)
+        p.terminate()
+
+    def test_expected_output_timeout(self):
+        p = Subprocess("python3", "--version")
+        with self.assertRaises(TimeoutError):
+            p.start(expected_output="Python 1.0.0", timeout=1)
+        p.terminate()
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/src/python_testing/matter_testing_infrastructure/setup.cfg b/src/python_testing/matter_testing_infrastructure/setup.cfg
index d1cbadf..862236a 100644
--- a/src/python_testing/matter_testing_infrastructure/setup.cfg
+++ b/src/python_testing/matter_testing_infrastructure/setup.cfg
@@ -13,7 +13,9 @@
 # limitations under the License.
 
 [metadata]
-name = metadata_parser
-version = 0.0.1
+name = chip-testing
+version = 1.0.0
 author = Project CHIP Authors
-description = Scripts to get metadata (runner arguments) associated with the python_testing scripts
\ No newline at end of file
+license-expression = Apache-2.0
+description = Various helpers associated with the python_testing scripts
+url = https://github.com/project-chip/connectedhomeip
diff --git a/src/python_testing/matter_testing_infrastructure/setup.py b/src/python_testing/matter_testing_infrastructure/setup.py
index 8a92fbd..ab0a7f3 100644
--- a/src/python_testing/matter_testing_infrastructure/setup.py
+++ b/src/python_testing/matter_testing_infrastructure/setup.py
@@ -12,9 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-"""The metadata_parser package."""
-
 import setuptools  # type: ignore
 
 setuptools.setup()  # Package definition in setup.cfg