From 36b5acbeb0e3d2f57810177878cb1714f5316e2c Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 12:37:37 -0500
Subject: [PATCH 01/12] PYTHON-5207 Convert mod_wsgi tests to use the new test
 runner

---
 .evergreen/config.yml                    | 45 ------------
 .evergreen/generated_configs/tasks.yml   | 42 +++++++++++
 .evergreen/scripts/generate_config.py    | 18 +++++
 .evergreen/scripts/mod_wsgi_tester.py    | 91 ++++++++++++++++++++++++
 .evergreen/scripts/run-mod-wsgi-tests.sh | 53 --------------
 .evergreen/scripts/run_tests.py          |  7 ++
 .evergreen/scripts/setup_tests.py        |  5 ++
 .evergreen/scripts/teardown_tests.py     |  6 ++
 .evergreen/scripts/utils.py              |  2 +-
 CONTRIBUTING.md                          | 11 +++
 test/mod_wsgi_test/test_client.py        |  4 +-
 11 files changed, 183 insertions(+), 101 deletions(-)
 create mode 100644 .evergreen/scripts/mod_wsgi_tester.py
 delete mode 100755 .evergreen/scripts/run-mod-wsgi-tests.sh

diff --git a/.evergreen/config.yml b/.evergreen/config.yml
index 4562f1d2be..e5b640a890 100644
--- a/.evergreen/config.yml
+++ b/.evergreen/config.yml
@@ -227,17 +227,6 @@ functions:
         args:
           - ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-image.sh
 
-  "run mod_wsgi tests":
-    - command: subprocess.exec
-      type: test
-      params:
-        include_expansions_in_env: [MOD_WSGI_VERSION, MOD_WSGI_EMBEDDED, "PYTHON_BINARY"]
-        working_dir: "src"
-        binary: bash
-        args:
-          - .evergreen/scripts/run-with-env.sh
-          - .evergreen/scripts/run-mod-wsgi-tests.sh
-
   "run doctests":
      - command: subprocess.exec
        type: test
@@ -411,40 +400,6 @@ tasks:
             TEST_NAME: index_management
             AUTH: "auth"
 
-    - name: "mod-wsgi-standalone"
-      tags: ["mod_wsgi"]
-      commands:
-        - func: "run server"
-          vars:
-            TOPOLOGY: "server"
-        - func: "run mod_wsgi tests"
-
-    - name: "mod-wsgi-replica-set"
-      tags: ["mod_wsgi"]
-      commands:
-        - func: "run server"
-          vars:
-            TOPOLOGY: "replica_set"
-        - func: "run mod_wsgi tests"
-
-    - name: "mod-wsgi-embedded-mode-standalone"
-      tags: ["mod_wsgi"]
-      commands:
-        - func: "run server"
-        - func: "run mod_wsgi tests"
-          vars:
-            MOD_WSGI_EMBEDDED: "1"
-
-    - name: "mod-wsgi-embedded-mode-replica-set"
-      tags: ["mod_wsgi"]
-      commands:
-        - func: "run server"
-          vars:
-            TOPOLOGY: "replica_set"
-        - func: "run mod_wsgi tests"
-          vars:
-            MOD_WSGI_EMBEDDED: "1"
-
     - name: "no-server"
       tags: ["no-server"]
       commands:
diff --git a/.evergreen/generated_configs/tasks.yml b/.evergreen/generated_configs/tasks.yml
index 0b0f09329a..bfc02f04d6 100644
--- a/.evergreen/generated_configs/tasks.yml
+++ b/.evergreen/generated_configs/tasks.yml
@@ -775,6 +775,48 @@ tasks:
           TEST_NAME: load_balancer
     tags: [load-balancer, noauth, nossl]
 
+  # Mod wsgi tests
+  - name: mod-wsgi-standalone
+    commands:
+      - func: run server
+        vars:
+          TOPOLOGY: standalone
+      - func: run tests
+        vars:
+          TEST_NAME: mod_wsgi
+          SUB_TEST_NAME: standalone
+    tags: [mod_wsgi]
+  - name: mod-wsgi-replica-set
+    commands:
+      - func: run server
+        vars:
+          TOPOLOGY: replica-set
+      - func: run tests
+        vars:
+          TEST_NAME: mod_wsgi
+          SUB_TEST_NAME: standalone
+    tags: [mod_wsgi]
+  - name: mod-wsgi-embedded-modestandalone
+    commands:
+      - func: run server
+        vars:
+          TOPOLOGY: standalone
+      - func: run tests
+        vars:
+          TEST_NAME: mod_wsgi
+          SUB_TEST_NAME: embedded-mode
+    tags: [mod_wsgi]
+  - name: mod-wsgi-embedded-modereplica-set
+    commands:
+      - func: run server
+        vars:
+          TOPOLOGY: replica-set
+      - func: run tests
+        vars:
+          TEST_NAME: mod_wsgi
+          SUB_TEST_NAME: embedded-mode
+    tags: [mod_wsgi]
+
   # Ocsp tests
   - name: test-ocsp-ecdsa-valid-cert-server-does-not-staple
     commands:
diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py
index d91e0e6ded..5878c6b1f4 100644
--- a/.evergreen/scripts/generate_config.py
+++ b/.evergreen/scripts/generate_config.py
@@ -892,6 +892,24 @@ def create_oidc_tasks():
     return tasks
 
 
+def create_mod_wsgi_tasks():
+    tasks = []
+    for test, topology in product(["standalone", "embedded-mode"], ["standalone", "replica-set"]):
+        if test == "standalone":
+            task_name = "mod-wsgi-"
+        else:
+            task_name = "mod-wsgi-embedded-mode"
+        task_name += topology
+        server_vars = dict(TOPOLOGY=topology)
+        server_func = FunctionCall(func="run server", vars=server_vars)
+        vars = dict(TEST_NAME="mod_wsgi", SUB_TEST_NAME=test)
+        test_func = FunctionCall(func="run tests", vars=vars)
+        tags = ["mod_wsgi"]
+        commands = [server_func, test_func]
+        tasks.append(EvgTask(name=task_name, tags=tags, commands=commands))
+    return tasks
+
+
 def _create_ocsp_task(algo, variant, server_type, base_task_name):
     file_name = f"{algo}-basic-tls-ocsp-{variant}.json"
 
diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
new file mode 100644
index 0000000000..ded43592a5
--- /dev/null
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -0,0 +1,91 @@
+from __future__ import annotations
+
+import os
+import sys
+import time
+import urllib.error
+import urllib.request
+from pathlib import Path
+from shutil import which
+
+from utils import LOGGER, ROOT, run_command, write_env
+
+
+def make_request(url, timeout=10):
+    for _ in range(int(timeout)):
+        try:
+            urllib.request.urlopen(url)  # noqa: S310
+            return
+        except urllib.error.HTTPError:
+            pass
+        time.sleep(1)
+    raise TimeoutError(f"Failed to access {url}")
+
+
+def setup_mod_wsgi(sub_test_name: str) -> None:
+    env = os.environ.copy()
+    if sub_test_name == "embedded":
+        env["MOD_WSGI_CONF"] = "mod_wsgi_test_embedded.conf"
+    elif sub_test_name == "standalone":
+        env["MOD_WSGI_CONF"] = "mod_wsgi_test.conf"
+    else:
+        raise ValueError("mod_wsgi sub test must be either 'standalone' or 'embedded'")
+    apache = which("apache")
+    if not apache and Path("/usr/lib/apache2/mpm-prefork/apache2").exists():
+        apache = "/usr/lib/apache2/mpm-prefork/apache2"
+    if apache:
+        apache_config = "apache24ubuntu161404.conf"
+    else:
+        apache = which("httpd")
+        if not apache:
+            raise ValueError("Could not find apache2 or httpd")
+        apache_config = "apache22amazon.conf"
+    python_version = ".".join(str(val) for val in sys.version_info[:2])
+    mod_wsgi_version = 4
+    env[
+        "MOD_WSGI_SO"
+    ] = f"/opt/python/mod_wsgi/python_version/{python_version}/mod_wsgi_version/{mod_wsgi_version}/mod_wsgi.so"
+    env["PYTHONHOME"] = f"/opt/python/{python_version}"
+    env["PROJECT_DIRECTORY"] = project_directory = str(ROOT)
+    write_env("APACHE", apache)
+    write_env("APACHE_CONFIG", apache_config)
+    uri1 = f"http://localhost:8080/interpreter1{project_directory}"
+    write_env("TEST_URI1", uri1)
+    uri2 = f"http://localhost:8080/interpreter2{project_directory}"
+    write_env("TEST_URI2", uri2)
+    run_command(f"{apache} -k start -f {ROOT}/test/mod_wsgi_test/{apache_config}")
+
+    # Wait for the endpoints to be available.
+    try:
+        make_request(uri1, 10)
+        make_request(uri2, 10)
+    except Exception as e:
+        LOGGER.error(Path("error_log").read_text())
+        raise e
+
+
+def test_mod_wsgi() -> None:
+    sys.path.insert(0, ROOT)
+    from test.mod_wsgi_test.test_client import main, parse_args
+
+    uri1 = os.environ["TEST_URI1"]
+    uri2 = os.environ["TEST_URI2"]
+    args = f"-n 25000 -t 100 parallel {uri1} {uri2}"
+    try:
+        main(*parse_args(args))
+
+        args = f"-n 25000 serial {uri1} {uri2}"
+        main(*parse_args(args))
+    except Exception as e:
+        LOGGER.error(Path("error_log").read_text())
+        raise e
+
+
+def teardown_mod_wsgi() -> None:
+    apache = os.environ["APACHE"]
+    apache_config = os.environ["APACHE_CONFIG"]
+    run_command(f"{apache} -k stop -f {ROOT}/test/mod_wsgi_test/{apache_config}")
+
+
+if __name__ == "__main__":
+    setup_mod_wsgi()
diff --git a/.evergreen/scripts/run-mod-wsgi-tests.sh b/.evergreen/scripts/run-mod-wsgi-tests.sh
deleted file mode 100755
index f59ace8116..0000000000
--- a/.evergreen/scripts/run-mod-wsgi-tests.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-set -o xtrace
-set -o errexit
-
-APACHE=$(command -v apache2 || command -v /usr/lib/apache2/mpm-prefork/apache2) || true
-if [ -n "$APACHE" ]; then
-    APACHE_CONFIG=apache24ubuntu161404.conf
-else
-    APACHE=$(command -v httpd) || true
-    if [ -z "$APACHE" ]; then
-        echo "Could not find apache2 binary"
-        exit 1
-    else
-        APACHE_CONFIG=apache22amazon.conf
-    fi
-fi
-
-
-PYTHON_VERSION=$(${PYTHON_BINARY} -c "import sys; sys.stdout.write('.'.join(str(val) for val in sys.version_info[:2]))")
-
-# Ensure the C extensions are installed.
-${PYTHON_BINARY} -m venv --system-site-packages .venv
-source .venv/bin/activate
-pip install -U pip
-export PYMONGO_C_EXT_MUST_BUILD=1
-python -m pip install -v -e .
-
-export MOD_WSGI_SO=/opt/python/mod_wsgi/python_version/$PYTHON_VERSION/mod_wsgi_version/$MOD_WSGI_VERSION/mod_wsgi.so
-export PYTHONHOME=/opt/python/$PYTHON_VERSION
-# If MOD_WSGI_EMBEDDED is set use the default embedded mode behavior instead
-# of daemon mode (WSGIDaemonProcess).
-if [ -n "${MOD_WSGI_EMBEDDED:-}" ]; then
-    export MOD_WSGI_CONF=mod_wsgi_test_embedded.conf
-else
-    export MOD_WSGI_CONF=mod_wsgi_test.conf
-fi
-
-cd ..
-$APACHE -k start -f ${PROJECT_DIRECTORY}/test/mod_wsgi_test/${APACHE_CONFIG}
-trap '$APACHE -k stop -f ${PROJECT_DIRECTORY}/test/mod_wsgi_test/${APACHE_CONFIG}' EXIT HUP
-
-wget -t 1 -T 10 -O - "http://localhost:8080/interpreter1${PROJECT_DIRECTORY}" || (cat error_log && exit 1)
-wget -t 1 -T 10 -O - "http://localhost:8080/interpreter2${PROJECT_DIRECTORY}" || (cat error_log && exit 1)
-
-python ${PROJECT_DIRECTORY}/test/mod_wsgi_test/test_client.py -n 25000 -t 100 parallel \
-    http://localhost:8080/interpreter1${PROJECT_DIRECTORY} http://localhost:8080/interpreter2${PROJECT_DIRECTORY} || \
-    (tail -n 100 error_log && exit 1)
-
-python ${PROJECT_DIRECTORY}/test/mod_wsgi_test/test_client.py -n 25000 serial \
-    http://localhost:8080/interpreter1${PROJECT_DIRECTORY} http://localhost:8080/interpreter2${PROJECT_DIRECTORY} || \
-    (tail -n 100 error_log && exit 1)
-
-rm -rf .venv
diff --git a/.evergreen/scripts/run_tests.py b/.evergreen/scripts/run_tests.py
index 38fd3c67cb..5e1e1486cb 100644
--- a/.evergreen/scripts/run_tests.py
+++ b/.evergreen/scripts/run_tests.py
@@ -100,6 +100,13 @@ def run() -> None:
     if TEST_PERF:
         start_time = datetime.now()
 
