Make scouting test servers work with rules_cypress
The upcoming `rules_js` migration will change the way that JS-based
tests work. Instead of executing from the root directory, the tests
execute from the directory in which they're defined. That means that
the `//scouting:scouting_test` test will execute from the `scouting`
directory in the future.
To accommodate this, I changed `scouting_test_servers.py` to use the
runfiles API. This lets the script access its dependencies regardless
of which directory it's executing from.
I also included a change where the script can accept an optional file
descriptor. When set, the script will print `READY` on that file
descriptor when startup is complete. This lets the Cypress test
framework sync with the script easily. Otherwise, I kept running into
flaky behaviour.
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: I5fa973c97faf53ec4149d07987f7a5f5b0066285
diff --git a/scouting/testing/scouting_test_servers.py b/scouting/testing/scouting_test_servers.py
index 9df23c1..d1e4e32 100644
--- a/scouting/testing/scouting_test_servers.py
+++ b/scouting/testing/scouting_test_servers.py
@@ -18,6 +18,10 @@
import time
from typing import List
+from rules_python.python.runfiles import runfiles
+
+RUNFILES = runfiles.Create()
+
def wait_for_server(port: int):
"""Waits for the server at the specified port to respond to TCP connections."""
@@ -59,15 +63,22 @@
tba_api_dir = tmpdir / "api" / "v3" / "event" / f"{year}{event_code}"
tba_api_dir.mkdir(parents=True, exist_ok=True)
(tba_api_dir / "matches").write_text(
- Path(f"scouting/scraping/test_data/{year}_{event_code}.json").
- read_text())
+ Path(
+ RUNFILES.Rlocation(
+ f"org_frc971/scouting/scraping/test_data/{year}_{event_code}.json"
+ )).read_text())
class Runner:
"""Helps manage the services we need for testing the scouting app."""
- def start(self, port: int):
- """Starts the services needed for testing the scouting app."""
+ def start(self, port: int, notify_fd: int = 0):
+ """Starts the services needed for testing the scouting app.
+
+ if notify_fd is set to a non-zero value, the string "READY" is written
+ to that file descriptor once everything is set up.
+ """
+
self.tmpdir = Path(os.environ["TEST_TMPDIR"]) / "servers"
self.tmpdir.mkdir(exist_ok=True)
@@ -76,12 +87,15 @@
# The database needs to be running and addressable before the scouting
# webserver can start.
- self.testdb_server = subprocess.Popen(
- ["scouting/db/testdb_server/testdb_server_/testdb_server"])
+ self.testdb_server = subprocess.Popen([
+ RUNFILES.Rlocation(
+ "org_frc971/scouting/db/testdb_server/testdb_server_/testdb_server"
+ )
+ ])
wait_for_server(5432)
self.webserver = subprocess.Popen([
- "scouting/scouting",
+ RUNFILES.Rlocation("org_frc971/scouting/scouting"),
f"--port={port}",
f"--db_config={db_config}",
f"--tba_config={tba_config}",
@@ -99,6 +113,10 @@
wait_for_server(7000)
wait_for_server(port)
+ if notify_fd:
+ with os.fdopen(notify_fd, "w") as file:
+ file.write("READY")
+
def stop(self):
"""Stops the services needed for testing the scouting app."""
servers = (self.webserver, self.testdb_server, self.fake_tba_api)
@@ -127,10 +145,17 @@
parser.add_argument("--port",
type=int,
help="The port for the actual web server.")
+ parser.add_argument(
+ "--notify_fd",
+ type=int,
+ default=0,
+ help=("If non-zero, indicates a file descriptor to which 'READY' is "
+ "written when everything has started up."),
+ )
args = parser.parse_args(argv[1:])
runner = Runner()
- runner.start(args.port)
+ runner.start(args.port, args.notify_fd)
# Wait until we're asked to shut down via CTRL-C or SIGTERM.
signal.signal(signal.SIGINT, discard_signal)