+    # Run mod_wsgi tests using the script.
+    if TEST_NAME == "mod_wsgi":
+        from mod_wsgi_tester import test_mod_wsgi
+
+        test_mod_wsgi()
+        return
+
     # Send kms tests to run remotely.
     if TEST_NAME == "kms" and SUB_TEST_NAME in ["azure", "gcp"]:
         from kms_tester import test_kms_send_to_remote
diff --git a/.evergreen/scripts/setup_tests.py b/.evergreen/scripts/setup_tests.py
index 868ac419b5..d8fefc8cfd 100644
--- a/.evergreen/scripts/setup_tests.py
+++ b/.evergreen/scripts/setup_tests.py
@@ -251,6 +251,11 @@ def handle_test_env() -> None:
         cmd = f'bash "{DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh" start'
         run_command(cmd)
 
+    if test_name == "mod_wsgi":
+        from mod_wsgi_tester import setup_mod_wsgi
+
+        setup_mod_wsgi(sub_test_name)
+
     if test_name == "ocsp":
         if sub_test_name:
             os.environ["OCSP_SERVER_TYPE"] = sub_test_name
diff --git a/.evergreen/scripts/teardown_tests.py b/.evergreen/scripts/teardown_tests.py
index 3920180422..750d2a0652 100644
--- a/.evergreen/scripts/teardown_tests.py
+++ b/.evergreen/scripts/teardown_tests.py
@@ -44,4 +44,10 @@
 elif TEST_NAME == "auth_aws" and sys.platform != "darwin":
     run_command(f"bash {DRIVERS_TOOLS}/.evergreen/auth_aws/teardown.sh")
 
+# Tear down mog_wsgi if applicable.
+elif TEST_NAME == "mod_wsgi":
+    from mod_wsgi_tester import teardown_mod_wsgi
+
+    teardown_mod_wsgi()
+
 LOGGER.info(f"Tearing down tests of type '{TEST_NAME}'... done.")
diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py
index 08d376461e..7831a208bc 100644
--- a/.evergreen/scripts/utils.py
+++ b/.evergreen/scripts/utils.py
@@ -50,7 +50,7 @@ class Distro:
 }
 
 # Tests that require a sub test suite.
-SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms"]
+SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms", "mod_wsgi"]
 
 
 def get_test_options(
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8844565d31..d2a833d874 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -275,6 +275,17 @@ Note: these tests can only be run from an Evergreen host.
 - Run `just setup-tests atlas_connect`.
 - Run `just run-tests`.
 
+### mod_wsgi tests
+
+Note: these tests can only be run from an Evergreen Linux host that has the Python toolchain.
+
+- Run `just run-server`.
+- Run `just setup-tests mod_wsgi <mode>`.
+- Run `just run-tests`.
+
+The `mode` can be `standalone` or `embedded`.  For the `replica_set` version of the tests, use
+`TOPOLOGY=replica_set just run-server`.
+
 ### OCSP tests
 
   - Export the orchestration file, e.g. `export ORCHESTRATION_FILE=rsa-basic-tls-ocsp-disableStapling.json`.
diff --git a/test/mod_wsgi_test/test_client.py b/test/mod_wsgi_test/test_client.py
index 88eeb7a57e..1ddb2dcc31 100644
--- a/test/mod_wsgi_test/test_client.py
+++ b/test/mod_wsgi_test/test_client.py
@@ -24,7 +24,7 @@
 from urllib.request import urlopen
 
 
-def parse_args():
+def parse_args(args=None):
     parser = OptionParser(
         """usage: %prog [options] mode url [<url2>...]
 
@@ -70,7 +70,7 @@ def parse_args():
     )
 
     try:
-        options, args = parser.parse_args()
+        options, args = parser.parse_args(args or sys.argv[1:])
         mode, urls = args[0], args[1:]
     except (ValueError, IndexError):
         parser.print_usage()

From 56e29824e3083761ac101ead12a2b38679af873c Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 13:53:34 -0500
Subject: [PATCH 02/12] clean up tasks

---
 .evergreen/generated_configs/tasks.yml    |  4 ++--
 .evergreen/generated_configs/variants.yml | 10 ++--------
 .evergreen/scripts/generate_config.py     |  9 ++-------
 3 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/.evergreen/generated_configs/tasks.yml b/.evergreen/generated_configs/tasks.yml
index bfc02f04d6..9e0585649f 100644
--- a/.evergreen/generated_configs/tasks.yml
+++ b/.evergreen/generated_configs/tasks.yml
@@ -796,7 +796,7 @@ tasks:
           TEST_NAME: mod_wsgi
           SUB_TEST_NAME: standalone
     tags: [mod_wsgi]
-  - name: mod-wsgi-embedded-modestandalone
+  - name: mod-wsgi-embedded-mode-standalone
     commands:
       - func: run server
         vars:
@@ -806,7 +806,7 @@ tasks:
           TEST_NAME: mod_wsgi
           SUB_TEST_NAME: embedded-mode
     tags: [mod_wsgi]
-  - name: mod-wsgi-embedded-modereplica-set
+  - name: mod-wsgi-embedded-mode-replica-set
     commands:
       - func: run server
         vars:
diff --git a/.evergreen/generated_configs/variants.yml b/.evergreen/generated_configs/variants.yml
index 4c54abf4b9..d70afa2bdd 100644
--- a/.evergreen/generated_configs/variants.yml
+++ b/.evergreen/generated_configs/variants.yml
@@ -696,10 +696,7 @@ buildvariants:
   # Mod wsgi tests
   - name: mod_wsgi-ubuntu-22-python3.9
     tasks:
-      - name: mod-wsgi-standalone
-      - name: mod-wsgi-replica-set
-      - name: mod-wsgi-embedded-mode-standalone
-      - name: mod-wsgi-embedded-mode-replica-set
+      - name: .mod_wsgi
     display_name: mod_wsgi Ubuntu-22 Python3.9
     run_on:
       - ubuntu2204-small
@@ -708,10 +705,7 @@ buildvariants:
       PYTHON_BINARY: /opt/python/3.9/bin/python3
   - name: mod_wsgi-ubuntu-22-python3.13
     tasks:
-      - name: mod-wsgi-standalone
-      - name: mod-wsgi-replica-set
-      - name: mod-wsgi-embedded-mode-standalone
-      - name: mod-wsgi-embedded-mode-replica-set
+      - name: .mod_wsgi
     display_name: mod_wsgi Ubuntu-22 Python3.13
     run_on:
       - ubuntu2204-small
diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py
index 5878c6b1f4..c6eba3857a 100644
--- a/.evergreen/scripts/generate_config.py
+++ b/.evergreen/scripts/generate_config.py
@@ -614,12 +614,7 @@ def create_atlas_data_lake_variants():
 def create_mod_wsgi_variants():
     variants = []
     host = HOSTS["ubuntu22"]
-    tasks = [
-        "mod-wsgi-standalone",
-        "mod-wsgi-replica-set",
-        "mod-wsgi-embedded-mode-standalone",
-        "mod-wsgi-embedded-mode-replica-set",
-    ]
+    tasks = [".mod_wsgi"]
     expansions = dict(MOD_WSGI_VERSION="4")
     for python in MIN_MAX_PYTHON:
         display_name = get_display_name("mod_wsgi", host, python=python)
@@ -898,7 +893,7 @@ def create_mod_wsgi_tasks():
         if test == "standalone":
             task_name = "mod-wsgi-"
         else:
-            task_name = "mod-wsgi-embedded-mode"
+            task_name = "mod-wsgi-embedded-mode-"
         task_name += topology
         server_vars = dict(TOPOLOGY=topology)
         server_func = FunctionCall(func="run server", vars=server_vars)

From 79028b60bec480273b5c9451437dfc21c8350125 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 13:59:28 -0500
Subject: [PATCH 03/12] clean up tasks

---
 .evergreen/generated_configs/tasks.yml | 4 ++--
 .evergreen/scripts/generate_config.py  | 4 ++--
 .evergreen/scripts/utils.py            | 4 +++-
 3 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/.evergreen/generated_configs/tasks.yml b/.evergreen/generated_configs/tasks.yml
index 9e0585649f..a632e4bb29 100644
--- a/.evergreen/generated_configs/tasks.yml
+++ b/.evergreen/generated_configs/tasks.yml
@@ -790,7 +790,7 @@ tasks:
     commands:
       - func: run server
         vars:
-          TOPOLOGY: replica-set
+          TOPOLOGY: replica_set
       - func: run tests
         vars:
           TEST_NAME: mod_wsgi
@@ -810,7 +810,7 @@ tasks:
     commands:
       - func: run server
         vars:
-          TOPOLOGY: replica-set
+          TOPOLOGY: replica_set
       - func: run tests
         vars:
           TEST_NAME: mod_wsgi
diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py
index c6eba3857a..94901d6551 100644
--- a/.evergreen/scripts/generate_config.py
+++ b/.evergreen/scripts/generate_config.py
@@ -889,12 +889,12 @@ def create_oidc_tasks():
 
 def create_mod_wsgi_tasks():
     tasks = []
-    for test, topology in product(["standalone", "embedded-mode"], ["standalone", "replica-set"]):
+    for test, topology in product(["standalone", "embedded-mode"], ["standalone", "replica_set"]):
         if test == "standalone":
             task_name = "mod-wsgi-"
         else:
             task_name = "mod-wsgi-embedded-mode-"
-        task_name += topology
+        task_name += topology.replace("_", "-")
         server_vars = dict(TOPOLOGY=topology)
         server_func = FunctionCall(func="run server", vars=server_vars)
         vars = dict(TEST_NAME="mod_wsgi", SUB_TEST_NAME=test)
diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py
index 7831a208bc..7a5f9b3e9f 100644
--- a/.evergreen/scripts/utils.py
+++ b/.evergreen/scripts/utils.py
@@ -52,6 +52,8 @@ class Distro:
 # Tests that require a sub test suite.
 SUB_TEST_REQUIRED = ["auth_aws", "auth_oidc", "kms", "mod_wsgi"]
 
+EXTRA_TESTS = ["mod_wsgi"]
+
 
 def get_test_options(
     description, require_sub_test_name=True, allow_extra_opts=False
@@ -62,7 +64,7 @@ def get_test_options(
     if require_sub_test_name:
         parser.add_argument(
             "test_name",
-            choices=sorted(TEST_SUITE_MAP),
+            choices=sorted(TEST_SUITE_MAP + EXTRA_TESTS),
             nargs="?",
             default="default",
             help="The optional name of the test suite to set up, typically the same name as a pytest marker.",

From 9d5f94bf350b2c36b129258a3c0d6c81e7fc8eb4 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 14:32:26 -0500
Subject: [PATCH 04/12] fix options logic

---
 .evergreen/scripts/utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.evergreen/scripts/utils.py b/.evergreen/scripts/utils.py
index 7a5f9b3e9f..45d01ae6bd 100644
--- a/.evergreen/scripts/utils.py
+++ b/.evergreen/scripts/utils.py
@@ -64,7 +64,7 @@ def get_test_options(
     if require_sub_test_name:
         parser.add_argument(
             "test_name",
-            choices=sorted(TEST_SUITE_MAP + EXTRA_TESTS),
+            choices=sorted(list(TEST_SUITE_MAP) + EXTRA_TESTS),
             nargs="?",
             default="default",
             help="The optional name of the test suite to set up, typically the same name as a pytest marker.",

From 77c85e9c56035866873a767ce8f0590c6a5e8f8d Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 14:52:59 -0500
Subject: [PATCH 05/12] fix tests

---
 .evergreen/generated_configs/tasks.yml | 4 ++--
 .evergreen/scripts/generate_config.py  | 2 +-
 .evergreen/scripts/mod_wsgi_tester.py  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.evergreen/generated_configs/tasks.yml b/.evergreen/generated_configs/tasks.yml
index a632e4bb29..070b163e90 100644
--- a/.evergreen/generated_configs/tasks.yml
+++ b/.evergreen/generated_configs/tasks.yml
@@ -804,7 +804,7 @@ tasks:
       - func: run tests
         vars:
           TEST_NAME: mod_wsgi
-          SUB_TEST_NAME: embedded-mode
+          SUB_TEST_NAME: embedded
     tags: [mod_wsgi]
   - name: mod-wsgi-embedded-mode-replica-set
     commands:
@@ -814,7 +814,7 @@ tasks:
       - func: run tests
         vars:
           TEST_NAME: mod_wsgi
-          SUB_TEST_NAME: embedded-mode
+          SUB_TEST_NAME: embedded
     tags: [mod_wsgi]
 
   # Ocsp tests
diff --git a/.evergreen/scripts/generate_config.py b/.evergreen/scripts/generate_config.py
index 94901d6551..b90a6af437 100644
--- a/.evergreen/scripts/generate_config.py
+++ b/.evergreen/scripts/generate_config.py
@@ -897,7 +897,7 @@ def create_mod_wsgi_tasks():
         task_name += topology.replace("_", "-")
         server_vars = dict(TOPOLOGY=topology)
         server_func = FunctionCall(func="run server", vars=server_vars)
-        vars = dict(TEST_NAME="mod_wsgi", SUB_TEST_NAME=test)
+        vars = dict(TEST_NAME="mod_wsgi", SUB_TEST_NAME=test.split("-")[0])
         test_func = FunctionCall(func="run tests", vars=vars)
         tags = ["mod_wsgi"]
         commands = [server_func, test_func]
diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
index ded43592a5..9fe94b5d3b 100644
--- a/.evergreen/scripts/mod_wsgi_tester.py
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -30,7 +30,7 @@ def setup_mod_wsgi(sub_test_name: str) -> None:
         env["MOD_WSGI_CONF"] = "mod_wsgi_test.conf"
     else:
         raise ValueError("mod_wsgi sub test must be either 'standalone' or 'embedded'")
-    apache = which("apache")
+    apache = which("apache2")
     if not apache and Path("/usr/lib/apache2/mpm-prefork/apache2").exists():
         apache = "/usr/lib/apache2/mpm-prefork/apache2"
     if apache:

From d2d8c7d288b7fc75b9dcf779d02a4f8097606f89 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 14:57:38 -0500
Subject: [PATCH 06/12] fix test run

---
 .evergreen/scripts/mod_wsgi_tester.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
index 9fe94b5d3b..5ee32bc1f1 100644
--- a/.evergreen/scripts/mod_wsgi_tester.py
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -53,7 +53,7 @@ def setup_mod_wsgi(sub_test_name: str) -> None:
     write_env("TEST_URI1", uri1)
     uri2 = f"http://localhost:8080/interpreter2{project_directory}"
     write_env("TEST_URI2", uri2)
-    run_command(f"{apache} -k start -f {ROOT}/test/mod_wsgi_test/{apache_config}")
+    run_command(f"{apache} -k start -f {ROOT}/test/mod_wsgi_test/{apache_config}", env=env)
 
     # Wait for the endpoints to be available.
     try:

From baa6ba69ef3f0f75d927154606e03276796c28e6 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 15:00:06 -0500
Subject: [PATCH 07/12] fix test run

---
 .evergreen/scripts/setup_tests.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.evergreen/scripts/setup_tests.py b/.evergreen/scripts/setup_tests.py
index d8fefc8cfd..fea397e642 100644
--- a/.evergreen/scripts/setup_tests.py
+++ b/.evergreen/scripts/setup_tests.py
@@ -383,7 +383,7 @@ def handle_test_env() -> None:
         # Use --capture=tee-sys so pytest prints test output inline:
         # https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html
         TEST_ARGS = f"-v --capture=tee-sys --durations=5 {TEST_ARGS}"
-        TEST_SUITE = TEST_SUITE_MAP[test_name]
+        TEST_SUITE = TEST_SUITE_MAP.get(test_name)
         if TEST_SUITE:
             TEST_ARGS = f"-m {TEST_SUITE} {TEST_ARGS}"
 

From 97df14b13570a5f52f786b0efec8198e9cee57a9 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 15:05:15 -0500
Subject: [PATCH 08/12] fix test run

---
 .evergreen/scripts/mod_wsgi_tester.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
index 5ee32bc1f1..941abb2933 100644
--- a/.evergreen/scripts/mod_wsgi_tester.py
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -72,10 +72,10 @@ def test_mod_wsgi() -> None:
     uri2 = os.environ["TEST_URI2"]
     args = f"-n 25000 -t 100 parallel {uri1} {uri2}"
     try:
-        main(*parse_args(args))
+        main(*parse_args(args.split()))
 
         args = f"-n 25000 serial {uri1} {uri2}"
-        main(*parse_args(args))
+        main(*parse_args(args.split()))
     except Exception as e:
         LOGGER.error(Path("error_log").read_text())
         raise e

From 82c0e41652b67c7e41abe0815265c301356bc4f5 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 15:12:16 -0500
Subject: [PATCH 09/12] fix teardown

---
 .evergreen/scripts/mod_wsgi_tester.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
index 941abb2933..628eb6e225 100644
--- a/.evergreen/scripts/mod_wsgi_tester.py
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -30,6 +30,7 @@ def setup_mod_wsgi(sub_test_name: str) -> None:
         env["MOD_WSGI_CONF"] = "mod_wsgi_test.conf"
     else:
         raise ValueError("mod_wsgi sub test must be either 'standalone' or 'embedded'")
+    write_env("MOD_WSGI_CONF", env["MOD_WSGI_CONF"])
     apache = which("apache2")
     if not apache and Path("/usr/lib/apache2/mpm-prefork/apache2").exists():
         apache = "/usr/lib/apache2/mpm-prefork/apache2"
@@ -42,9 +43,9 @@ def setup_mod_wsgi(sub_test_name: str) -> None:
         apache_config = "apache22amazon.conf"
     python_version = ".".join(str(val) for val in sys.version_info[:2])
     mod_wsgi_version = 4
-    env[
-        "MOD_WSGI_SO"
-    ] = f"/opt/python/mod_wsgi/python_version/{python_version}/mod_wsgi_version/{mod_wsgi_version}/mod_wsgi.so"
+    so_file = f"/opt/python/mod_wsgi/python_version/{python_version}/mod_wsgi_version/{mod_wsgi_version}/mod_wsgi.so"
+    write_env("MOD_WSGI_SO", so_file)
+    env["MOD_WSGI_SO"] = so_file
     env["PYTHONHOME"] = f"/opt/python/{python_version}"
     env["PROJECT_DIRECTORY"] = project_directory = str(ROOT)
     write_env("APACHE", apache)
@@ -84,6 +85,7 @@ def test_mod_wsgi() -> None:
 def teardown_mod_wsgi() -> None:
     apache = os.environ["APACHE"]
     apache_config = os.environ["APACHE_CONFIG"]
+
     run_command(f"{apache} -k stop -f {ROOT}/test/mod_wsgi_test/{apache_config}")
 
 

From 12707baddb3f8b8c4e5b80c97392f2f29205c4f7 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 16:19:36 -0500
Subject: [PATCH 10/12] update env var name

---
 .evergreen/scripts/mod_wsgi_tester.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.evergreen/scripts/mod_wsgi_tester.py b/.evergreen/scripts/mod_wsgi_tester.py
index 628eb6e225..5968849068 100644
--- a/.evergreen/scripts/mod_wsgi_tester.py
+++ b/.evergreen/scripts/mod_wsgi_tester.py
@@ -48,7 +48,7 @@ def setup_mod_wsgi(sub_test_name: str) -> None:
     env["MOD_WSGI_SO"] = so_file
     env["PYTHONHOME"] = f"/opt/python/{python_version}"
     env["PROJECT_DIRECTORY"] = project_directory = str(ROOT)
-    write_env("APACHE", apache)
+    write_env("APACHE_BINARY", apache)
     write_env("APACHE_CONFIG", apache_config)
     uri1 = f"http://localhost:8080/interpreter1{project_directory}"
     write_env("TEST_URI1", uri1)
@@ -83,7 +83,7 @@ def test_mod_wsgi() -> None:
 
 
 def teardown_mod_wsgi() -> None:
-    apache = os.environ["APACHE"]
+    apache = os.environ["APACHE_BINARY"]
     apache_config = os.environ["APACHE_CONFIG"]
 
     run_command(f"{apache} -k stop -f {ROOT}/test/mod_wsgi_test/{apache_config}")

From 1c5777856b1bcf67b3ab590927aab336ab8d7394 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 16:28:35 -0500
Subject: [PATCH 11/12] fix comment

---
 .evergreen/scripts/run_tests.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.evergreen/scripts/run_tests.py b/.evergreen/scripts/run_tests.py
index 5e1e1486cb..13a510475f 100644
--- a/.evergreen/scripts/run_tests.py
+++ b/.evergreen/scripts/run_tests.py
@@ -100,7 +100,7 @@ def run() -> None:
     if TEST_PERF:
         start_time = datetime.now()
 
-    # Run mod_wsgi tests using the script.
+    # Run mod_wsgi tests using the helper.
     if TEST_NAME == "mod_wsgi":
         from mod_wsgi_tester import test_mod_wsgi
 

From ba5d535669458a22319392f407e9233b1413bcd6 Mon Sep 17 00:00:00 2001
From: Steven Silvester <steve.silvester@mongodb.com>
Date: Thu, 13 Mar 2025 16:33:20 -0500
Subject: [PATCH 12/12] fix handling of variables

---
 test/mod_wsgi_test/test_client.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/mod_wsgi_test/test_client.py b/test/mod_wsgi_test/test_client.py
index 1ddb2dcc31..c122863bfa 100644
--- a/test/mod_wsgi_test/test_client.py
+++ b/test/mod_wsgi_test/test_client.py
@@ -103,11 +103,11 @@ def __init__(self, options, urls, nrequests_per_thread):
     def run(self):
         for _i in range(self.nrequests_per_thread):
             try:
-                get(urls)
+                get(self.urls)
             except Exception as e:
                 print(e)
 
-                if not options.continue_:
+                if not self.options.continue_:
                     thread.interrupt_main()
                     thread.exit()
 
@@ -117,7 +117,7 @@ def run(self):
                 URLGetterThread.counter += 1
                 counter = URLGetterThread.counter
 
-            should_print = options.verbose and not counter % 1000
+            should_print = self.options.verbose and not counter % 1000
 
             if should_print:
                 print(counter)