From 17ea8348d3dc4ca11090f1ddd9307fa52adeab37 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sat, 13 Jun 2026 19:34:05 -0700 Subject: [PATCH 01/45] Squash merge avah/rename_to_gameplay_tests into avah/combine_simulated_and_field_tests --- .github/workflows/main.yml | 13 +- scripts/compile_pip_requirements.sh | 2 +- src/MODULE.bazel | 6 +- src/software/BUILD | 4 +- src/software/ai/hl/stp/play/BUILD | 8 +- .../ai/hl/stp/play/ball_placement/BUILD | 4 +- .../ball_placement_play_test.py | 6 +- .../ai/hl/stp/play/crease_defense/BUILD | 4 +- .../crease_defense_play_test.py | 8 +- src/software/ai/hl/stp/play/defense/BUILD | 4 +- .../hl/stp/play/defense/defense_play_test.py | 6 +- .../ai/hl/stp/play/enemy_ball_placement/BUILD | 4 +- .../enemy_ball_placement_play_test.py | 4 +- .../ai/hl/stp/play/enemy_free_kick/BUILD | 4 +- .../enemy_free_kick_play_test.py | 10 +- src/software/ai/hl/stp/play/example/BUILD | 4 +- .../hl/stp/play/example/example_play_test.py | 4 +- src/software/ai/hl/stp/play/free_kick/BUILD | 4 +- .../stp/play/free_kick/free_kick_play_test.py | 8 +- src/software/ai/hl/stp/play/halt_play/BUILD | 4 +- .../hl/stp/play/halt_play/halt_play_test.py | 4 +- .../ai/hl/stp/play/kickoff_enemy/BUILD | 4 +- .../kickoff_enemy/kickoff_enemy_play_test.py | 8 +- .../ai/hl/stp/play/kickoff_friendly/BUILD | 4 +- .../kickoff_friendly_play_test.py | 10 +- .../ai/hl/stp/play/kickoff_play_test.py | 8 +- src/software/ai/hl/stp/play/offense/BUILD | 4 +- .../hl/stp/play/offense/offense_play_test.py | 10 +- .../ai/hl/stp/play/passing_sim_test.py | 10 +- .../ai/hl/stp/play/penalty_kick_enemy/BUILD | 4 +- .../penalty_kick_enemy_play_test.py | 8 +- .../ai/hl/stp/play/shoot_or_chip/BUILD | 4 +- .../shoot_or_chip/shoot_or_chip_play_test.py | 2 +- .../ai/hl/stp/play/shoot_or_pass/BUILD | 4 +- .../shoot_or_pass/shoot_or_pass_play_test.py | 4 +- src/software/ai/hl/stp/play/stop_play_test.py | 8 +- src/software/ai/hl/stp/tactic/attacker/BUILD | 4 +- .../tactic/attacker/attacker_tactic_test.py | 12 +- src/software/ai/hl/stp/tactic/chip/BUILD | 4 +- .../ai/hl/stp/tactic/chip/chip_tactic_test.py | 6 +- .../ai/hl/stp/tactic/crease_defender/BUILD | 4 +- .../crease_defender_tactic_test.py | 12 +- src/software/ai/hl/stp/tactic/defender/BUILD | 2 +- src/software/ai/hl/stp/tactic/dribble/BUILD | 4 +- .../stp/tactic/dribble/dribble_tactic_test.py | 12 +- src/software/ai/hl/stp/tactic/goalie/BUILD | 4 +- .../stp/tactic/goalie/goalie_tactic_test.py | 16 +-- src/software/ai/hl/stp/tactic/halt/BUILD | 4 +- .../ai/hl/stp/tactic/halt/halt_tactic_test.py | 6 +- src/software/ai/hl/stp/tactic/kick/BUILD | 4 +- .../ai/hl/stp/tactic/kick/kick_tactic_test.py | 4 +- src/software/ai/hl/stp/tactic/move/BUILD | 4 +- .../ai/hl/stp/tactic/move/move_tactic_test.py | 14 +- .../ai/hl/stp/tactic/pass_defender/BUILD | 4 +- .../pass_defender_tactic_test.py | 16 +-- .../ai/hl/stp/tactic/penalty_kick/BUILD | 4 +- .../penalty_kick/penalty_kick_tactic_test.py | 8 +- .../ai/hl/stp/tactic/pivot_kick/BUILD | 4 +- .../pivot_kick/pivot_kick_tactic_test.py | 4 +- src/software/ai/hl/stp/tactic/receiver/BUILD | 4 +- .../tactic/receiver/receiver_tactic_test.py | 8 +- .../ai/hl/stp/tactic/shadow_enemy/BUILD | 2 +- src/software/ai/navigator/trajectory/BUILD | 6 +- .../trajectory/simulated_hrvo_test.py | 8 +- src/software/conftest.py | 4 +- src/software/field_tests/BUILD | 66 --------- src/software/gameplay_tests/BUILD | 131 ++++++++++++++++++ .../field_test_fixture.py | 4 +- .../movement_robot_field_test.py | 4 +- .../passing_field_test.py | 6 +- .../pivot_kick_field_test.py | 6 +- .../requirements.in | 0 .../requirements_lock.txt | 4 +- .../simulated_test_ball_model.py | 18 +-- .../simulated_test_fixture.py | 4 +- .../tbots_test_runner.py | 0 .../validation/BUILD | 0 .../validation/__init__.py | 0 .../validation/avoid_collisions.py | 2 +- .../validation/ball_enters_region.py | 2 +- .../validation/ball_is_off_ground.py | 2 +- .../validation/ball_kicked_in_direction.py | 2 +- .../validation/ball_moves_in_direction.py | 2 +- .../validation/ball_speed_threshold.py | 4 +- .../validation/ball_stops_in_region.py | 2 +- .../validation/delay_validation.py | 2 +- .../validation/duration_validation.py | 2 +- .../validation/excessive_dribbling.py | 2 +- .../friendly_has_ball_possession.py | 2 +- .../validation/friendly_receives_ball_slow.py | 4 +- .../validation/friendly_team_scored.py | 2 +- .../validation/or_validation.py | 2 +- .../validation/robot_at_angular_velocity.py | 2 +- .../validation/robot_at_orientation.py | 2 +- .../validation/robot_at_position.py | 2 +- .../robot_enters_placement_region.py | 2 +- .../validation/robot_enters_region.py | 2 +- .../robot_enters_region_and_stops.py | 4 +- .../validation/robot_received_ball.py | 2 +- .../validation/robot_speed_threshold.py | 4 +- .../validation/speed_threshold_helpers.py | 0 .../validation/validation.py | 0 src/software/logger/logger.h | 2 +- src/software/sensor_fusion/filter/BUILD | 4 +- .../filter/ball_occlusion_test.py | 2 +- src/software/simulated_tests/BUILD | 68 --------- src/software/thunderscope/replay/test/BUILD | 2 +- .../replay/test/replay_corruption_test.py | 2 +- .../replay/test/replay_indexing_test.py | 2 +- src/tbots.py | 2 +- 110 files changed, 385 insertions(+), 389 deletions(-) delete mode 100644 src/software/field_tests/BUILD create mode 100644 src/software/gameplay_tests/BUILD rename src/software/{field_tests => gameplay_tests}/field_test_fixture.py (99%) rename src/software/{field_tests => gameplay_tests}/movement_robot_field_test.py (98%) rename src/software/{field_tests => gameplay_tests}/passing_field_test.py (94%) rename src/software/{field_tests => gameplay_tests}/pivot_kick_field_test.py (86%) rename src/software/{simulated_tests => gameplay_tests}/requirements.in (100%) rename src/software/{simulated_tests => gameplay_tests}/requirements_lock.txt (92%) rename src/software/{simulated_tests => gameplay_tests}/simulated_test_ball_model.py (89%) rename src/software/{simulated_tests => gameplay_tests}/simulated_test_fixture.py (99%) rename src/software/{simulated_tests => gameplay_tests}/tbots_test_runner.py (100%) rename src/software/{simulated_tests => gameplay_tests}/validation/BUILD (100%) rename src/software/{simulated_tests => gameplay_tests}/validation/__init__.py (100%) rename src/software/{simulated_tests => gameplay_tests}/validation/avoid_collisions.py (99%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_enters_region.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_is_off_ground.py (98%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_kicked_in_direction.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_moves_in_direction.py (98%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_speed_threshold.py (94%) rename src/software/{simulated_tests => gameplay_tests}/validation/ball_stops_in_region.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/delay_validation.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/duration_validation.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/excessive_dribbling.py (98%) rename src/software/{simulated_tests => gameplay_tests}/validation/friendly_has_ball_possession.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/friendly_receives_ball_slow.py (93%) rename src/software/{simulated_tests => gameplay_tests}/validation/friendly_team_scored.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/or_validation.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_at_angular_velocity.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_at_orientation.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_at_position.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_enters_placement_region.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_enters_region.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_enters_region_and_stops.py (96%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_received_ball.py (97%) rename src/software/{simulated_tests => gameplay_tests}/validation/robot_speed_threshold.py (95%) rename src/software/{simulated_tests => gameplay_tests}/validation/speed_threshold_helpers.py (100%) rename src/software/{simulated_tests => gameplay_tests}/validation/validation.py (100%) delete mode 100644 src/software/simulated_tests/BUILD diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 154ea528f3..0c7d271178 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,9 +78,8 @@ jobs: cd src bazel build --show_timestamps --copt=-O3 --verbose_failures \ -- //... -//software:unix_full_system \ - -//software/simulated_tests/... \ + -//software/gameplay_tests/... \ -//software/ai/hl/... \ - -//software/field_tests/... \ -//software/embedded/... \ -//toolchains/cc/... \ -//software:unix_full_system_tar_gen @@ -121,9 +120,8 @@ jobs: cd src bazel test --copt=-O3 --show_timestamps --verbose_failures \ -- //... -//software:unix_full_system \ - -//software/simulated_tests/... \ + -//software/gameplay_tests/... \ -//software/ai/hl/... \ - -//software/field_tests/... \ -//software/ai/navigator/... \ -//toolchains/cc/... \ -//software:unix_full_system_tar_gen @@ -153,14 +151,15 @@ jobs: - uses: ./.github/actions/environment-setup - name: Simulated Test Run + # TODO (#2908): reenable once field tests can be ran as simulated + # //software/gameplay_tests/... \ run: | cd src - bazel test //software/simulated_tests:requirements_test + bazel test //software/gameplay_tests:requirements_test bazel test --copt=-O3 --flaky_test_attempts=3 --show_timestamps \ --test_arg="--ci_mode" \ -- //software:unix_full_system \ - //software/simulated_tests/... \ - -//software/simulated_tests:requirements_test \ + -//software/gameplay_tests:requirements_test \ //software/ai/hl/... \ //software/ai/navigator/... diff --git a/scripts/compile_pip_requirements.sh b/scripts/compile_pip_requirements.sh index baa1946e74..2e76cb9b13 100755 --- a/scripts/compile_pip_requirements.sh +++ b/scripts/compile_pip_requirements.sh @@ -17,6 +17,6 @@ BAZEL_ROOT_DIR="$CURR_DIR/../src" cd $BAZEL_ROOT_DIR bazel run //software/thunderscope:requirements.update bazel run //software/embedded/ansible:requirements.update -bazel run //software/simulated_tests:requirements.update +bazel run //software/gameplay_tests:requirements.update bazel run //software/embedded/robot_diagnostics_cli:requirements.update bazel run //starlark/nanopb:requirements.update diff --git a/src/MODULE.bazel b/src/MODULE.bazel index 28f23edf87..21c9aca242 100644 --- a/src/MODULE.bazel +++ b/src/MODULE.bazel @@ -50,12 +50,12 @@ pip.parse( ) use_repo(pip, "ansible_deps") pip.parse( - hub_name = "simulated_tests_deps", + hub_name = "gameplay_tests_deps", python_interpreter = "/opt/tbotspython/bin/python", python_version = "3.12", - requirements_lock = "//software/simulated_tests:requirements_lock.txt", + requirements_lock = "//software/gameplay_tests:requirements_lock.txt", ) -use_repo(pip, "simulated_tests_deps") +use_repo(pip, "gameplay_tests_deps") pip.parse( hub_name = "robot_diagnostics_cli_deps", python_interpreter = "/opt/tbotspython/bin/python", diff --git a/src/software/BUILD b/src/software/BUILD index dbafc6f2e8..d7e481d506 100644 --- a/src/software/BUILD +++ b/src/software/BUILD @@ -125,7 +125,7 @@ py_library( name = "conftest", srcs = ["conftest.py"], deps = [ - "//software/field_tests:field_test_fixture", - "//software/simulated_tests:simulated_test_fixture", + "//software/gameplay_tests:field_test_fixture", + "//software/gameplay_tests:simulated_test_fixture", ], ) diff --git a/src/software/ai/hl/stp/play/BUILD b/src/software/ai/hl/stp/play/BUILD index e3ccbedd34..447d45ca8d 100644 --- a/src/software/ai/hl/stp/play/BUILD +++ b/src/software/ai/hl/stp/play/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -78,7 +78,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) @@ -92,7 +92,7 @@ py_test( main = "stop_play_test.py", deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) @@ -147,7 +147,7 @@ py_test( "//proto:software_py_proto", "//proto:tbots_py_proto", "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/ball_placement/BUILD b/src/software/ai/hl/stp/play/ball_placement/BUILD index c5b5d0acd6..0666696e7c 100644 --- a/src/software/ai/hl/stp/play/ball_placement/BUILD +++ b/src/software/ai/hl/stp/play/ball_placement/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -39,7 +39,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index 65bb14fbd6..ef4ffd84d5 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -5,14 +5,14 @@ from proto.import_all_protos import * from proto.ssl_gc_common_pb2 import Team from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_enters_region import ( +from software.gameplay_tests.validation.ball_enters_region import ( BallAlwaysStaysInRegion, BallEventuallyEntersRegion, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( RobotEventuallyExitsRegion, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/crease_defense/BUILD b/src/software/ai/hl/stp/play/crease_defense/BUILD index cc991f0f13..4e3d816b66 100644 --- a/src/software/ai/hl/stp/play/crease_defense/BUILD +++ b/src/software/ai/hl/stp/play/crease_defense/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -35,7 +35,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py index 10dd36212d..5514eaf6a5 100644 --- a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py +++ b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py @@ -3,16 +3,16 @@ from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) -from software.simulated_tests.validation.robot_speed_threshold import ( +from software.gameplay_tests.validation.robot_speed_threshold import ( RobotSpeedEventuallyBelowThreshold, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( NumberOfRobotsEventuallyEntersRegion, ) -from software.simulated_tests.validation.delay_validation import DelayValidation +from software.gameplay_tests.validation.delay_validation import DelayValidation def test_crease_defense_play(simulated_test_runner): diff --git a/src/software/ai/hl/stp/play/defense/BUILD b/src/software/ai/hl/stp/play/defense/BUILD index 8b12429e14..007ad9b2c6 100644 --- a/src/software/ai/hl/stp/play/defense/BUILD +++ b/src/software/ai/hl/stp/play/defense/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -38,7 +38,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/defense/defense_play_test.py b/src/software/ai/hl/stp/play/defense/defense_play_test.py index 07e29ed85b..be1366c15d 100644 --- a/src/software/ai/hl/stp/play/defense/defense_play_test.py +++ b/src/software/ai/hl/stp/play/defense/defense_play_test.py @@ -2,13 +2,13 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.friendly_has_ball_possession import ( +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.friendly_has_ball_possession import ( FriendlyEventuallyHasBallPossession, ) from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/BUILD b/src/software/ai/hl/stp/play/enemy_ball_placement/BUILD index 663c6bc9dd..473f8fb905 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/BUILD +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -30,7 +30,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py index 686f71d1a5..391380ab37 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py @@ -2,8 +2,8 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.robot_enters_placement_region import * -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.validation.robot_enters_placement_region import * +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/BUILD b/src/software/ai/hl/stp/play/enemy_free_kick/BUILD index 00b9690299..300724d149 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/BUILD +++ b/src/software/ai/hl/stp/play/enemy_free_kick/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -42,7 +42,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py index 2ee008c340..644a80a224 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py @@ -3,17 +3,17 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.or_validation import OrValidation +from software.gameplay_tests.validation.or_validation import OrValidation -from software.simulated_tests.validation.friendly_team_scored import * -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.friendly_team_scored import * +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.robot_enters_region import ( RobotEventuallyEntersRegion, RobotNeverEntersRegion, ) from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/example/BUILD b/src/software/ai/hl/stp/play/example/BUILD index f3ccd10a83..ce02f329c0 100644 --- a/src/software/ai/hl/stp/play/example/BUILD +++ b/src/software/ai/hl/stp/play/example/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -37,7 +37,7 @@ py_test( srcs = ["example_play_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/example/example_play_test.py b/src/software/ai/hl/stp/play/example/example_play_test.py index 9fbede9cf6..df4db68c1c 100644 --- a/src/software/ai/hl/stp/play/example/example_play_test.py +++ b/src/software/ai/hl/stp/play/example/example_play_test.py @@ -1,5 +1,5 @@ import software.python_bindings as tbots_cpp -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( NumberOfRobotsEventuallyExitsRegion, NumberOfRobotsEventuallyEntersRegion, ) @@ -7,7 +7,7 @@ from proto.ssl_gc_common_pb2 import Team from proto.play_pb2 import PlayName from proto.import_all_protos import Command -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/free_kick/BUILD b/src/software/ai/hl/stp/play/free_kick/BUILD index 49e5d6921d..ddde759378 100644 --- a/src/software/ai/hl/stp/play/free_kick/BUILD +++ b/src/software/ai/hl/stp/play/free_kick/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -39,7 +39,7 @@ py_test( srcs = ["free_kick_play_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py index 264d6ff9f3..63da9d1fa9 100644 --- a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py @@ -7,16 +7,16 @@ from proto.ssl_gc_common_pb2 import Team from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.friendly_team_scored import ( +from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( RobotEventuallyEntersRegion, ) -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/halt_play/BUILD b/src/software/ai/hl/stp/play/halt_play/BUILD index 9628823e55..bd881eb162 100644 --- a/src/software/ai/hl/stp/play/halt_play/BUILD +++ b/src/software/ai/hl/stp/play/halt_play/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -39,7 +39,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index 1d7412c750..0584b9ca01 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -1,8 +1,8 @@ import software.python_bindings as tbots_cpp -from software.simulated_tests.validation.robot_speed_threshold import * +from software.gameplay_tests.validation.robot_speed_threshold import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/BUILD b/src/software/ai/hl/stp/play/kickoff_enemy/BUILD index aab88be734..0a5a92203e 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/BUILD +++ b/src/software/ai/hl/stp/play/kickoff_enemy/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -31,7 +31,7 @@ py_test( srcs = ["kickoff_enemy_play_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index ce8968de8f..6af37ba5ce 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -3,17 +3,17 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( NumberOfRobotsEventuallyEntersRegion, NumberOfRobotsAlwaysStaysInRegion, RobotNeverEntersRegion, ) -from software.simulated_tests.validation.or_validation import OrValidation -from software.simulated_tests.validation.ball_enters_region import BallNeverEntersRegion +from software.gameplay_tests.validation.or_validation import OrValidation +from software.gameplay_tests.validation.ball_enters_region import BallNeverEntersRegion from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/BUILD b/src/software/ai/hl/stp/play/kickoff_friendly/BUILD index 1ca18ac954..fbd9795329 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/BUILD +++ b/src/software/ai/hl/stp/play/kickoff_friendly/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -30,7 +30,7 @@ py_test( srcs = ["kickoff_friendly_play_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index 51b4bd48cc..6ea8c4608e 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -3,21 +3,21 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( NumberOfRobotsEventuallyEntersRegion, NumberOfRobotsAlwaysStaysInRegion, NumberOfRobotsNeverEntersRegion, RobotEventuallyEntersRegion, ) -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.validation.ball_enters_region import BallNeverEntersRegion -from software.simulated_tests.validation.or_validation import OrValidation +from software.gameplay_tests.validation.ball_enters_region import BallNeverEntersRegion +from software.gameplay_tests.validation.or_validation import OrValidation from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team from proto.import_all_protos import Command -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/kickoff_play_test.py b/src/software/ai/hl/stp/play/kickoff_play_test.py index f56f5eb8dd..5a5cd6ae3f 100644 --- a/src/software/ai/hl/stp/play/kickoff_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_play_test.py @@ -2,15 +2,15 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.robot_enters_region import * -from software.simulated_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.robot_enters_region import * +from software.gameplay_tests.validation.ball_enters_region import * from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) -from software.simulated_tests.validation.or_validation import OrValidation +from software.gameplay_tests.validation.or_validation import OrValidation @pytest.mark.parametrize("is_friendly_test", [True, False]) diff --git a/src/software/ai/hl/stp/play/offense/BUILD b/src/software/ai/hl/stp/play/offense/BUILD index 67687c03ee..b3558acde9 100644 --- a/src/software/ai/hl/stp/play/offense/BUILD +++ b/src/software/ai/hl/stp/play/offense/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -35,7 +35,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/offense/offense_play_test.py b/src/software/ai/hl/stp/play/offense/offense_play_test.py index 29f49b039a..7d8a7e1990 100644 --- a/src/software/ai/hl/stp/play/offense/offense_play_test.py +++ b/src/software/ai/hl/stp/play/offense/offense_play_test.py @@ -1,12 +1,12 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.validation.friendly_team_scored import * -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.friendly_has_ball_possession import * -from software.simulated_tests.validation.excessive_dribbling import * +from software.gameplay_tests.validation.friendly_team_scored import * +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.friendly_has_ball_possession import * +from software.gameplay_tests.validation.excessive_dribbling import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index bf915965fd..5326ae407a 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -1,20 +1,20 @@ import pytest import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.friendly_receives_ball_slow import ( +from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) -from software.simulated_tests.validation.friendly_has_ball_possession import ( +from software.gameplay_tests.validation.friendly_has_ball_possession import ( FriendlyEventuallyHasBallPossession, ) -from software.simulated_tests.validation.ball_moves_in_direction import ( +from software.gameplay_tests.validation.ball_moves_in_direction import ( BallMovesForwardInRegions, ) -from software.simulated_tests.validation.ball_enters_region import ( +from software.gameplay_tests.validation.ball_enters_region import ( BallEventuallyExitsRegion, BallEventuallyEntersRegion, ) diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/BUILD b/src/software/ai/hl/stp/play/penalty_kick_enemy/BUILD index 9dc2cb7fb5..6df2ebab97 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/BUILD +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -39,7 +39,7 @@ py_test( srcs = ["penalty_kick_enemy_play_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py index 366518b3bb..77a8b74807 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py @@ -4,19 +4,19 @@ from software.py_constants import ROBOT_MAX_RADIUS_METERS from proto.play_pb2 import PlayName -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( NumberOfRobotsEventuallyEntersRegion, ) -from software.simulated_tests.validation.robot_at_position import ( +from software.gameplay_tests.validation.robot_at_position import ( RobotEventuallyAtPosition, ) -from software.simulated_tests.validation.robot_at_orientation import ( +from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, ) from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/BUILD b/src/software/ai/hl/stp/play/shoot_or_chip/BUILD index f55d2d0bf5..c0174be4b9 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/BUILD +++ b/src/software/ai/hl/stp/play/shoot_or_chip/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -38,7 +38,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py index 7c07dd531c..77a7b3c574 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py @@ -3,7 +3,7 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.import_all_protos import * diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/BUILD b/src/software/ai/hl/stp/play/shoot_or_pass/BUILD index 0c70c99acb..0cfedfe412 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/BUILD +++ b/src/software/ai/hl/stp/play/shoot_or_pass/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -54,7 +54,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 25bcd71c33..8f0105f3e6 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -5,10 +5,10 @@ from proto.play_pb2 import PlayName from proto.ssl_gc_common_pb2 import Team from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.friendly_team_scored import ( +from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.simulated_test_fixture import pytest_main @pytest.mark.skip( diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index 8735319ffe..c7ebcc57ba 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -5,14 +5,14 @@ from proto.play_pb2 import PlayName from proto.ssl_gc_common_pb2 import Team from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.robot_speed_threshold import ( +from software.gameplay_tests.validation.robot_speed_threshold import ( RobotSpeedAlwaysBelowThreshold, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( RobotNeverEntersRegion, ) -from software.simulated_tests.validation.delay_validation import DelayValidation -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.validation.delay_validation import DelayValidation +from software.gameplay_tests.simulated_test_fixture import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/attacker/BUILD b/src/software/ai/hl/stp/tactic/attacker/BUILD index 9bacc3f3d6..45c0539877 100644 --- a/src/software/ai/hl/stp/tactic/attacker/BUILD +++ b/src/software/ai/hl/stp/tactic/attacker/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -40,7 +40,7 @@ py_test( srcs = ["attacker_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index eb2d8c3740..13b91dd495 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -4,22 +4,22 @@ from proto.import_all_protos import AttackerTactic, Pass from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.validation.excessive_dribbling import ( +from software.gameplay_tests.validation.excessive_dribbling import ( NeverExcessivelyDribbles, ) -from software.simulated_tests.validation.friendly_team_scored import ( +from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.simulated_tests.validation.robot_at_orientation import ( +from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, ) -from software.simulated_tests.validation.robot_at_position import ( +from software.gameplay_tests.validation.robot_at_position import ( RobotEventuallyAtPosition, ) -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.simulated_test_fixture import pytest_main def calculate_ball_velocity(passer_point, receiver_point, speed): diff --git a/src/software/ai/hl/stp/tactic/chip/BUILD b/src/software/ai/hl/stp/tactic/chip/BUILD index 69bde4554a..3c23a97295 100644 --- a/src/software/ai/hl/stp/tactic/chip/BUILD +++ b/src/software/ai/hl/stp/tactic/chip/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -36,7 +36,7 @@ py_test( srcs = ["chip_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py index 4aad0c5249..de41738ba1 100644 --- a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py @@ -3,13 +3,13 @@ import software.python_bindings as tbots_cpp from software.py_constants import ROBOT_MAX_RADIUS_METERS -from software.simulated_tests.validation.ball_is_off_ground import ( +from software.gameplay_tests.validation.ball_is_off_ground import ( BallIsEventuallyOffGround, ) -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/crease_defender/BUILD b/src/software/ai/hl/stp/tactic/crease_defender/BUILD index 77aa1af69a..ebaa1c341b 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/BUILD +++ b/src/software/ai/hl/stp/tactic/crease_defender/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -40,7 +40,7 @@ py_test( srcs = ["crease_defender_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py index bfa92f4afd..b09aee57d7 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py @@ -8,22 +8,22 @@ MaxAllowedSpeedMode, ) from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_is_off_ground import ( +from software.gameplay_tests.validation.ball_is_off_ground import ( BallIsAlwaysOnGround, BallIsEventuallyOffGround, ) -from software.simulated_tests.validation.ball_speed_threshold import ( +from software.gameplay_tests.validation.ball_speed_threshold import ( BallSpeedAlwaysBelowThreshold, ) -from software.simulated_tests.validation.delay_validation import DelayValidation -from software.simulated_tests.validation.excessive_dribbling import ( +from software.gameplay_tests.validation.delay_validation import DelayValidation +from software.gameplay_tests.validation.excessive_dribbling import ( NeverExcessivelyDribbles, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( RobotEventuallyEntersRegion, RobotNeverEntersRegion, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/defender/BUILD b/src/software/ai/hl/stp/tactic/defender/BUILD index 7c0610ce93..f4bedf6d73 100644 --- a/src/software/ai/hl/stp/tactic/defender/BUILD +++ b/src/software/ai/hl/stp/tactic/defender/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) diff --git a/src/software/ai/hl/stp/tactic/dribble/BUILD b/src/software/ai/hl/stp/tactic/dribble/BUILD index 1b0c296620..d683df5ac0 100644 --- a/src/software/ai/hl/stp/tactic/dribble/BUILD +++ b/src/software/ai/hl/stp/tactic/dribble/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -38,7 +38,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index c138969def..aa4559ef30 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -4,24 +4,24 @@ from proto.import_all_protos import DribbleTactic from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_enters_region import ( +from software.gameplay_tests.validation.ball_enters_region import ( BallAlwaysStaysInRegion, BallEventuallyEntersRegion, ) -from software.simulated_tests.validation.excessive_dribbling import ( +from software.gameplay_tests.validation.excessive_dribbling import ( EventuallyStartsExcessivelyDribbling, NeverExcessivelyDribbles, ) -from software.simulated_tests.validation.robot_at_orientation import ( +from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, ) -from software.simulated_tests.validation.robot_received_ball import ( +from software.gameplay_tests.validation.robot_received_ball import ( RobotEventuallyReceivedBall, ) -from software.simulated_tests.validation.delay_validation import ( +from software.gameplay_tests.validation.delay_validation import ( DelayValidation, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/goalie/BUILD b/src/software/ai/hl/stp/tactic/goalie/BUILD index 0f53b8a418..4073220821 100644 --- a/src/software/ai/hl/stp/tactic/goalie/BUILD +++ b/src/software/ai/hl/stp/tactic/goalie/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -54,7 +54,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py index 2111109524..b663a152b5 100644 --- a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py @@ -2,14 +2,14 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.robot_enters_region import * -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.ball_moves_in_direction import * -from software.simulated_tests.validation.friendly_has_ball_possession import * -from software.simulated_tests.validation.ball_speed_threshold import * -from software.simulated_tests.validation.robot_speed_threshold import * -from software.simulated_tests.validation.excessive_dribbling import * -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.validation.robot_enters_region import * +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.ball_moves_in_direction import * +from software.gameplay_tests.validation.friendly_has_ball_possession import * +from software.gameplay_tests.validation.ball_speed_threshold import * +from software.gameplay_tests.validation.robot_speed_threshold import * +from software.gameplay_tests.validation.excessive_dribbling import * +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/halt/BUILD b/src/software/ai/hl/stp/tactic/halt/BUILD index bc6cdc146c..ec5e0fbd7e 100644 --- a/src/software/ai/hl/stp/tactic/halt/BUILD +++ b/src/software/ai/hl/stp/tactic/halt/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -34,7 +34,7 @@ py_test( srcs = ["halt_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py index 4c7466c2e8..79b043b508 100644 --- a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py @@ -5,11 +5,11 @@ HaltTactic, ) from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.robot_speed_threshold import ( +from software.gameplay_tests.validation.robot_speed_threshold import ( RobotSpeedEventuallyBelowThreshold, ) -from software.simulated_tests.validation.delay_validation import DelayValidation -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.validation.delay_validation import DelayValidation +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/kick/BUILD b/src/software/ai/hl/stp/tactic/kick/BUILD index d873ad65c3..7d941081cd 100644 --- a/src/software/ai/hl/stp/tactic/kick/BUILD +++ b/src/software/ai/hl/stp/tactic/kick/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -36,7 +36,7 @@ py_test( srcs = ["kick_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py index 19eceb1f0e..29a4a4c514 100644 --- a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py @@ -4,10 +4,10 @@ from proto.import_all_protos import KickTactic from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/move/BUILD b/src/software/ai/hl/stp/tactic/move/BUILD index 5343192526..ad65d4701e 100644 --- a/src/software/ai/hl/stp/tactic/move/BUILD +++ b/src/software/ai/hl/stp/tactic/move/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -35,7 +35,7 @@ py_test( srcs = ["move_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py index 7bae43eb0b..d0dca6aa1e 100644 --- a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py @@ -1,23 +1,23 @@ import pytest import software.python_bindings as tbots_cpp -from software.simulated_tests.validation.robot_at_position import ( +from software.gameplay_tests.validation.robot_at_position import ( RobotEventuallyAtPosition, ) -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.validation.robot_at_orientation import ( +from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, ) -from software.simulated_tests.validation.ball_is_off_ground import ( +from software.gameplay_tests.validation.ball_is_off_ground import ( BallIsEventuallyOffGround, ) -from software.simulated_tests.validation.robot_at_angular_velocity import ( +from software.gameplay_tests.validation.robot_at_angular_velocity import ( RobotEventuallyAtAngularVelocity, ) -from software.simulated_tests.validation.duration_validation import DurationValidation -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.validation.duration_validation import DurationValidation +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/pass_defender/BUILD b/src/software/ai/hl/stp/tactic/pass_defender/BUILD index b167a182d4..ec21625111 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/BUILD +++ b/src/software/ai/hl/stp/tactic/pass_defender/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -41,7 +41,7 @@ py_test( ], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py index fd8ab0c74a..9c50a03231 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py @@ -2,14 +2,14 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.robot_enters_region import * -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.ball_moves_in_direction import * -from software.simulated_tests.validation.friendly_has_ball_possession import * -from software.simulated_tests.validation.ball_speed_threshold import * -from software.simulated_tests.validation.robot_speed_threshold import * -from software.simulated_tests.validation.excessive_dribbling import * -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.validation.robot_enters_region import * +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.ball_moves_in_direction import * +from software.gameplay_tests.validation.friendly_has_ball_possession import * +from software.gameplay_tests.validation.ball_speed_threshold import * +from software.gameplay_tests.validation.robot_speed_threshold import * +from software.gameplay_tests.validation.excessive_dribbling import * +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/BUILD b/src/software/ai/hl/stp/tactic/penalty_kick/BUILD index 52697136af..20c1642ee6 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/BUILD +++ b/src/software/ai/hl/stp/tactic/penalty_kick/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -40,7 +40,7 @@ py_test( srcs = ["penalty_kick_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index 0e4713e5ac..bea017ad1a 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -3,17 +3,17 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import PenaltyKickTactic -from software.simulated_tests.validation.friendly_team_scored import ( +from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.simulated_tests.validation.excessive_dribbling import ( +from software.gameplay_tests.validation.excessive_dribbling import ( NeverExcessivelyDribbles, ) -from software.simulated_tests.validation.ball_moves_in_direction import ( +from software.gameplay_tests.validation.ball_moves_in_direction import ( BallAlwaysMovesForward, ) from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD index 6afb91869b..a248df127b 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD +++ b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -38,7 +38,7 @@ py_test( srcs = ["pivot_kick_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py index 8a281bb09b..04663036b1 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py @@ -4,10 +4,10 @@ from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.ball_kicked_in_direction import ( +from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/ai/hl/stp/tactic/receiver/BUILD b/src/software/ai/hl/stp/tactic/receiver/BUILD index 77453ab057..01e205dda9 100644 --- a/src/software/ai/hl/stp/tactic/receiver/BUILD +++ b/src/software/ai/hl/stp/tactic/receiver/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) @@ -40,7 +40,7 @@ py_test( srcs = ["receiver_tactic_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index 9c07bb3762..a38dadf898 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -3,16 +3,16 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import Pass, ReceiverTactic from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.validation.friendly_team_scored import ( +from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.simulated_tests.validation.robot_at_orientation import ( +from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, ) -from software.simulated_tests.validation.robot_received_ball import ( +from software.gameplay_tests.validation.robot_received_ball import ( RobotEventuallyReceivedBall, ) -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.simulated_test_fixture import pytest_main def calculate_ball_velocity(passer_point, receiver_point, speed): diff --git a/src/software/ai/hl/stp/tactic/shadow_enemy/BUILD b/src/software/ai/hl/stp/tactic/shadow_enemy/BUILD index a56ba5e49d..078ebe5b3e 100644 --- a/src/software/ai/hl/stp/tactic/shadow_enemy/BUILD +++ b/src/software/ai/hl/stp/tactic/shadow_enemy/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) diff --git a/src/software/ai/navigator/trajectory/BUILD b/src/software/ai/navigator/trajectory/BUILD index 630ed8fde3..6a5e23ecf1 100644 --- a/src/software/ai/navigator/trajectory/BUILD +++ b/src/software/ai/navigator/trajectory/BUILD @@ -1,8 +1,8 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = [ "//software/ai:__subpackages__", - "//software/simulated_tests:__subpackages__", + "//software/gameplay_tests:__subpackages__", ]) cc_library( @@ -156,7 +156,7 @@ py_test( deps = [ "//proto/message_translation:py_tbots_protobuf", "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 5bcd2a99d2..118ee44e77 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -1,16 +1,16 @@ import pytest -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) -from software.simulated_tests.validation.avoid_collisions import * +from software.gameplay_tests.validation.avoid_collisions import * import software.python_bindings as tbots from software.py_constants import * from proto.message_translation.tbots_protobuf import create_world_state import math from proto.import_all_protos import * from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import SimulatedTestRunner -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.simulated_test_fixture import SimulatedTestRunner +from software.gameplay_tests.validation.validation import ( create_validation_types, create_validation_geometry, ) diff --git a/src/software/conftest.py b/src/software/conftest.py index 61da04ba49..e625d1ec07 100644 --- a/src/software/conftest.py +++ b/src/software/conftest.py @@ -1,6 +1,6 @@ import pytest # noqa: F401 -from software.simulated_tests.simulated_test_fixture import simulated_test_runner # noqa: F401 -from software.field_tests.field_test_fixture import field_test_runner # noqa: F401 +from software.gameplay_tests.simulated_test_fixture import simulated_test_runner # noqa: F401 +from software.gameplay_tests.field_test_fixture import field_test_runner # noqa: F401 # Pytest requires that all tests fixtures shared across a package be defined # in a single conftest.py file in the parent directory of the package. diff --git a/src/software/field_tests/BUILD b/src/software/field_tests/BUILD deleted file mode 100644 index 5a45d08742..0000000000 --- a/src/software/field_tests/BUILD +++ /dev/null @@ -1,66 +0,0 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") - -package(default_visibility = ["//visibility:public"]) - -py_library( - name = "field_test_fixture", - srcs = [ - "field_test_fixture.py", - ], - data = [ - "//software:py_constants.so", - ], - deps = [ - "//proto:import_all_protos", - "//software/logger:py_logger", - "//software/networking/unix:threaded_unix_listener_py", - "//software/networking/unix:threaded_unix_sender_py", - "//software/simulated_tests:tbots_test_runner", - "//software/simulated_tests/validation:validations", - "//software/thunderscope", - "//software/thunderscope:constants", - "//software/thunderscope:estop_helpers", - "//software/thunderscope:robot_communication", - "//software/thunderscope/binary_context_managers:full_system", - "//software/thunderscope/binary_context_managers:game_controller", - "//software/thunderscope/binary_context_managers:simulator", - ], -) - -py_test( - name = "movement_robot_field_test", - srcs = [ - "movement_robot_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/simulated_tests:tbots_test_runner", - "//software/simulated_tests/validation:validations", - requirement("pytest"), - ], -) - -py_test( - name = "pivot_kick_field_test", - srcs = [ - "pivot_kick_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/simulated_tests:tbots_test_runner", - "//software/simulated_tests/validation:validations", - requirement("pytest"), - ], -) - -py_test( - name = "passing_field_test", - srcs = [ - "passing_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/simulated_tests/validation:validations", - requirement("pytest"), - ], -) diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD new file mode 100644 index 0000000000..a70ceb7bd8 --- /dev/null +++ b/src/software/gameplay_tests/BUILD @@ -0,0 +1,131 @@ +load("@gameplay_tests_deps//:requirements.bzl", "requirement") +load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +package(default_visibility = ["//visibility:public"]) + +compile_pip_requirements( + name = "requirements", + src = "requirements.in", + requirements_txt = "requirements_lock.txt", +) + +py_library( + name = "tbots_test_runner", + srcs = [ + "tbots_test_runner.py", + ], + data = [ + "//software:py_constants.so", + ], + deps = [ + "//proto:import_all_protos", + "//software/gameplay_tests/validation:validations", + "//software/logger:py_logger", + "//software/networking/unix:threaded_unix_listener_py", + "//software/networking/unix:threaded_unix_sender_py", + "//software/thunderscope", + "//software/thunderscope:config", + "//software/thunderscope:constants", + "//software/thunderscope:time_provider", + "//software/thunderscope/binary_context_managers:full_system", + "//software/thunderscope/binary_context_managers:game_controller", + "//software/thunderscope/binary_context_managers:simulator", + ], +) + +py_library( + name = "simulated_test_fixture", + srcs = [ + "simulated_test_fixture.py", + ], + data = [ + "//software:py_constants.so", + ], + deps = [ + ":tbots_test_runner", + "//proto:import_all_protos", + "//software/gameplay_tests/validation:validations", + "//software/logger:py_logger", + "//software/networking/unix:threaded_unix_listener_py", + "//software/networking/unix:threaded_unix_sender_py", + "//software/thunderscope", + "//software/thunderscope/binary_context_managers:full_system", + "//software/thunderscope/binary_context_managers:game_controller", + "//software/thunderscope/binary_context_managers:simulator", + ], +) + +py_library( + name = "field_test_fixture", + srcs = [ + "field_test_fixture.py", + ], + data = [ + "//software:py_constants.so", + ], + deps = [ + "//proto:import_all_protos", + "//software/gameplay_tests:tbots_test_runner", + "//software/gameplay_tests/validation:validations", + "//software/logger:py_logger", + "//software/networking/unix:threaded_unix_listener_py", + "//software/networking/unix:threaded_unix_sender_py", + "//software/thunderscope", + "//software/thunderscope:constants", + "//software/thunderscope:estop_helpers", + "//software/thunderscope:robot_communication", + "//software/thunderscope/binary_context_managers:full_system", + "//software/thunderscope/binary_context_managers:game_controller", + "//software/thunderscope/binary_context_managers:simulator", + ], +) + +py_test( + name = "simulated_test_ball_model", + srcs = [ + "simulated_test_ball_model.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) + +py_test( + name = "movement_robot_field_test", + srcs = [ + "movement_robot_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests:tbots_test_runner", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) + +py_test( + name = "pivot_kick_field_test", + srcs = [ + "pivot_kick_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests:tbots_test_runner", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) + +py_test( + name = "passing_field_test", + srcs = [ + "passing_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) diff --git a/src/software/field_tests/field_test_fixture.py b/src/software/gameplay_tests/field_test_fixture.py similarity index 99% rename from src/software/field_tests/field_test_fixture.py rename to src/software/gameplay_tests/field_test_fixture.py index fe3d9eb992..19e4ff6aad 100644 --- a/src/software/field_tests/field_test_fixture.py +++ b/src/software/gameplay_tests/field_test_fixture.py @@ -7,7 +7,7 @@ import argparse from proto.import_all_protos import * -from software.simulated_tests.validation import validation +from software.gameplay_tests.validation import validation from software.thunderscope.constants import EstopMode, IndividualRobotMode from software.thunderscope.thunderscope import Thunderscope from software.thunderscope.proto_unix_io import ProtoUnixIO @@ -18,7 +18,7 @@ from software.thunderscope.thunderscope_config import configure_field_test_view -from software.simulated_tests.tbots_test_runner import TbotsTestRunner +from software.gameplay_tests.tbots_test_runner import TbotsTestRunner from software.thunderscope.robot_communication import RobotCommunication from software.thunderscope.estop_helpers import get_estop_config from software.py_constants import * diff --git a/src/software/field_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py similarity index 98% rename from src/software/field_tests/movement_robot_field_test.py rename to src/software/gameplay_tests/movement_robot_field_test.py index 56c31df60b..cd6850d7df 100644 --- a/src/software/field_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -1,7 +1,7 @@ from proto.import_all_protos import * -from software.field_tests.field_test_fixture import * +from software.gameplay_tests.field_test_fixture import * -from software.simulated_tests.simulated_test_fixture import * +from software.gameplay_tests.simulated_test_fixture import * from software.logger.logger import create_logger import math diff --git a/src/software/field_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py similarity index 94% rename from src/software/field_tests/passing_field_test.py rename to src/software/gameplay_tests/passing_field_test.py index 43785bb5d4..e9308e1e0f 100644 --- a/src/software/field_tests/passing_field_test.py +++ b/src/software/gameplay_tests/passing_field_test.py @@ -1,10 +1,10 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.field_tests.field_test_fixture import * -from software.simulated_tests.validation.friendly_receives_ball_slow import ( +from software.gameplay_tests.field_test_fixture import * +from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/field_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py similarity index 86% rename from src/software/field_tests/pivot_kick_field_test.py rename to src/software/gameplay_tests/pivot_kick_field_test.py index 6205bf12cf..51dbf30d06 100644 --- a/src/software/field_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -1,10 +1,10 @@ import math from proto.import_all_protos import * -from software.field_tests.field_test_fixture import * +from software.gameplay_tests.field_test_fixture import * -from software.simulated_tests.simulated_test_fixture import * +from software.gameplay_tests.simulated_test_fixture import * from software.logger.logger import create_logger -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/simulated_tests/requirements.in b/src/software/gameplay_tests/requirements.in similarity index 100% rename from src/software/simulated_tests/requirements.in rename to src/software/gameplay_tests/requirements.in diff --git a/src/software/simulated_tests/requirements_lock.txt b/src/software/gameplay_tests/requirements_lock.txt similarity index 92% rename from src/software/simulated_tests/requirements_lock.txt rename to src/software/gameplay_tests/requirements_lock.txt index 8bf6d03071..e563375bdc 100644 --- a/src/software/simulated_tests/requirements_lock.txt +++ b/src/software/gameplay_tests/requirements_lock.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# bazel run //software/simulated_tests:requirements.update +# bazel run //software/gameplay_tests:requirements.update # attrs==23.2.0 \ --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ @@ -27,7 +27,7 @@ py==1.11.0 \ pytest==6.2.5 \ --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ --hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134 - # via -r software/simulated_tests/requirements.in + # via -r software/gameplay_tests/requirements.in toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f diff --git a/src/software/simulated_tests/simulated_test_ball_model.py b/src/software/gameplay_tests/simulated_test_ball_model.py similarity index 89% rename from src/software/simulated_tests/simulated_test_ball_model.py rename to src/software/gameplay_tests/simulated_test_ball_model.py index f5653d2612..e0f491177d 100644 --- a/src/software/simulated_tests/simulated_test_ball_model.py +++ b/src/software/gameplay_tests/simulated_test_ball_model.py @@ -1,16 +1,16 @@ import pytest import software.python_bindings as tbots_cpp -from software.simulated_tests.validation.robot_enters_region import * -from software.simulated_tests.validation.ball_enters_region import * -from software.simulated_tests.validation.ball_moves_in_direction import * -from software.simulated_tests.validation.friendly_has_ball_possession import * -from software.simulated_tests.validation.ball_speed_threshold import * -from software.simulated_tests.validation.robot_speed_threshold import * -from software.simulated_tests.validation.ball_stops_in_region import * -from software.simulated_tests.validation.excessive_dribbling import * +from software.gameplay_tests.validation.robot_enters_region import * +from software.gameplay_tests.validation.ball_enters_region import * +from software.gameplay_tests.validation.ball_moves_in_direction import * +from software.gameplay_tests.validation.friendly_has_ball_possession import * +from software.gameplay_tests.validation.ball_speed_threshold import * +from software.gameplay_tests.validation.robot_speed_threshold import * +from software.gameplay_tests.validation.ball_stops_in_region import * +from software.gameplay_tests.validation.excessive_dribbling import * from proto.message_translation.tbots_protobuf import create_world_state -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/simulated_tests/simulated_test_fixture.py b/src/software/gameplay_tests/simulated_test_fixture.py similarity index 99% rename from src/software/simulated_tests/simulated_test_fixture.py rename to src/software/gameplay_tests/simulated_test_fixture.py index 6b4dd49dab..71c14e1c85 100644 --- a/src/software/simulated_tests/simulated_test_fixture.py +++ b/src/software/gameplay_tests/simulated_test_fixture.py @@ -8,8 +8,8 @@ import pytest from proto.import_all_protos import * -from software.simulated_tests.validation import validation -from software.simulated_tests.tbots_test_runner import TbotsTestRunner +from software.gameplay_tests.validation import validation +from software.gameplay_tests.tbots_test_runner import TbotsTestRunner from software.thunderscope.thunderscope import Thunderscope from software.thunderscope.proto_unix_io import ProtoUnixIO from software.py_constants import MILLISECONDS_PER_SECOND diff --git a/src/software/simulated_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py similarity index 100% rename from src/software/simulated_tests/tbots_test_runner.py rename to src/software/gameplay_tests/tbots_test_runner.py diff --git a/src/software/simulated_tests/validation/BUILD b/src/software/gameplay_tests/validation/BUILD similarity index 100% rename from src/software/simulated_tests/validation/BUILD rename to src/software/gameplay_tests/validation/BUILD diff --git a/src/software/simulated_tests/validation/__init__.py b/src/software/gameplay_tests/validation/__init__.py similarity index 100% rename from src/software/simulated_tests/validation/__init__.py rename to src/software/gameplay_tests/validation/__init__.py diff --git a/src/software/simulated_tests/validation/avoid_collisions.py b/src/software/gameplay_tests/validation/avoid_collisions.py similarity index 99% rename from src/software/simulated_tests/validation/avoid_collisions.py rename to src/software/gameplay_tests/validation/avoid_collisions.py index 41953cd994..6ac44f7db9 100644 --- a/src/software/simulated_tests/validation/avoid_collisions.py +++ b/src/software/gameplay_tests/validation/avoid_collisions.py @@ -1,7 +1,7 @@ from software.py_constants import * import software.python_bindings as tbots from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_types, create_validation_geometry, diff --git a/src/software/simulated_tests/validation/ball_enters_region.py b/src/software/gameplay_tests/validation/ball_enters_region.py similarity index 96% rename from src/software/simulated_tests/validation/ball_enters_region.py rename to src/software/gameplay_tests/validation/ball_enters_region.py index ff6e840f59..359a44bfff 100644 --- a/src/software/simulated_tests/validation/ball_enters_region.py +++ b/src/software/gameplay_tests/validation/ball_enters_region.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/ball_is_off_ground.py b/src/software/gameplay_tests/validation/ball_is_off_ground.py similarity index 98% rename from src/software/simulated_tests/validation/ball_is_off_ground.py rename to src/software/gameplay_tests/validation/ball_is_off_ground.py index 880150c838..26b8c6a760 100644 --- a/src/software/simulated_tests/validation/ball_is_off_ground.py +++ b/src/software/gameplay_tests/validation/ball_is_off_ground.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/ball_kicked_in_direction.py b/src/software/gameplay_tests/validation/ball_kicked_in_direction.py similarity index 97% rename from src/software/simulated_tests/validation/ball_kicked_in_direction.py rename to src/software/gameplay_tests/validation/ball_kicked_in_direction.py index 3bd99978ce..40d9669b1e 100644 --- a/src/software/simulated_tests/validation/ball_kicked_in_direction.py +++ b/src/software/gameplay_tests/validation/ball_kicked_in_direction.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/ball_moves_in_direction.py b/src/software/gameplay_tests/validation/ball_moves_in_direction.py similarity index 98% rename from src/software/simulated_tests/validation/ball_moves_in_direction.py rename to src/software/gameplay_tests/validation/ball_moves_in_direction.py index 533d818fc7..fb2c99fc0f 100644 --- a/src/software/simulated_tests/validation/ball_moves_in_direction.py +++ b/src/software/gameplay_tests/validation/ball_moves_in_direction.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/ball_speed_threshold.py b/src/software/gameplay_tests/validation/ball_speed_threshold.py similarity index 94% rename from src/software/simulated_tests/validation/ball_speed_threshold.py rename to src/software/gameplay_tests/validation/ball_speed_threshold.py index f1f8dcec46..240c6db4e4 100644 --- a/src/software/simulated_tests/validation/ball_speed_threshold.py +++ b/src/software/gameplay_tests/validation/ball_speed_threshold.py @@ -1,9 +1,9 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * from software.py_constants import * -from software.simulated_tests.validation.speed_threshold_helpers import * +from software.gameplay_tests.validation.speed_threshold_helpers import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/ball_stops_in_region.py b/src/software/gameplay_tests/validation/ball_stops_in_region.py similarity index 96% rename from src/software/simulated_tests/validation/ball_stops_in_region.py rename to src/software/gameplay_tests/validation/ball_stops_in_region.py index 38b0312481..0506865964 100644 --- a/src/software/simulated_tests/validation/ball_stops_in_region.py +++ b/src/software/gameplay_tests/validation/ball_stops_in_region.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/delay_validation.py b/src/software/gameplay_tests/validation/delay_validation.py similarity index 96% rename from src/software/simulated_tests/validation/delay_validation.py rename to src/software/gameplay_tests/validation/delay_validation.py index b51725f7d4..29f455ba6b 100644 --- a/src/software/simulated_tests/validation/delay_validation.py +++ b/src/software/gameplay_tests/validation/delay_validation.py @@ -2,7 +2,7 @@ from proto.validation_pb2 import ValidationStatus, ValidationType, ValidationGeometry from software.py_constants import DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, ) diff --git a/src/software/simulated_tests/validation/duration_validation.py b/src/software/gameplay_tests/validation/duration_validation.py similarity index 96% rename from src/software/simulated_tests/validation/duration_validation.py rename to src/software/gameplay_tests/validation/duration_validation.py index 1020e024d2..c400493a61 100644 --- a/src/software/simulated_tests/validation/duration_validation.py +++ b/src/software/gameplay_tests/validation/duration_validation.py @@ -2,7 +2,7 @@ from proto.validation_pb2 import ValidationStatus, ValidationType, ValidationGeometry from software.py_constants import DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, ) diff --git a/src/software/simulated_tests/validation/excessive_dribbling.py b/src/software/gameplay_tests/validation/excessive_dribbling.py similarity index 98% rename from src/software/simulated_tests/validation/excessive_dribbling.py rename to src/software/gameplay_tests/validation/excessive_dribbling.py index 98a386b055..4904ba65a3 100644 --- a/src/software/simulated_tests/validation/excessive_dribbling.py +++ b/src/software/gameplay_tests/validation/excessive_dribbling.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import ValidationStatus, ValidationGeometry -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/friendly_has_ball_possession.py b/src/software/gameplay_tests/validation/friendly_has_ball_possession.py similarity index 97% rename from src/software/simulated_tests/validation/friendly_has_ball_possession.py rename to src/software/gameplay_tests/validation/friendly_has_ball_possession.py index bf1013f7ed..2bcb90baa3 100644 --- a/src/software/simulated_tests/validation/friendly_has_ball_possession.py +++ b/src/software/gameplay_tests/validation/friendly_has_ball_possession.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/friendly_receives_ball_slow.py b/src/software/gameplay_tests/validation/friendly_receives_ball_slow.py similarity index 93% rename from src/software/simulated_tests/validation/friendly_receives_ball_slow.py rename to src/software/gameplay_tests/validation/friendly_receives_ball_slow.py index 219271e001..f94ed02fe8 100644 --- a/src/software/simulated_tests/validation/friendly_receives_ball_slow.py +++ b/src/software/gameplay_tests/validation/friendly_receives_ball_slow.py @@ -1,10 +1,10 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( create_validation_types, ) -from software.simulated_tests.validation.friendly_has_ball_possession import ( +from software.gameplay_tests.validation.friendly_has_ball_possession import ( FriendlyHasBallPossession, ) from typing import override diff --git a/src/software/simulated_tests/validation/friendly_team_scored.py b/src/software/gameplay_tests/validation/friendly_team_scored.py similarity index 96% rename from src/software/simulated_tests/validation/friendly_team_scored.py rename to src/software/gameplay_tests/validation/friendly_team_scored.py index 458502f152..146e77923b 100644 --- a/src/software/simulated_tests/validation/friendly_team_scored.py +++ b/src/software/gameplay_tests/validation/friendly_team_scored.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/or_validation.py b/src/software/gameplay_tests/validation/or_validation.py similarity index 96% rename from src/software/simulated_tests/validation/or_validation.py rename to src/software/gameplay_tests/validation/or_validation.py index b14affe8c3..88ae1180c7 100644 --- a/src/software/simulated_tests/validation/or_validation.py +++ b/src/software/gameplay_tests/validation/or_validation.py @@ -1,5 +1,5 @@ from proto.validation_pb2 import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, ) from typing import override diff --git a/src/software/simulated_tests/validation/robot_at_angular_velocity.py b/src/software/gameplay_tests/validation/robot_at_angular_velocity.py similarity index 97% rename from src/software/simulated_tests/validation/robot_at_angular_velocity.py rename to src/software/gameplay_tests/validation/robot_at_angular_velocity.py index f9fec65db8..bb047de91b 100644 --- a/src/software/simulated_tests/validation/robot_at_angular_velocity.py +++ b/src/software/gameplay_tests/validation/robot_at_angular_velocity.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/robot_at_orientation.py b/src/software/gameplay_tests/validation/robot_at_orientation.py similarity index 97% rename from src/software/simulated_tests/validation/robot_at_orientation.py rename to src/software/gameplay_tests/validation/robot_at_orientation.py index df3c113257..cdb85b488f 100644 --- a/src/software/simulated_tests/validation/robot_at_orientation.py +++ b/src/software/gameplay_tests/validation/robot_at_orientation.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/robot_at_position.py b/src/software/gameplay_tests/validation/robot_at_position.py similarity index 97% rename from src/software/simulated_tests/validation/robot_at_position.py rename to src/software/gameplay_tests/validation/robot_at_position.py index 0637585254..8943551bd4 100644 --- a/src/software/simulated_tests/validation/robot_at_position.py +++ b/src/software/gameplay_tests/validation/robot_at_position.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, ValidationStatus, create_validation_geometry, diff --git a/src/software/simulated_tests/validation/robot_enters_placement_region.py b/src/software/gameplay_tests/validation/robot_enters_placement_region.py similarity index 97% rename from src/software/simulated_tests/validation/robot_enters_placement_region.py rename to src/software/gameplay_tests/validation/robot_enters_placement_region.py index db6275e1f2..37121e6b0c 100644 --- a/src/software/simulated_tests/validation/robot_enters_placement_region.py +++ b/src/software/gameplay_tests/validation/robot_enters_placement_region.py @@ -2,7 +2,7 @@ from software.py_constants import ENEMY_BALL_PLACEMENT_DISTANCE_METERS from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/robot_enters_region.py b/src/software/gameplay_tests/validation/robot_enters_region.py similarity index 97% rename from src/software/simulated_tests/validation/robot_enters_region.py rename to src/software/gameplay_tests/validation/robot_enters_region.py index d5d6584487..ebdbbf8887 100644 --- a/src/software/simulated_tests/validation/robot_enters_region.py +++ b/src/software/gameplay_tests/validation/robot_enters_region.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/robot_enters_region_and_stops.py b/src/software/gameplay_tests/validation/robot_enters_region_and_stops.py similarity index 96% rename from src/software/simulated_tests/validation/robot_enters_region_and_stops.py rename to src/software/gameplay_tests/validation/robot_enters_region_and_stops.py index 5cc4a5b052..1676347665 100644 --- a/src/software/simulated_tests/validation/robot_enters_region_and_stops.py +++ b/src/software/gameplay_tests/validation/robot_enters_region_and_stops.py @@ -2,11 +2,11 @@ from software.py_constants import * from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( create_validation_types, create_validation_geometry, ) -from software.simulated_tests.validation.robot_enters_region import ( +from software.gameplay_tests.validation.robot_enters_region import ( RobotEntersRegion, ) from typing import override diff --git a/src/software/simulated_tests/validation/robot_received_ball.py b/src/software/gameplay_tests/validation/robot_received_ball.py similarity index 97% rename from src/software/simulated_tests/validation/robot_received_ball.py rename to src/software/gameplay_tests/validation/robot_received_ball.py index 674f75c800..660a492896 100644 --- a/src/software/simulated_tests/validation/robot_received_ball.py +++ b/src/software/gameplay_tests/validation/robot_received_ball.py @@ -1,7 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/robot_speed_threshold.py b/src/software/gameplay_tests/validation/robot_speed_threshold.py similarity index 95% rename from src/software/simulated_tests/validation/robot_speed_threshold.py rename to src/software/gameplay_tests/validation/robot_speed_threshold.py index 19e20db786..4b5126175e 100644 --- a/src/software/simulated_tests/validation/robot_speed_threshold.py +++ b/src/software/gameplay_tests/validation/robot_speed_threshold.py @@ -1,11 +1,11 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * from software.py_constants import * -from software.simulated_tests.validation.speed_threshold_helpers import * +from software.gameplay_tests.validation.speed_threshold_helpers import * from typing import override -from software.simulated_tests.validation.validation import ( +from software.gameplay_tests.validation.validation import ( Validation, create_validation_geometry, create_validation_types, diff --git a/src/software/simulated_tests/validation/speed_threshold_helpers.py b/src/software/gameplay_tests/validation/speed_threshold_helpers.py similarity index 100% rename from src/software/simulated_tests/validation/speed_threshold_helpers.py rename to src/software/gameplay_tests/validation/speed_threshold_helpers.py diff --git a/src/software/simulated_tests/validation/validation.py b/src/software/gameplay_tests/validation/validation.py similarity index 100% rename from src/software/simulated_tests/validation/validation.py rename to src/software/gameplay_tests/validation/validation.py diff --git a/src/software/logger/logger.h b/src/software/logger/logger.h index df6691750e..44bfd16392 100644 --- a/src/software/logger/logger.h +++ b/src/software/logger/logger.h @@ -82,7 +82,7 @@ class LoggerSingleton // Standalone Simulator: // bazel-out/k8-fastbuild/bin/software/simulation/standalone_simulator_main.runfiles/__main__/ // Simulated - // tests:bazel-out/k8-fastbuild/bin/software/simulated_tests/TEST_NAME.runfiles/__main__/ + // tests:bazel-out/k8-fastbuild/bin/software/gameplay_tests/TEST_NAME.runfiles/__main__/ // where TEST_NAME is the name of the simulated test // Log locations can also be defined by setting the --logging_dir command-line diff --git a/src/software/sensor_fusion/filter/BUILD b/src/software/sensor_fusion/filter/BUILD index ab07139bd8..37f8795a42 100644 --- a/src/software/sensor_fusion/filter/BUILD +++ b/src/software/sensor_fusion/filter/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//software/sensor_fusion:__subpackages__"]) @@ -93,7 +93,7 @@ py_test( srcs = ["ball_occlusion_test.py"], deps = [ "//software:conftest", - "//software/simulated_tests/validation:validations", + "//software/gameplay_tests/validation:validations", requirement("pytest"), ], ) diff --git a/src/software/sensor_fusion/filter/ball_occlusion_test.py b/src/software/sensor_fusion/filter/ball_occlusion_test.py index 38b07e569f..e2cfaac9da 100644 --- a/src/software/sensor_fusion/filter/ball_occlusion_test.py +++ b/src/software/sensor_fusion/filter/ball_occlusion_test.py @@ -7,7 +7,7 @@ from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.simulated_tests.simulated_test_fixture import ( +from software.gameplay_tests.simulated_test_fixture import ( pytest_main, ) diff --git a/src/software/simulated_tests/BUILD b/src/software/simulated_tests/BUILD deleted file mode 100644 index 5cf8ae08b8..0000000000 --- a/src/software/simulated_tests/BUILD +++ /dev/null @@ -1,68 +0,0 @@ -load("@rules_python//python:pip.bzl", "compile_pip_requirements") -load("@simulated_tests_deps//:requirements.bzl", "requirement") - -package(default_visibility = ["//visibility:public"]) - -compile_pip_requirements( - name = "requirements", - src = "requirements.in", - requirements_txt = "requirements_lock.txt", -) - -py_library( - name = "tbots_test_runner", - srcs = [ - "tbots_test_runner.py", - ], - data = [ - "//software:py_constants.so", - ], - deps = [ - "//proto:import_all_protos", - "//software/logger:py_logger", - "//software/networking/unix:threaded_unix_listener_py", - "//software/networking/unix:threaded_unix_sender_py", - "//software/simulated_tests/validation:validations", - "//software/thunderscope", - "//software/thunderscope:config", - "//software/thunderscope:constants", - "//software/thunderscope:time_provider", - "//software/thunderscope/binary_context_managers:full_system", - "//software/thunderscope/binary_context_managers:game_controller", - "//software/thunderscope/binary_context_managers:simulator", - ], -) - -py_library( - name = "simulated_test_fixture", - srcs = [ - "simulated_test_fixture.py", - ], - data = [ - "//software:py_constants.so", - ], - deps = [ - ":tbots_test_runner", - "//proto:import_all_protos", - "//software/logger:py_logger", - "//software/networking/unix:threaded_unix_listener_py", - "//software/networking/unix:threaded_unix_sender_py", - "//software/simulated_tests/validation:validations", - "//software/thunderscope", - "//software/thunderscope/binary_context_managers:full_system", - "//software/thunderscope/binary_context_managers:game_controller", - "//software/thunderscope/binary_context_managers:simulator", - ], -) - -py_test( - name = "simulated_test_ball_model", - srcs = [ - "simulated_test_ball_model.py", - ], - deps = [ - "//software:conftest", - "//software/simulated_tests/validation:validations", - requirement("pytest"), - ], -) diff --git a/src/software/thunderscope/replay/test/BUILD b/src/software/thunderscope/replay/test/BUILD index 9b994ec17b..f65f871c21 100644 --- a/src/software/thunderscope/replay/test/BUILD +++ b/src/software/thunderscope/replay/test/BUILD @@ -1,4 +1,4 @@ -load("@simulated_tests_deps//:requirements.bzl", "requirement") +load("@gameplay_tests_deps//:requirements.bzl", "requirement") package(default_visibility = ["//visibility:public"]) diff --git a/src/software/thunderscope/replay/test/replay_corruption_test.py b/src/software/thunderscope/replay/test/replay_corruption_test.py index da16eb8b2d..8c9bd67dae 100644 --- a/src/software/thunderscope/replay/test/replay_corruption_test.py +++ b/src/software/thunderscope/replay/test/replay_corruption_test.py @@ -26,7 +26,7 @@ from software.thunderscope.constants import ProtoPlayerFlags from software.thunderscope.replay.proto_player import ProtoPlayer from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.simulated_test_fixture import pytest_main random.seed(0) diff --git a/src/software/thunderscope/replay/test/replay_indexing_test.py b/src/software/thunderscope/replay/test/replay_indexing_test.py index 4d58d3a05d..30391729da 100644 --- a/src/software/thunderscope/replay/test/replay_indexing_test.py +++ b/src/software/thunderscope/replay/test/replay_indexing_test.py @@ -13,7 +13,7 @@ from software.thunderscope.replay.proto_player import ProtoPlayer from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.simulated_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.simulated_test_fixture import pytest_main from software.thunderscope.replay.test.replay_corruption_test import ( create_valid_log_entry, create_random_proto, diff --git a/src/tbots.py b/src/tbots.py index 6ffe8cb151..d31d68a8c0 100755 --- a/src/tbots.py +++ b/src/tbots.py @@ -188,7 +188,7 @@ def create_command(config: BuildConfig, extra_args: list[str]) -> list[str]: """ if config.test_suite and config.action == ActionArgument.test: target = """-- //... \\ - -//software/field_tests/... \\ + -//software/gameplay_tests/... \\ -//toolchains/cc/... \\ -//software:unix_full_system_tar_gen""" else: From fd08dcda79cf390e36df7b986bf5f2eff74bf6d0 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sat, 13 Jun 2026 20:02:23 -0700 Subject: [PATCH 02/45] Move pytest_main definition to util.py file --- src/software/BUILD | 1 + .../ball_placement_play_test.py | 4 +- .../crease_defense_play_test.py | 4 +- .../hl/stp/play/defense/defense_play_test.py | 4 +- .../enemy_ball_placement_play_test.py | 4 +- .../enemy_free_kick_play_test.py | 4 +- .../hl/stp/play/example/example_play_test.py | 4 +- .../stp/play/free_kick/free_kick_play_test.py | 4 +- .../hl/stp/play/halt_play/halt_play_test.py | 4 +- .../kickoff_enemy/kickoff_enemy_play_test.py | 4 +- .../kickoff_friendly_play_test.py | 4 +- .../ai/hl/stp/play/kickoff_play_test.py | 4 +- .../hl/stp/play/offense/offense_play_test.py | 4 +- .../ai/hl/stp/play/passing_sim_test.py | 4 +- .../penalty_kick_enemy_play_test.py | 4 +- .../shoot_or_chip/shoot_or_chip_play_test.py | 4 +- .../shoot_or_pass/shoot_or_pass_play_test.py | 2 +- src/software/ai/hl/stp/play/stop_play_test.py | 2 +- .../tactic/attacker/attacker_tactic_test.py | 2 +- .../ai/hl/stp/tactic/chip/chip_tactic_test.py | 4 +- .../crease_defender_tactic_test.py | 4 +- .../stp/tactic/dribble/dribble_tactic_test.py | 4 +- .../stp/tactic/goalie/goalie_tactic_test.py | 4 +- .../ai/hl/stp/tactic/halt/halt_tactic_test.py | 4 +- .../ai/hl/stp/tactic/kick/kick_tactic_test.py | 4 +- .../ai/hl/stp/tactic/move/move_tactic_test.py | 4 +- .../pass_defender_tactic_test.py | 4 +- .../penalty_kick/penalty_kick_tactic_test.py | 4 +- .../pivot_kick/pivot_kick_tactic_test.py | 4 +- .../tactic/receiver/receiver_tactic_test.py | 2 +- .../trajectory/simulated_hrvo_test.py | 4 +- src/software/gameplay_tests/BUILD | 10 ++ .../gameplay_tests/passing_field_test.py | 4 +- .../gameplay_tests/pivot_kick_field_test.py | 4 +- .../simulated_test_ball_model.py | 4 +- .../gameplay_tests/simulated_test_fixture.py | 58 +++---- src/software/gameplay_tests/util.py | 151 ++++++++++++++++++ .../filter/ball_occlusion_test.py | 4 +- .../replay/test/replay_corruption_test.py | 2 +- .../replay/test/replay_indexing_test.py | 2 +- 40 files changed, 221 insertions(+), 131 deletions(-) create mode 100644 src/software/gameplay_tests/util.py diff --git a/src/software/BUILD b/src/software/BUILD index d7e481d506..b5df219cc4 100644 --- a/src/software/BUILD +++ b/src/software/BUILD @@ -126,6 +126,7 @@ py_library( srcs = ["conftest.py"], deps = [ "//software/gameplay_tests:field_test_fixture", + "//software/gameplay_tests:util", "//software/gameplay_tests:simulated_test_fixture", ], ) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index ef4ffd84d5..e786c0de9a 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -12,9 +12,7 @@ from software.gameplay_tests.validation.robot_enters_region import ( RobotEventuallyExitsRegion, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py index 5514eaf6a5..68bfbc1b92 100644 --- a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py +++ b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py @@ -3,9 +3,7 @@ from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from software.gameplay_tests.validation.robot_speed_threshold import ( RobotSpeedEventuallyBelowThreshold, ) diff --git a/src/software/ai/hl/stp/play/defense/defense_play_test.py b/src/software/ai/hl/stp/play/defense/defense_play_test.py index be1366c15d..854c521278 100644 --- a/src/software/ai/hl/stp/play/defense/defense_play_test.py +++ b/src/software/ai/hl/stp/play/defense/defense_play_test.py @@ -8,9 +8,7 @@ ) from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py index 391380ab37..6c3b55293f 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py @@ -3,9 +3,7 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName from software.gameplay_tests.validation.robot_enters_placement_region import * -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py index 644a80a224..b539afb1c3 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py @@ -13,9 +13,7 @@ ) from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/play/example/example_play_test.py b/src/software/ai/hl/stp/play/example/example_play_test.py index df4db68c1c..a6d4f0ecbc 100644 --- a/src/software/ai/hl/stp/play/example/example_play_test.py +++ b/src/software/ai/hl/stp/play/example/example_play_test.py @@ -7,9 +7,7 @@ from proto.ssl_gc_common_pb2 import Team from proto.play_pb2 import PlayName from proto.import_all_protos import Command -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_example_play(simulated_test_runner): diff --git a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py index 63da9d1fa9..4656e1756f 100644 --- a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py @@ -16,9 +16,7 @@ from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index 0584b9ca01..343255cc9b 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -2,9 +2,7 @@ from software.gameplay_tests.validation.robot_speed_threshold import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main # TODO issue #2599 - Remove Duration parameter from test diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index 6af37ba5ce..df0ba5cd3e 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -13,9 +13,7 @@ from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_kickoff_enemy_play(simulated_test_runner): diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index 6ea8c4608e..fd49a3d78c 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -17,9 +17,7 @@ from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team from proto.import_all_protos import Command -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_kickoff_friendly_play(simulated_test_runner): diff --git a/src/software/ai/hl/stp/play/kickoff_play_test.py b/src/software/ai/hl/stp/play/kickoff_play_test.py index 5a5cd6ae3f..79e9d44121 100644 --- a/src/software/ai/hl/stp/play/kickoff_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_play_test.py @@ -7,9 +7,7 @@ from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from software.gameplay_tests.validation.or_validation import OrValidation diff --git a/src/software/ai/hl/stp/play/offense/offense_play_test.py b/src/software/ai/hl/stp/play/offense/offense_play_test.py index 7d8a7e1990..36cba8f00c 100644 --- a/src/software/ai/hl/stp/play/offense/offense_play_test.py +++ b/src/software/ai/hl/stp/play/offense/offense_play_test.py @@ -6,9 +6,7 @@ from software.gameplay_tests.validation.excessive_dribbling import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_offense_play(simulated_test_runner): diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index 5326ae407a..72c8d818cc 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -1,9 +1,7 @@ import pytest import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py index 77a8b74807..8308986390 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py @@ -16,9 +16,7 @@ from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py index 77a7b3c574..5d96cec22e 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py @@ -3,9 +3,7 @@ import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 8f0105f3e6..352c4993b0 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -8,7 +8,7 @@ from software.gameplay_tests.validation.friendly_team_scored import ( FriendlyTeamEventuallyScored, ) -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main @pytest.mark.skip( diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index c7ebcc57ba..e8eaa7c8ad 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -12,7 +12,7 @@ RobotNeverEntersRegion, ) from software.gameplay_tests.validation.delay_validation import DelayValidation -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index 13b91dd495..1b07b8ac7a 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -19,7 +19,7 @@ from software.gameplay_tests.validation.robot_at_position import ( RobotEventuallyAtPosition, ) -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main def calculate_ball_velocity(passer_point, receiver_point, speed): diff --git a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py index de41738ba1..95a8f39fcf 100644 --- a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py @@ -9,9 +9,7 @@ from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import ChipTactic diff --git a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py index b09aee57d7..2430b0e08d 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py @@ -23,9 +23,7 @@ RobotEventuallyEntersRegion, RobotNeverEntersRegion, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_not_bumping_ball_towards_net(simulated_test_runner): diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index aa4559ef30..33bf16d262 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -21,9 +21,7 @@ from software.gameplay_tests.validation.delay_validation import ( DelayValidation, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def get_enemy_robot_positions(): diff --git a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py index b663a152b5..daa2b6e0e7 100644 --- a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py @@ -9,9 +9,7 @@ from software.gameplay_tests.validation.ball_speed_threshold import * from software.gameplay_tests.validation.robot_speed_threshold import * from software.gameplay_tests.validation.excessive_dribbling import * -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py index 79b043b508..66b28f313b 100644 --- a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py @@ -9,9 +9,7 @@ RobotSpeedEventuallyBelowThreshold, ) from software.gameplay_tests.validation.delay_validation import DelayValidation -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py index 29a4a4c514..dd9c2f8cfb 100644 --- a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py @@ -7,9 +7,7 @@ from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py index d0dca6aa1e..07c3440de2 100644 --- a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py @@ -17,9 +17,7 @@ RobotEventuallyAtAngularVelocity, ) from software.gameplay_tests.validation.duration_validation import DurationValidation -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import * diff --git a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py index 9c50a03231..9648711c5b 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py @@ -9,9 +9,7 @@ from software.gameplay_tests.validation.ball_speed_threshold import * from software.gameplay_tests.validation.robot_speed_threshold import * from software.gameplay_tests.validation.excessive_dribbling import * -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from proto.message_translation.tbots_protobuf import create_world_state diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index bea017ad1a..9aa2a4f9a3 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -13,9 +13,7 @@ BallAlwaysMovesForward, ) from proto.message_translation.tbots_protobuf import create_world_state -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py index 04663036b1..3810a4d3b4 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py @@ -7,9 +7,7 @@ from software.gameplay_tests.validation.ball_kicked_in_direction import ( BallEventuallyKickedInDirection, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index a38dadf898..3696066ec3 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -12,7 +12,7 @@ from software.gameplay_tests.validation.robot_received_ball import ( RobotEventuallyReceivedBall, ) -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main def calculate_ball_velocity(passer_point, receiver_point, speed): diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 118ee44e77..97da20a3aa 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -1,7 +1,5 @@ import pytest -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main from software.gameplay_tests.validation.avoid_collisions import * import software.python_bindings as tbots from software.py_constants import * diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index a70ceb7bd8..0442d47d99 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -80,6 +80,16 @@ py_library( ], ) +py_library( + name = "util", + srcs = [ + "util.py", + ], + deps = [ + requirement("pytest"), + ], +) + py_test( name = "simulated_test_ball_model", srcs = [ diff --git a/src/software/gameplay_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py index e9308e1e0f..2e6f9dac3b 100644 --- a/src/software/gameplay_tests/passing_field_test.py +++ b/src/software/gameplay_tests/passing_field_test.py @@ -4,9 +4,7 @@ from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main def test_passing(field_test_runner): diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py index 51dbf30d06..3a8999871b 100644 --- a/src/software/gameplay_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -4,9 +4,7 @@ from software.gameplay_tests.simulated_test_fixture import * from software.logger.logger import create_logger -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main logger = create_logger(__name__) diff --git a/src/software/gameplay_tests/simulated_test_ball_model.py b/src/software/gameplay_tests/simulated_test_ball_model.py index e0f491177d..08ee1159dd 100644 --- a/src/software/gameplay_tests/simulated_test_ball_model.py +++ b/src/software/gameplay_tests/simulated_test_ball_model.py @@ -10,9 +10,7 @@ from software.gameplay_tests.validation.ball_stops_in_region import * from software.gameplay_tests.validation.excessive_dribbling import * from proto.message_translation.tbots_protobuf import create_world_state -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main # the friction model currently used in the er-force simulator diff --git a/src/software/gameplay_tests/simulated_test_fixture.py b/src/software/gameplay_tests/simulated_test_fixture.py index 71c14e1c85..930f15c9bd 100644 --- a/src/software/gameplay_tests/simulated_test_fixture.py +++ b/src/software/gameplay_tests/simulated_test_fixture.py @@ -535,22 +535,6 @@ def load_command_line_arguments(allow_unrecognized: bool = False) -> argparse.Na return parser.parse_known_args()[0] if allow_unrecognized else parser.parse_args() -def pytest_main(file): - """Runs the pytest file - - :param file: The test file to run - """ - args = load_command_line_arguments(allow_unrecognized=True) - - # Run the test, -s disables all capturing at -vv increases verbosity - # -W ignore::DeprecationWarning ignores deprecation warnings that spam the output - sys.exit( - pytest.main( - ["-svv", "-W ignore::DeprecationWarning", "-k", args.test_filter, file] - ) - ) - - @pytest.fixture def simulated_test_runner(): args = load_command_line_arguments() @@ -571,25 +555,29 @@ def simulated_test_runner(): test_name = current_test.split("-")[0][:25] # Launch all binaries - with Simulator( - f"{args.simulator_runtime_dir}/test/{test_name}", - args.debug_simulator, - args.enable_realism, - ) as simulator, FullSystem( - "software/unix_full_system", - f"{args.blue_full_system_runtime_dir}/test/{test_name}", - args.debug_blue_full_system, - False, - should_restart_on_crash=False, - running_in_realtime=args.enable_thunderscope and not args.ci_mode, - ) as blue_fs, FullSystem( - "software/unix_full_system", - f"{args.yellow_full_system_runtime_dir}/test/{test_name}", - args.debug_yellow_full_system, - True, - should_restart_on_crash=False, - running_in_realtime=args.enable_thunderscope and not args.ci_mode, - ) as yellow_fs: + with ( + Simulator( + f"{args.simulator_runtime_dir}/test/{test_name}", + args.debug_simulator, + args.enable_realism, + ) as simulator, + FullSystem( + "software/unix_full_system", + f"{args.blue_full_system_runtime_dir}/test/{test_name}", + args.debug_blue_full_system, + False, + should_restart_on_crash=False, + running_in_realtime=args.enable_thunderscope and not args.ci_mode, + ) as blue_fs, + FullSystem( + "software/unix_full_system", + f"{args.yellow_full_system_runtime_dir}/test/{test_name}", + args.debug_yellow_full_system, + True, + should_restart_on_crash=False, + running_in_realtime=args.enable_thunderscope and not args.ci_mode, + ) as yellow_fs, + ): with Gamecontroller( suppress_logs=(not args.show_gamecontroller_logs) ) as gamecontroller: diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py new file mode 100644 index 0000000000..d2136d8be5 --- /dev/null +++ b/src/software/gameplay_tests/util.py @@ -0,0 +1,151 @@ +import argparse +import sys + +import pytest + + +def load_command_line_arguments(allow_unrecognized: bool = False): + """Load in command-line arguments using argparse + + NOTE: Pytest has its own built in argument parser (conftest.py, pytest_addoption) + but it doesn't seem to play nicely with bazel. We just use argparse instead. + + :param allow_unrecognized: if true, does not raise an error for unrecognized arguments + """ + parser = argparse.ArgumentParser( + description="Run simulated or field gameplay tests" + ) + + general_group = parser.add_argument_group("General test arguments") + general_group.add_argument( + "--run_field_test", + action="store_true", + default=False, + help="Runs test as a field test instead of a simulated test", + ) + general_group.add_argument( + "--enable_thunderscope", action="store_true", help="enable thunderscope" + ) + general_group.add_argument( + "--test_filter", + action="store", + default="", + help="The test filter, if not specified all tests will run. " + + "See https://docs.pytest.org/en/latest/how-to/usage.html#specifying-tests-selecting-tests", + ) + general_group.add_argument( + "--blue_full_system_runtime_dir", + type=str, + help="blue full_system runtime directory", + default="/tmp/tbots/blue", + ) + general_group.add_argument( + "--yellow_full_system_runtime_dir", + type=str, + help="yellow full_system runtime directory", + default="/tmp/tbots/yellow", + ) + general_group.add_argument( + "--layout", + action="store", + help="Which layout to run, if not specified the last layout will run", + ) + general_group.add_argument( + "--debug_blue_full_system", + action="store_true", + default=False, + help="Debug blue full_system", + ) + general_group.add_argument( + "--debug_yellow_full_system", + action="store_true", + default=False, + help="Debug yellow full_system", + ) + general_group.add_argument( + "--show_gamecontroller_logs", + action="store_true", + default=False, + help="Show gamecontroller logs", + ) + + simulated_group = parser.add_argument_group("Simulated test arguments") + simulated_group.add_argument( + "--simulator_runtime_dir", + type=str, + help="simulator runtime directory", + default="/tmp/tbots", + ) + simulated_group.add_argument( + "--debug_simulator", + action="store_true", + default=False, + help="Debug the simulator", + ) + simulated_group.add_argument( + "--enable_realism", + action="store_true", + default=False, + help="Use realism in the simulator", + ) + + field_group = parser.add_argument_group("Field test arguments") + field_group.add_argument( + "--interface", + action="store", + type=str, + default=None, + help="Which interface to communicate over", + ) + field_group.add_argument( + "--channel", + action="store", + type=int, + default=0, + help="Which channel to communicate over", + ) + field_group.add_argument( + "--estop_baudrate", + action="store", + type=int, + default=115200, + help="Estop Baudrate", + ) + field_group.add_argument( + "--run_yellow", + action="store_true", + default=False, + help="Run the test with friendly robots in yellow mode", + ) + + estop_group = field_group.add_mutually_exclusive_group() + estop_group.add_argument( + "--keyboard_estop", + action="store_true", + default=False, + help="Allows the use of the spacebar as an estop instead of a physical one", + ) + estop_group.add_argument( + "--disable_communication", + action="store_true", + default=False, + help="Disables checking for estop plugged in (ONLY USE FOR LOCAL TESTING)", + ) + + return parser.parse_known_args()[0] if allow_unrecognized else parser.parse_args() + + +def pytest_main(file): + """Runs the pytest file + + :param file: The test file to run + """ + args = load_command_line_arguments(allow_unrecognized=True) + + # Run the test, -s disables all capturing at -vv increases verbosity + # -W ignore::DeprecationWarning ignores deprecation warnings that spam the output + sys.exit( + pytest.main( + ["-svv", "-W ignore::DeprecationWarning", "-k", args.test_filter, file] + ) + ) diff --git a/src/software/sensor_fusion/filter/ball_occlusion_test.py b/src/software/sensor_fusion/filter/ball_occlusion_test.py index e2cfaac9da..1d4c861933 100644 --- a/src/software/sensor_fusion/filter/ball_occlusion_test.py +++ b/src/software/sensor_fusion/filter/ball_occlusion_test.py @@ -7,9 +7,7 @@ from proto.message_translation.tbots_protobuf import create_world_state from proto.import_all_protos import Command from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import ( - pytest_main, -) +from software.gameplay_tests.util import pytest_main @pytest.mark.parametrize( diff --git a/src/software/thunderscope/replay/test/replay_corruption_test.py b/src/software/thunderscope/replay/test/replay_corruption_test.py index 8c9bd67dae..7d43c1ca3e 100644 --- a/src/software/thunderscope/replay/test/replay_corruption_test.py +++ b/src/software/thunderscope/replay/test/replay_corruption_test.py @@ -26,7 +26,7 @@ from software.thunderscope.constants import ProtoPlayerFlags from software.thunderscope.replay.proto_player import ProtoPlayer from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main random.seed(0) diff --git a/src/software/thunderscope/replay/test/replay_indexing_test.py b/src/software/thunderscope/replay/test/replay_indexing_test.py index 30391729da..4d721e5d26 100644 --- a/src/software/thunderscope/replay/test/replay_indexing_test.py +++ b/src/software/thunderscope/replay/test/replay_indexing_test.py @@ -13,7 +13,7 @@ from software.thunderscope.replay.proto_player import ProtoPlayer from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.gameplay_tests.simulated_test_fixture import pytest_main +from software.gameplay_tests.util import pytest_main from software.thunderscope.replay.test.replay_corruption_test import ( create_valid_log_entry, create_random_proto, From 0d473c5cf8875357ed570687b976ab2923e09a08 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 14 Jun 2026 10:04:36 -0700 Subject: [PATCH 03/45] Rename simulated_test_fixture to just fixture --- src/software/BUILD | 2 +- src/software/ai/navigator/trajectory/simulated_hrvo_test.py | 5 ++--- src/software/conftest.py | 2 +- src/software/gameplay_tests/BUILD | 4 ++-- .../gameplay_tests/{simulated_test_fixture.py => fixture.py} | 0 src/software/gameplay_tests/movement_robot_field_test.py | 2 +- src/software/gameplay_tests/pivot_kick_field_test.py | 1 - 7 files changed, 7 insertions(+), 9 deletions(-) rename src/software/gameplay_tests/{simulated_test_fixture.py => fixture.py} (100%) diff --git a/src/software/BUILD b/src/software/BUILD index b5df219cc4..130ea4af68 100644 --- a/src/software/BUILD +++ b/src/software/BUILD @@ -126,7 +126,7 @@ py_library( srcs = ["conftest.py"], deps = [ "//software/gameplay_tests:field_test_fixture", + "//software/gameplay_tests:fixture", "//software/gameplay_tests:util", - "//software/gameplay_tests:simulated_test_fixture", ], ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 97da20a3aa..e5c1eadb06 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -7,7 +7,6 @@ import math from proto.import_all_protos import * from proto.ssl_gc_common_pb2 import Team -from software.gameplay_tests.simulated_test_fixture import SimulatedTestRunner from software.gameplay_tests.validation.validation import ( create_validation_types, create_validation_geometry, @@ -166,7 +165,7 @@ def hrvo_setup( friendly_robots_final_orientations: list[tbots.Angle], enemy_robots_positions: list[tbots.Point], enemy_robots_destinations: list[tbots.Point], - simulated_test_runner: SimulatedTestRunner, + simulated_test_runner, ): """Setup for the hrvo tests @@ -470,7 +469,7 @@ def hrvo_setup( ], ) def test_robot_movement( - simulated_test_runner: SimulatedTestRunner, + simulated_test_runner, friendly_robot_positions: list[tbots.Point], friendly_robot_destinations: list[tbots.Point], friendly_robots_final_orientations: list[tbots.Angle], diff --git a/src/software/conftest.py b/src/software/conftest.py index e625d1ec07..0940351cbf 100644 --- a/src/software/conftest.py +++ b/src/software/conftest.py @@ -1,5 +1,5 @@ import pytest # noqa: F401 -from software.gameplay_tests.simulated_test_fixture import simulated_test_runner # noqa: F401 +from software.gameplay_tests.fixture import simulated_test_runner # noqa: F401 from software.gameplay_tests.field_test_fixture import field_test_runner # noqa: F401 # Pytest requires that all tests fixtures shared across a package be defined diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index 0442d47d99..48062bf40c 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -34,9 +34,9 @@ py_library( ) py_library( - name = "simulated_test_fixture", + name = "fixture", srcs = [ - "simulated_test_fixture.py", + "fixture.py", ], data = [ "//software:py_constants.so", diff --git a/src/software/gameplay_tests/simulated_test_fixture.py b/src/software/gameplay_tests/fixture.py similarity index 100% rename from src/software/gameplay_tests/simulated_test_fixture.py rename to src/software/gameplay_tests/fixture.py diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index cd6850d7df..475d83f7c6 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -1,7 +1,7 @@ from proto.import_all_protos import * from software.gameplay_tests.field_test_fixture import * -from software.gameplay_tests.simulated_test_fixture import * +from software.gameplay_tests.util import pytest_main from software.logger.logger import create_logger import math diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py index 3a8999871b..7db112fb20 100644 --- a/src/software/gameplay_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -2,7 +2,6 @@ from proto.import_all_protos import * from software.gameplay_tests.field_test_fixture import * -from software.gameplay_tests.simulated_test_fixture import * from software.logger.logger import create_logger from software.gameplay_tests.util import pytest_main From 0421022fa3fdc1129a750c190c771e4a33aecbb5 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Mon, 15 Jun 2026 09:20:33 -0700 Subject: [PATCH 04/45] Separate simulated test runner into separate file, remove aggregate test runner --- .../ai/hl/stp/tactic/move/move_tactic_test.py | 15 +- src/software/gameplay_tests/BUILD | 20 +- src/software/gameplay_tests/fixture.py | 538 +----------------- .../gameplay_tests/simulated_test_runner.py | 315 ++++++++++ src/software/gameplay_tests/util.py | 6 + 5 files changed, 352 insertions(+), 542 deletions(-) create mode 100644 src/software/gameplay_tests/simulated_test_runner.py diff --git a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py index 07c3440de2..c2e3dc823f 100644 --- a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py @@ -69,8 +69,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, ) @@ -131,8 +130,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=10, ) @@ -190,8 +188,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, ) @@ -290,8 +287,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, ) @@ -334,8 +330,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=6, ) diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index 48062bf40c..3ef64e63d0 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -34,9 +34,9 @@ py_library( ) py_library( - name = "fixture", + name = "simulated_test_runner", srcs = [ - "fixture.py", + "simulated_test_runner.py", ], data = [ "//software:py_constants.so", @@ -44,11 +44,21 @@ py_library( deps = [ ":tbots_test_runner", "//proto:import_all_protos", - "//software/gameplay_tests/validation:validations", "//software/logger:py_logger", - "//software/networking/unix:threaded_unix_listener_py", - "//software/networking/unix:threaded_unix_sender_py", "//software/thunderscope", + requirement("pytest"), + ], +) + +py_library( + name = "fixture", + srcs = [ + "fixture.py", + ], + deps = [ + ":simulated_test_runner", + "//software/thunderscope", + "//software/thunderscope:proto_unix_io", "//software/thunderscope/binary_context_managers:full_system", "//software/thunderscope/binary_context_managers:game_controller", "//software/thunderscope/binary_context_managers:simulator", diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 930f15c9bd..d683157210 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -1,422 +1,18 @@ -import threading -import queue -import argparse import time -import sys import os import pytest -from proto.import_all_protos import * -from software.gameplay_tests.validation import validation -from software.gameplay_tests.tbots_test_runner import TbotsTestRunner from software.thunderscope.thunderscope import Thunderscope from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.py_constants import MILLISECONDS_PER_SECOND from software.thunderscope.binary_context_managers.full_system import FullSystem from software.thunderscope.binary_context_managers.simulator import Simulator from software.thunderscope.binary_context_managers.game_controller import Gamecontroller from software.thunderscope.thunderscope_config import configure_simulated_test_view -from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer - -from software.logger.logger import create_logger -from typing import override - -logger = create_logger(__name__) +from software.gameplay_tests.util import load_command_line_arguments +from software.gameplay_tests.simulated_test_runner import SimulatedTestRunner LAUNCH_DELAY_S = 0.1 -WORLD_BUFFER_TIMEOUT = 0.5 -PROCESS_BUFFER_DELAY_S = 0.01 -PAUSE_AFTER_FAIL_DELAY_S = 3 - - -class SimulatedTestRunner(TbotsTestRunner): - """Run a simulated test""" - - def __init__( - self, - test_name, - thunderscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - ci_mode=False, - ): - """Initialize the SimulatorTestRunner - - :param test_name: The name of the test to run - :param thunderscope: The Thunderscope to use, None if not used - :param simulator_proto_unix_io: The simulator proto unix io to use - :param blue_full_system_proto_unix_io: The blue full system proto unix io to use - :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use - :param gamecontroller: The gamecontroller context managed instance - :param ci_mode: Run test as fast as possible - """ - super(SimulatedTestRunner, self).__init__( - test_name, - thunderscope, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - ) - self.simulator_proto_unix_io = simulator_proto_unix_io - self.ci_mode = ci_mode - - @override - def set_world_state(self, worldstate: WorldState): - """Sets the simulation worldstate - - :param worldstate: proto containing the desired worldstate - """ - self.simulator_proto_unix_io.send_proto(WorldState, worldstate) - - def excepthook(self, args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. - - :param args: The args passed in from the hook - """ - self.__stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception - - def __stopper(self, delay=PROCESS_BUFFER_DELAY_S): - """Stop running the test - - :param delay: How long to wait before closing everything, defaults - to PROCESS_BUFFER_DELAY_S to minimize buffer warnings - """ - time.sleep(delay) - - if self.thunderscope: - self.thunderscope.close() - - def sync_setup(self, setup, param): - """Run setup until simulator has received game state - - :param setup: Function that sets up the world state - :param param: Parameter passed into setup - """ - world_state_received_buffer = ThreadSafeBuffer(1, WorldStateReceivedTrigger) - self.simulator_proto_unix_io.register_observer( - WorldStateReceivedTrigger, world_state_received_buffer - ) - - while True: - setup(param) - - try: - world_state_received_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT - ) - except queue.Empty: - # Did not receive a response within timeout period - continue - else: - # Received a response from the simulator - break - - def runner( - self, - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, - ): - """Run a test - - :param always_validation_sequence_set: Validation functions that should - hold on every tick - :param eventually_validation_sequence_set: Validation that should - eventually be true, before the test ends - :param test_timeout_s: The timeout for the test, if any eventually_validations - remain after the timeout, the test fails. - :param tick_duration_s: The simulation step duration - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out - """ - time_elapsed_s = 0 - - eventually_validation_failure_msg = "Test Timed Out" - - while time_elapsed_s < test_timeout_s: - # get time before we execute the loop - processing_start_time = time.time() - - # Check for new CI commands at this time step - for delay, cmd, team in ci_cmd_with_delay: - # If delay matches time - if delay <= time_elapsed_s: - # send command - self.gamecontroller.send_gc_command(gc_command=cmd, team=team) - # remove command from the list - ci_cmd_with_delay.remove((delay, cmd, team)) - - tick = SimulatorTick(milliseconds=tick_duration_s * MILLISECONDS_PER_SECOND) - self.simulator_proto_unix_io.send_proto(SimulatorTick, tick) - time_elapsed_s += tick_duration_s - - while True: - try: - world = self.world_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False - ) - - # We block until the timeout for the new primitives from AI. if not found still, - # the SSL Wrapper packet is resent in a loop until we actually get a primitive set from AI - # Otherwise, if the AI misses the first SSL Wrapper packet and doesn't start - # the simulated test will continue to tick forward, causes syncing issues with the AI - self.primitive_set_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False - ) - - break - except queue.Empty: - # If we timeout, that means full_system missed the last - # wrapper and robot status, lets resend it. - logger.warning("Fullsystem missed last wrapper, resending ...") - - ssl_wrapper = self.ssl_wrapper_buffer.get(block=False) - robot_status = self.robot_status_buffer.get(block=False) - - self.blue_full_system_proto_unix_io.send_proto( - SSL_WrapperPacket, ssl_wrapper - ) - self.blue_full_system_proto_unix_io.send_proto( - RobotStatus, robot_status - ) - - # get the time difference after we get the primitive (after any blocking that happened) - processing_time = time.time() - processing_start_time - - # if the time we have blocked is less than a tick, sleep for the remaining time (for Thunderscope only) - if ( - self.thunderscope - and not self.ci_mode - and tick_duration_s > processing_time - ): - time.sleep(tick_duration_s - processing_time) - - # Validate - ( - eventually_validation_proto_set, - always_validation_proto_set, - ) = validation.run_validation_sequence_sets( - world, - eventually_validation_sequence_set, - always_validation_sequence_set, - ) - - # Set the test name - eventually_validation_proto_set.test_name = self.test_name - always_validation_proto_set.test_name = self.test_name - - # Send out the validation proto to the full system - # for visualization and logging for replays. - if self.is_yellow_friendly: - self.yellow_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set - ) - self.yellow_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set - ) - else: - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set - ) - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set - ) - - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) - - if not run_till_end: - try: - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - self.__stopper() - return - except AssertionError as e: - eventually_validation_failure_msg = str(e) - - if not run_till_end: - raise AssertionError(eventually_validation_failure_msg) - - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - - self.__stopper() - - @override - def run_test( - self, - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s=3, - tick_duration_s=0.0166, # Default to 60hz - index=0, - ci_cmd_with_delay=[], - run_till_end=True, - **kwargs, - ): - """Helper function to run a test, with thunderscope if enabled - - :param always_validation_sequence_set: validation that should always be true - :param eventually_validation_sequence_set: validation that should eventually be true - :param test_timeout_s: how long the test should run before timing out - :param tick_duration_s: length of a tick - :param index: index of the current test. default is 0 (invariant test) - values can be passed in during aggregate testing for different timeout durations - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out - """ - test_timeout_duration = ( - test_timeout_s[index] if type(test_timeout_s) == list else test_timeout_s - ) - - # If thunderscope is enabled, run the test in a thread and show - # thunderscope on this thread. The excepthook is setup to catch - # any test failures and propagate them to the main thread - if self.thunderscope: - run_sim_thread = threading.Thread( - target=self.runner, - daemon=True, - args=[ - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_duration, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, - ], - ) - run_sim_thread.start() - self.thunderscope.show() - run_sim_thread.join() - - if self.last_exception: - pytest.fail(str(self.last_exception)) - - # If thunderscope is disabled, just run the test - else: - self.runner( - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_duration, - tick_duration_s, - ci_cmd_with_delay=ci_cmd_with_delay, - run_till_end=run_till_end, - ) - - -class InvariantTestRunner(SimulatedTestRunner): - """Runs a simulated test only once with a given parameter - - Test passes or fails based on the outcome of this test - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @override - def run_test( - self, - setup=(lambda x: None), - params=[0], - inv_always_validation_sequence_set=[[]], - inv_eventually_validation_sequence_set=[[]], - **kwargs, - ): - """Run an invariant test - - :param setup: Function that sets up the World state and the gamecontroller before running the test - :param params: List of parameters for each iteration of the test - (this method only uses the first element) - :param inv_always_validation_sequence_set: Validation functions for invariant testing - that should hold on every tick - :param inv_eventually_validation_sequence_set: Validation functions for invariant testing - that should eventually be true, before the test ends - """ - threading.excepthook = self.excepthook - - super().sync_setup(setup, params[0]) - - super().run_test( - inv_always_validation_sequence_set, - inv_eventually_validation_sequence_set, - **kwargs, - ) - - -class AggregateTestRunner(SimulatedTestRunner): - """Runs a simulated test multiple times with different given parameters - - Result of the test is determined by comparing the number of - passing iterations to a predetermined acceptable threshold - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @override - def run_test( - self, - setup=(lambda arg: None), - params=[], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[[]], - **kwargs, - ): - """Run an aggregate test - - :param setup: Function that sets up the World state and the gamecontroller before running the test - :param params: List of parameters for each iteration of the test - :param ag_always_validation_sequence_set: Validation functions for aggregate testing - that should hold on every tick - :param ag_eventually_validation_sequence_set: Validation functions for aggregate testing - that should eventually be true, before the test end - """ - threading.excepthook = self.excepthook - - failed_tests = 0 - - # Runs the test once for each given parameter - # Catches Assertion Error thrown by failing test and increments counter - # Calculates overall results and prints them - for x in range(len(params)): - super().sync_setup(setup, params[x]) - - try: - super().run_test( - ag_always_validation_sequence_set, - ag_eventually_validation_sequence_set, - **kwargs, - ) - except AssertionError: - failed_tests += 1 - - # TODO (#2856) Fix validation and results output - - logger.info(f"{failed_tests} test failed") - - assert failed_tests == 0 def get_runtime_dir(): @@ -439,109 +35,11 @@ def get_runtime_dir(): RUNTIME_DIR = get_runtime_dir() -def load_command_line_arguments(allow_unrecognized: bool = False) -> argparse.Namespace: - """Load command line arguments. - - We aren't using pytest.ini because it does not allow for dynamic defaults, - which we need to use TEST_TMPDIR when it's available. - - We aren't using conftest.py's pytest_addoption because we want to be able to - run the gamecontroller script directly from python without pytest. - Pytest does have a way to access parsed options (request.config.getoption), - but it doesn't seem to play nicely with bazel. We just use argparse instead. - - :param allow_unrecognized: if true, does not raise an error for unrecognized arguments - """ - parser = argparse.ArgumentParser(description="Run simulated pytests") - parser.add_argument( - "--enable_thunderscope", action="store_true", help="enable thunderscope" - ) - parser.add_argument( - "--ci_mode", - action="store_true", - default=False, - help="Run simulator at faster speed", - ) - parser.add_argument( - "--aggregate", action="store_true", default=False, help="Run aggregate test" - ) - parser.add_argument( - "--simulator_runtime_dir", - type=str, - help="simulator runtime directory", - default=RUNTIME_DIR, - ) - parser.add_argument( - "--blue_full_system_runtime_dir", - type=str, - help="blue full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "blue"), - ) - parser.add_argument( - "--yellow_full_system_runtime_dir", - type=str, - help="yellow full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "yellow"), - ) - parser.add_argument( - "--layout", - action="store", - help="Which layout to run, if not specified the last layout will run", - ) - parser.add_argument( - "--debug_blue_full_system", - action="store_true", - default=False, - help="Debug blue full_system", - ) - parser.add_argument( - "--debug_yellow_full_system", - action="store_true", - default=False, - help="Debug yellow full_system", - ) - parser.add_argument( - "--debug_simulator", - action="store_true", - default=False, - help="Debug the simulator", - ) - parser.add_argument( - "--visualization_buffer_size", - action="store", - type=int, - default=5, - help="How many packets to buffer while rendering", - ) - parser.add_argument( - "--show_gamecontroller_logs", - action="store_true", - default=False, - help="Show gamecontroller logs", - ) - parser.add_argument( - "--test_filter", - action="store", - default="", - help="The test filter, if not specified all tests will run. " - + "See https://docs.pytest.org/en/latest/how-to/usage.html#specifying-tests-selecting-tests", - ) - parser.add_argument( - "--enable_realism", - action="store_true", - default=False, - help="Use realism in the simulator", - ) - return parser.parse_known_args()[0] if allow_unrecognized else parser.parse_args() - - @pytest.fixture def simulated_test_runner(): args = load_command_line_arguments() tscope = None - aggregate = args.aggregate - simulator_proto_unix_io = ProtoUnixIO() yellow_full_system_proto_unix_io = ProtoUnixIO() blue_full_system_proto_unix_io = ProtoUnixIO() @@ -609,28 +107,14 @@ def simulated_test_runner(): time.sleep(LAUNCH_DELAY_S) - runner = None - - # Initialise the right runner based on which testing mode is selected - if aggregate: - runner = AggregateTestRunner( - current_test, - tscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - args.ci_mode, - ) - else: - runner = InvariantTestRunner( - current_test, - tscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - args.ci_mode, - ) + runner = SimulatedTestRunner( + current_test, + tscope, + simulator_proto_unix_io, + blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io, + gamecontroller, + args.ci_mode, + ) yield runner diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py new file mode 100644 index 0000000000..c571edff82 --- /dev/null +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -0,0 +1,315 @@ +import queue +import threading +import time +from typing import override + +import pytest +from software.py_constants import MILLISECONDS_PER_SECOND + +from proto.import_all_protos import * +from software.gameplay_tests.tbots_test_runner import TbotsTestRunner +from software.gameplay_tests.validation import validation +from software.logger.logger import create_logger +from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer + +logger = create_logger(__name__) + +LAUNCH_DELAY_S = 0.1 +WORLD_BUFFER_TIMEOUT = 0.5 +PROCESS_BUFFER_DELAY_S = 0.01 +PAUSE_AFTER_FAIL_DELAY_S = 3 + + +class SimulatedTestRunner(TbotsTestRunner): + """Run a simulated test""" + + def __init__( + self, + test_name, + thunderscope, + simulator_proto_unix_io, + blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io, + gamecontroller, + ci_mode=False, + ): + """Initialize the SimulatorTestRunner + + :param test_name: The name of the test to run + :param thunderscope: The Thunderscope to use, None if not used + :param simulator_proto_unix_io: The simulator proto unix io to use + :param blue_full_system_proto_unix_io: The blue full system proto unix io to use + :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use + :param gamecontroller: The gamecontroller context managed instance + :param ci_mode: Run test as fast as possible + """ + super(SimulatedTestRunner, self).__init__( + test_name, + thunderscope, + blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io, + gamecontroller, + ) + self.simulator_proto_unix_io = simulator_proto_unix_io + self.ci_mode = ci_mode + + @override + def set_world_state(self, worldstate: WorldState): + """Sets the simulation worldstate + + :param worldstate: proto containing the desired worldstate + """ + self.simulator_proto_unix_io.send_proto(WorldState, worldstate) + + def excepthook(self, args): + """This function is _critical_ for show_thunderscope to work. + If the test Thread will raises an exception we won't be able to close + the window from the main thread. + + :param args: The args passed in from the hook + """ + self.__stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) + self.last_exception = args.exc_value + raise self.last_exception + + def __stopper(self, delay=PROCESS_BUFFER_DELAY_S): + """Stop running the test + + :param delay: How long to wait before closing everything, defaults + to PROCESS_BUFFER_DELAY_S to minimize buffer warnings + """ + time.sleep(delay) + + if self.thunderscope: + self.thunderscope.close() + + def sync_setup(self, setup): + """Run setup until simulator has received game state + + :param setup: Function that sets up the world state + :param param: Parameter passed into setup + """ + world_state_received_buffer = ThreadSafeBuffer(1, WorldStateReceivedTrigger) + self.simulator_proto_unix_io.register_observer( + WorldStateReceivedTrigger, world_state_received_buffer + ) + + while True: + setup() + + try: + world_state_received_buffer.get( + block=True, timeout=WORLD_BUFFER_TIMEOUT + ) + except queue.Empty: + # Did not receive a response within timeout period + continue + else: + # Received a response from the simulator + break + + def runner( + self, + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + tick_duration_s, + ci_cmd_with_delay, + run_till_end, + ): + """Run a test + + :param always_validation_sequence_set: Validation functions that should + hold on every tick + :param eventually_validation_sequence_set: Validation that should + eventually be true, before the test ends + :param test_timeout_s: The timeout for the test, if any eventually_validations + remain after the timeout, the test fails. + :param tick_duration_s: The simulation step duration + :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. + [ + (time, command, team), + (time, command, team), + ... + ] + :param run_till_end: If true, test runs till the end even if eventually validation passes + If false, test stops once eventually validation passes and fails if time out + """ + time_elapsed_s = 0 + + eventually_validation_failure_msg = "Test Timed Out" + + while time_elapsed_s < test_timeout_s: + # get time before we execute the loop + processing_start_time = time.time() + + # Check for new CI commands at this time step + for delay, cmd, team in ci_cmd_with_delay: + # If delay matches time + if delay <= time_elapsed_s: + # send command + self.gamecontroller.send_gc_command(gc_command=cmd, team=team) + # remove command from the list + ci_cmd_with_delay.remove((delay, cmd, team)) + + tick = SimulatorTick(milliseconds=tick_duration_s * MILLISECONDS_PER_SECOND) + self.simulator_proto_unix_io.send_proto(SimulatorTick, tick) + time_elapsed_s += tick_duration_s + + while True: + try: + world = self.world_buffer.get( + block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False + ) + + # We block until the timeout for the new primitives from AI. if not found still, + # the SSL Wrapper packet is resent in a loop until we actually get a primitive set from AI + # Otherwise, if the AI misses the first SSL Wrapper packet and doesn't start + # the simulated test will continue to tick forward, causes syncing issues with the AI + self.primitive_set_buffer.get( + block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False + ) + + break + except queue.Empty: + # If we timeout, that means full_system missed the last + # wrapper and robot status, lets resend it. + logger.warning("Fullsystem missed last wrapper, resending ...") + + ssl_wrapper = self.ssl_wrapper_buffer.get(block=False) + robot_status = self.robot_status_buffer.get(block=False) + + self.blue_full_system_proto_unix_io.send_proto( + SSL_WrapperPacket, ssl_wrapper + ) + self.blue_full_system_proto_unix_io.send_proto( + RobotStatus, robot_status + ) + + # get the time difference after we get the primitive (after any blocking that happened) + processing_time = time.time() - processing_start_time + + # if the time we have blocked is less than a tick, sleep for the remaining time (for Thunderscope only) + if ( + self.thunderscope + and not self.ci_mode + and tick_duration_s > processing_time + ): + time.sleep(tick_duration_s - processing_time) + + # Validate + ( + eventually_validation_proto_set, + always_validation_proto_set, + ) = validation.run_validation_sequence_sets( + world, + eventually_validation_sequence_set, + always_validation_sequence_set, + ) + + # Set the test name + eventually_validation_proto_set.test_name = self.test_name + always_validation_proto_set.test_name = self.test_name + + # Send out the validation proto to the full system + # for visualization and logging for replays. + if self.is_yellow_friendly: + self.yellow_full_system_proto_unix_io.send_proto( + ValidationProtoSet, eventually_validation_proto_set + ) + self.yellow_full_system_proto_unix_io.send_proto( + ValidationProtoSet, always_validation_proto_set + ) + else: + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, eventually_validation_proto_set + ) + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, always_validation_proto_set + ) + + # Check that all always validations are always valid + validation.check_validation(always_validation_proto_set) + + if not run_till_end: + try: + # Check that all eventually validations are eventually valid + validation.check_validation(eventually_validation_proto_set) + self.__stopper() + return + except AssertionError as e: + eventually_validation_failure_msg = str(e) + + if not run_till_end: + raise AssertionError(eventually_validation_failure_msg) + + # Check that all eventually validations are eventually valid + validation.check_validation(eventually_validation_proto_set) + + self.__stopper() + + @override + def run_test( + self, + always_validation_sequence_set=[], + eventually_validation_sequence_set=[], + setup=(lambda: None), + test_timeout_s=3, + tick_duration_s=0.0166, # Default to 60hz + ci_cmd_with_delay=[], + run_till_end=True, + **kwargs, + ): + """Helper function to run a test, with thunderscope if enabled + + :param always_validation_sequence_set: validation that should always be true + :param eventually_validation_sequence_set: validation that should eventually be true + :param setup: function that sets up the World state and the gamecontroller before running the test + :param test_timeout_s: how long the test should run before timing out + :param tick_duration_s: length of a tick + :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. + [ + (time, command, team), + (time, command, team), + ... + ] + :param run_till_end: If true, test runs till the end even if eventually validation passes + If false, test stops once eventually validation passes and fails if time out + """ + threading.excepthook = self.excepthook + + self.sync_setup(setup) + + # If thunderscope is enabled, run the test in a thread and show + # thunderscope on this thread. The excepthook is setup to catch + # any test failures and propagate them to the main thread + if self.thunderscope: + run_sim_thread = threading.Thread( + target=self.runner, + daemon=True, + args=[ + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + tick_duration_s, + ci_cmd_with_delay, + run_till_end, + ], + ) + run_sim_thread.start() + self.thunderscope.show() + run_sim_thread.join() + + if self.last_exception: + pytest.fail(str(self.last_exception)) + + # If thunderscope is disabled, just run the test + else: + self.runner( + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + tick_duration_s, + ci_cmd_with_delay=ci_cmd_with_delay, + run_till_end=run_till_end, + ) diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index d2136d8be5..afd524b486 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -70,6 +70,12 @@ def load_command_line_arguments(allow_unrecognized: bool = False): ) simulated_group = parser.add_argument_group("Simulated test arguments") + simulated_group.add_argument( + "--ci_mode", + action="store_true", + default=False, + help="Run simulator at faster speed", + ) simulated_group.add_argument( "--simulator_runtime_dir", type=str, From c3522184e72a0e674333e28adf023a983e6487a9 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Mon, 15 Jun 2026 12:54:05 -0700 Subject: [PATCH 05/45] Move field_test_fixture to fixture, and separate out field_test_runner --- src/software/BUILD | 1 - src/software/conftest.py | 2 +- src/software/gameplay_tests/BUILD | 42 +- .../gameplay_tests/field_test_fixture.py | 453 ------------------ .../gameplay_tests/field_test_runner.py | 192 ++++++++ src/software/gameplay_tests/fixture.py | 148 +++++- .../gameplay_tests/simulated_test_runner.py | 7 +- .../gameplay_tests/tbots_test_runner.py | 44 +- 8 files changed, 375 insertions(+), 514 deletions(-) delete mode 100644 src/software/gameplay_tests/field_test_fixture.py create mode 100644 src/software/gameplay_tests/field_test_runner.py diff --git a/src/software/BUILD b/src/software/BUILD index 130ea4af68..27110d2946 100644 --- a/src/software/BUILD +++ b/src/software/BUILD @@ -125,7 +125,6 @@ py_library( name = "conftest", srcs = ["conftest.py"], deps = [ - "//software/gameplay_tests:field_test_fixture", "//software/gameplay_tests:fixture", "//software/gameplay_tests:util", ], diff --git a/src/software/conftest.py b/src/software/conftest.py index 0940351cbf..b66836a7b4 100644 --- a/src/software/conftest.py +++ b/src/software/conftest.py @@ -1,6 +1,6 @@ import pytest # noqa: F401 from software.gameplay_tests.fixture import simulated_test_runner # noqa: F401 -from software.gameplay_tests.field_test_fixture import field_test_runner # noqa: F401 +from software.gameplay_tests.fixture import field_test_runner # noqa: F401 # Pytest requires that all tests fixtures shared across a package be defined # in a single conftest.py file in the parent directory of the package. diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index 3ef64e63d0..c1fe0f09c2 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -34,9 +34,9 @@ py_library( ) py_library( - name = "simulated_test_runner", + name = "field_test_runner", srcs = [ - "simulated_test_runner.py", + "field_test_runner.py", ], data = [ "//software:py_constants.so", @@ -51,42 +51,38 @@ py_library( ) py_library( - name = "fixture", + name = "simulated_test_runner", srcs = [ - "fixture.py", + "simulated_test_runner.py", + ], + data = [ + "//software:py_constants.so", ], deps = [ - ":simulated_test_runner", + ":tbots_test_runner", + "//proto:import_all_protos", + "//software/logger:py_logger", "//software/thunderscope", - "//software/thunderscope:proto_unix_io", - "//software/thunderscope/binary_context_managers:full_system", - "//software/thunderscope/binary_context_managers:game_controller", - "//software/thunderscope/binary_context_managers:simulator", + requirement("pytest"), ], ) py_library( - name = "field_test_fixture", + name = "fixture", srcs = [ - "field_test_fixture.py", - ], - data = [ - "//software:py_constants.so", + "fixture.py", ], deps = [ - "//proto:import_all_protos", - "//software/gameplay_tests:tbots_test_runner", - "//software/gameplay_tests/validation:validations", - "//software/logger:py_logger", - "//software/networking/unix:threaded_unix_listener_py", - "//software/networking/unix:threaded_unix_sender_py", - "//software/thunderscope", - "//software/thunderscope:constants", + ":util", + ":field_test_runner", + ":simulated_test_runner", "//software/thunderscope:estop_helpers", - "//software/thunderscope:robot_communication", + "//software/logger:py_logger", + "//software/thunderscope:thunderscope", "//software/thunderscope/binary_context_managers:full_system", "//software/thunderscope/binary_context_managers:game_controller", "//software/thunderscope/binary_context_managers:simulator", + requirement("pytest"), ], ) diff --git a/src/software/gameplay_tests/field_test_fixture.py b/src/software/gameplay_tests/field_test_fixture.py deleted file mode 100644 index 19e4ff6aad..0000000000 --- a/src/software/gameplay_tests/field_test_fixture.py +++ /dev/null @@ -1,453 +0,0 @@ -import queue -import time -import os -import threading - -import pytest -import argparse -from proto.import_all_protos import * - -from software.gameplay_tests.validation import validation -from software.thunderscope.constants import EstopMode, IndividualRobotMode -from software.thunderscope.thunderscope import Thunderscope -from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.thunderscope.binary_context_managers.full_system import FullSystem -from software.thunderscope.binary_context_managers.game_controller import Gamecontroller -from software.thunderscope.wifi_communication_manager import WifiCommunicationManager -from software.logger.logger import create_logger - - -from software.thunderscope.thunderscope_config import configure_field_test_view -from software.gameplay_tests.tbots_test_runner import TbotsTestRunner -from software.thunderscope.robot_communication import RobotCommunication -from software.thunderscope.estop_helpers import get_estop_config -from software.py_constants import * -from typing import override - -logger = create_logger(__name__) - -WORLD_BUFFER_TIMEOUT = 5.0 -PROCESS_BUFFER_DELAY_S = 0.01 -PAUSE_AFTER_FAIL_DELAY_S = 3 -LAUNCH_DELAY_S = 0.1 -TEST_END_DELAY = 0.3 - - -class FieldTestRunner(TbotsTestRunner): - """Run a field test""" - - def __init__( - self, - test_name, - thunderscope, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - publish_validation_protos=True, - is_yellow_friendly=False, - ): - """Initialize the FieldTestRunner - - :param test_name: The name of the test to run - :param thunderscope: The Thunderscope to use, None if not used - :param blue_full_system_proto_unix_io: The blue full system proto unix io to use - :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use - :param gamecontroller: The gamecontroller context managed instance - :param publish_validation_protos: whether to publish validation protos - :param: is_yellow_friendly: if yellow is the friendly team - """ - super(FieldTestRunner, self).__init__( - test_name, - thunderscope, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - is_yellow_friendly, - ) - self.publish_validation_protos = publish_validation_protos - self.is_yellow_friendly = is_yellow_friendly - - logger.info("determining robots on field") - # survey field for available robot ids - try: - world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - self.initial_world = world - self.friendly_robot_ids_field = [ - robot.id for robot in world.friendly_team.team_robots - ] - - logger.info(f"friendly team ids {self.friendly_robot_ids_field}") - - if len(self.friendly_robot_ids_field) == 0: - raise Exception("no friendly robots found on field") - - except queue.Empty: - raise Exception( - f"No Worlds were received with in {WORLD_BUFFER_TIMEOUT} seconds. Please make sure atleast 1 robot and 1 ball is present on the field." - ) - - @override - def send_gamecontroller_command( - self, - gc_command: proto.ssl_gc_state_pb2.Command, - team: proto.ssl_gc_common_pb2.Team, - final_ball_placement_point=None, - ): - """Send a command to the gamecontroller - - :param gc_command: The command to send - :param team: The team which the command as attributed to - :param final_ball_placement_point: The ball placement point - """ - self.gamecontroller.send_ci_input( - gc_command=gc_command, - team=team, - final_ball_placement_point=final_ball_placement_point, - ) - - @override - def run_test( - self, - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], - test_timeout_s=3, - ): - """Run a test. In a field test this means beginning validation. - - :param always_validation_sequence_set: Validation functions that should - hold on every tick - :param eventually_validation_sequence_set: Validation that should - eventually be true, before the test ends - :param test_timeout_s: The timeout for the test, if any eventually_validations - remain after the timeout, the test fails. - """ - - def stop_test(delay): - time.sleep(delay) - if self.thunderscope: - self.thunderscope.close() - - def __runner(): - time.sleep(LAUNCH_DELAY_S) - - test_end_time = time.time() + test_timeout_s - - while time.time() < test_end_time: - while True: - try: - world = self.world_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT - ) - break - except queue.Empty: - # If we timeout, that means full_system missed the last - # wrapper and robot status, lets resend it. - logger.warning( - f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." - ) - - # Validate - ( - eventually_validation_proto_set, - always_validation_proto_set, - ) = validation.run_validation_sequence_sets( - world, - eventually_validation_sequence_set, - always_validation_sequence_set, - ) - - if self.publish_validation_protos: - # Set the test name - eventually_validation_proto_set.test_name = self.test_name - always_validation_proto_set.test_name = self.test_name - - # Send out the validation proto to thunderscope - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set - ) - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set - ) - - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) - - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - stop_test(TEST_END_DELAY) - - def excepthook(args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. - - :param args: The args passed in from the hook - """ - stop_test(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception - - threading.excepthook = excepthook - - if self.thunderscope: - run_test_thread = threading.Thread(target=__runner, daemon=True) - run_test_thread.start() - self.thunderscope.show() - run_test_thread.join() - - if self.last_exception: - pytest.fail(str(ex.last_exception)) - - else: - __runner() - - -def get_runtime_dir(): - """Gets the base runtime directory for the test execution. - - TODO: Refactor #3744 - - Creates a new persistent directory for each test so that tests - running in parallel do not interfere with each other. - - :return: The path to the runtime directory. - """ - import uuid - - runtime_dir = os.path.join("/tmp", f"tbots_{uuid.uuid4().hex[:8]}") - os.makedirs(runtime_dir, exist_ok=True) - return runtime_dir - - -RUNTIME_DIR = get_runtime_dir() - - -def load_command_line_arguments(): - """Load in command-line arguments using argparse - - NOTE: Pytest has its own built in argument parser (conftest.py, pytest_addoption) - but it doesn't seem to play nicely with bazel. We just use argparse instead. - """ - parser = argparse.ArgumentParser(description="Run simulated or field pytests") - parser.add_argument( - "--simulator_runtime_dir", - type=str, - help="simulator runtime directory", - default=RUNTIME_DIR, - ) - parser.add_argument( - "--blue_full_system_runtime_dir", - type=str, - help="blue full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "blue"), - ) - parser.add_argument( - "--yellow_full_system_runtime_dir", - type=str, - help="yellow full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "yellow"), - ) - parser.add_argument( - "--layout", - action="store", - help="Which layout to run, if not specified the last layout will run", - ) - parser.add_argument( - "--debug_blue_full_system", - action="store_true", - default=False, - help="Debug blue full_system", - ) - parser.add_argument( - "--debug_yellow_full_system", - action="store_true", - default=False, - help="Debug yellow full_system", - ) - parser.add_argument( - "--debug_simulator", - action="store_true", - default=False, - help="Debug the simulator", - ) - parser.add_argument( - "--visualization_buffer_size", - action="store", - type=int, - default=5, - help="How many packets to buffer while rendering", - ) - parser.add_argument( - "--show_gamecontroller_logs", - action="store_true", - default=False, - help="How many packets to buffer while rendering", - ) - parser.add_argument( - "--run_field_test", - action="store_true", - default=False, - help="whether to run test as a field test instead of a simulated test", - ) - parser.add_argument( - "--test_filter", - action="store", - default="", - help="The test filter, if not specified all tests will run. " - + "See https://docs.pytest.org/en/latest/how-to/usage.html#specifying-tests-selecting-tests", - ) - - parser.add_argument( - "--interface", - action="store", - type=str, - default=None, - help="Which interface to communicate over", - ) - - parser.add_argument( - "--channel", - action="store", - type=int, - default=0, - help="Which channel to communicate over", - ) - - parser.add_argument( - "--estop_baudrate", - action="store", - type=int, - default=115200, - help="Estop Baudrate", - ) - - parser.add_argument( - "--run_yellow", - action="store_true", - default=False, - help="Run the test with friendly robots in yellow mode", - ) - - estop_group = parser.add_mutually_exclusive_group() - estop_group.add_argument( - "--keyboard_estop", - action="store_true", - default=False, - help="Allows the use of the spacebar as an estop instead of a physical one", - ) - estop_group.add_argument( - "--disable_communication", - action="store_true", - default=False, - help="Disables checking for estop plugged in (ONLY USE FOR LOCAL TESTING)", - ) - - return parser.parse_args() - - -@pytest.fixture -def field_test_runner(): - """Runs a field test - - :return: yields the runner to the test fixture - """ - simulator_proto_unix_io = ProtoUnixIO() - yellow_full_system_proto_unix_io = ProtoUnixIO() - blue_full_system_proto_unix_io = ProtoUnixIO() - args = load_command_line_arguments() - - # Grab the current test name to store the proto log for the test case - current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] - current_test = current_test.replace("]", "") - current_test = current_test.replace("[", "-") - - test_name = current_test.split("-")[0] - debug_full_sys = args.debug_blue_full_system - runtime_dir = f"{args.blue_full_system_runtime_dir}/test/{test_name}" - friendly_proto_unix_io = blue_full_system_proto_unix_io - - if args.run_yellow: - debug_full_sys = args.debug_yellow_full_system - runtime_dir = f"{args.yellow_full_system_runtime_dir}/test/{test_name}" - friendly_proto_unix_io = yellow_full_system_proto_unix_io - - estop_mode, estop_path = get_estop_config( - args.keyboard_estop, args.disable_communication - ) - - # Launch all binaries - with FullSystem( - "software/unix_full_system", - full_system_runtime_dir=runtime_dir, - debug_full_system=debug_full_sys, - friendly_colour_yellow=args.run_yellow, - should_restart_on_crash=False, - ) as friendly_fs, Gamecontroller( - # we would be using conventional port if and only if we are playing in robocup. - suppress_logs=(not args.show_gamecontroller_logs), - use_conventional_port=False, - ) as gamecontroller, WifiCommunicationManager( - current_proto_unix_io=friendly_proto_unix_io, - multicast_channel=getRobotMulticastChannel(args.channel), - should_setup_full_system=True, - interface=args.interface, - referee_port=gamecontroller.get_referee_port() - if gamecontroller - else SSL_REFEREE_PORT, - ) as wifi_communication_manager, RobotCommunication( - current_proto_unix_io=friendly_proto_unix_io, - communication_manager=wifi_communication_manager, - estop_mode=estop_mode, - estop_path=estop_path, - ) as rc_friendly: - friendly_fs.setup_proto_unix_io(friendly_proto_unix_io) - - gamecontroller.setup_proto_unix_io( - blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, - simulator_proto_unix_io=simulator_proto_unix_io, - ) - # Inject the proto unix ios into thunderscope and start the test - tscope = Thunderscope( - configure_field_test_view( - simulator_proto_unix_io=simulator_proto_unix_io, - blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, - yellow_is_friendly=args.run_yellow, - ), - layout_path=None, - ) - - # Set control mode for all robots to AI so that packets are sent to the robots - for robot_id in range(MAX_ROBOT_IDS_PER_SIDE): - rc_friendly.toggle_individual_robot_control_mode( - robot_id, - IndividualRobotMode.AI, - ) - - # connect the keyboard estop toggle to the key event if needed - if estop_mode == EstopMode.KEYBOARD_ESTOP: - tscope.keyboard_estop_shortcut.activated.connect( - rc_friendly.toggle_keyboard_estop - ) - # we call this method to enable estop automatically when a field test starts - rc_friendly.toggle_keyboard_estop() - logger.warning( - "\x1b[31;20m" - + "Keyboard Estop Enabled, robots will start moving automatically when test starts!" - + "\x1b[0m" - ) - - time.sleep(LAUNCH_DELAY_S) - runner = FieldTestRunner( - test_name=current_test, - blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, - gamecontroller=gamecontroller, - thunderscope=tscope, - is_yellow_friendly=args.run_yellow, - ) - - friendly_proto_unix_io.register_observer(World, runner.world_buffer) - - yield runner diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py new file mode 100644 index 0000000000..be853a8bbb --- /dev/null +++ b/src/software/gameplay_tests/field_test_runner.py @@ -0,0 +1,192 @@ +import queue +import threading +import time +from typing import override + +import pytest +from proto.ssl_gc_common_pb2 import Team +from proto.ssl_gc_state_pb2 import Command + +from proto.import_all_protos import ValidationProtoSet +from software.gameplay_tests.tbots_test_runner import TbotsTestRunner +from software.gameplay_tests.validation import validation +from software.logger.logger import create_logger + +logger = create_logger(__name__) + +WORLD_BUFFER_TIMEOUT = 5.0 +PROCESS_BUFFER_DELAY_S = 0.01 +PAUSE_AFTER_FAIL_DELAY_S = 3 +LAUNCH_DELAY_S = 0.1 +TEST_END_DELAY = 0.3 + + +class FieldTestRunner(TbotsTestRunner): + """Run a field test""" + + def __init__( + self, + test_name, + thunderscope, + blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io, + gamecontroller, + publish_validation_protos=True, + is_yellow_friendly=False, + ): + """Initialize the FieldTestRunner + + :param test_name: The name of the test to run + :param thunderscope: The Thunderscope to use, None if not used + :param blue_full_system_proto_unix_io: The blue full system proto unix io to use + :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use + :param gamecontroller: The gamecontroller context managed instance + :param publish_validation_protos: whether to publish validation protos + :param: is_yellow_friendly: if yellow is the friendly team + """ + super(FieldTestRunner, self).__init__( + test_name, + thunderscope, + blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io, + gamecontroller, + is_yellow_friendly, + ) + self.publish_validation_protos = publish_validation_protos + self.is_yellow_friendly = is_yellow_friendly + + logger.info("determining robots on field") + # survey field for available robot ids + try: + world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) + self.initial_world = world + self.friendly_robot_ids_field = [ + robot.id for robot in world.friendly_team.team_robots + ] + + logger.info(f"friendly team ids {self.friendly_robot_ids_field}") + + if len(self.friendly_robot_ids_field) == 0: + raise Exception("no friendly robots found on field") + + except queue.Empty: + raise Exception( + f"No Worlds were received with in {WORLD_BUFFER_TIMEOUT} seconds. \ + Please make sure atleast 1 robot and 1 ball is present on the field." + ) + + @override + def send_gamecontroller_command( + self, + gc_command: Command, + team: Team, + final_ball_placement_point=None, + ): + """Send a command to the gamecontroller + + :param gc_command: The command to send + :param team: The team which the command as attributed to + :param final_ball_placement_point: The ball placement point + """ + self.gamecontroller.send_ci_input( + gc_command=gc_command, + team=team, + final_ball_placement_point=final_ball_placement_point, + ) + + @override + def run_test( + self, + setup=lambda: None, + always_validation_sequence_set=[[]], + eventually_validation_sequence_set=[[]], + test_timeout_s=3, + ): + """Run a test. In a field test this means beginning validation. + + :param always_validation_sequence_set: Validation functions that should + hold on every tick + :param eventually_validation_sequence_set: Validation that should + eventually be true, before the test ends + :param test_timeout_s: The timeout for the test, if any eventually_validations + remain after the timeout, the test fails. + """ + + def stop_test(delay): + time.sleep(delay) + if self.thunderscope: + self.thunderscope.close() + + def __runner(): + time.sleep(LAUNCH_DELAY_S) + + test_end_time = time.time() + test_timeout_s + + while time.time() < test_end_time: + while True: + try: + world = self.world_buffer.get( + block=True, timeout=WORLD_BUFFER_TIMEOUT + ) + break + except queue.Empty: + # If we timeout, that means full_system missed the last + # wrapper and robot status, lets resend it. + logger.warning( + f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." + ) + + # Validate + ( + eventually_validation_proto_set, + always_validation_proto_set, + ) = validation.run_validation_sequence_sets( + world, + eventually_validation_sequence_set, + always_validation_sequence_set, + ) + + if self.publish_validation_protos: + # Set the test name + eventually_validation_proto_set.test_name = self.test_name + always_validation_proto_set.test_name = self.test_name + + # Send out the validation proto to thunderscope + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, eventually_validation_proto_set + ) + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, always_validation_proto_set + ) + + # Check that all always validations are always valid + validation.check_validation(always_validation_proto_set) + + # Check that all eventually validations are eventually valid + validation.check_validation(eventually_validation_proto_set) + stop_test(TEST_END_DELAY) + + def excepthook(args): + """This function is _critical_ for show_thunderscope to work. + If the test Thread will raises an exception we won't be able to close + the window from the main thread. + + :param args: The args passed in from the hook + """ + stop_test(delay=PAUSE_AFTER_FAIL_DELAY_S) + self.last_exception = args.exc_value + raise self.last_exception + + threading.excepthook = excepthook + + if self.thunderscope: + run_test_thread = threading.Thread(target=__runner, daemon=True) + run_test_thread.start() + self.thunderscope.show() + run_test_thread.join() + + if self.last_exception: + pytest.fail(str(self.last_exception)) + + else: + __runner() diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index d683157210..779568bea3 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -1,16 +1,33 @@ -import time import os +import time import pytest - -from software.thunderscope.thunderscope import Thunderscope -from software.thunderscope.proto_unix_io import ProtoUnixIO +from software.py_constants import ( + MAX_ROBOT_IDS_PER_SIDE, + SSL_REFEREE_PORT, + getRobotMulticastChannel, +) + +from proto.import_all_protos import World +from software.gameplay_tests.field_test_runner import FieldTestRunner +from software.gameplay_tests.simulated_test_runner import SimulatedTestRunner +from software.gameplay_tests.util import load_command_line_arguments +from software.logger.logger import create_logger from software.thunderscope.binary_context_managers.full_system import FullSystem -from software.thunderscope.binary_context_managers.simulator import Simulator from software.thunderscope.binary_context_managers.game_controller import Gamecontroller -from software.thunderscope.thunderscope_config import configure_simulated_test_view -from software.gameplay_tests.util import load_command_line_arguments -from software.gameplay_tests.simulated_test_runner import SimulatedTestRunner +from software.thunderscope.binary_context_managers.simulator import Simulator +from software.thunderscope.constants import EstopMode, IndividualRobotMode +from software.thunderscope.estop_helpers import get_estop_config +from software.thunderscope.proto_unix_io import ProtoUnixIO +from software.thunderscope.robot_communication import RobotCommunication +from software.thunderscope.thunderscope import Thunderscope +from software.thunderscope.thunderscope_config import ( + configure_field_test_view, + configure_simulated_test_view, +) +from software.thunderscope.wifi_communication_manager import WifiCommunicationManager + +logger = create_logger(__name__) LAUNCH_DELAY_S = 0.1 @@ -18,8 +35,6 @@ def get_runtime_dir(): """Gets the base runtime directory for the test execution. - TODO: Refactor #3744 - Creates a new persistent directory for each test so that tests running in parallel do not interfere with each other. @@ -118,3 +133,116 @@ def simulated_test_runner(): ) yield runner + + +@pytest.fixture +def field_test_runner(): + """Runs a field test + + :return: yields the runner to the test fixture + """ + simulator_proto_unix_io = ProtoUnixIO() + yellow_full_system_proto_unix_io = ProtoUnixIO() + blue_full_system_proto_unix_io = ProtoUnixIO() + args = load_command_line_arguments() + + # Grab the current test name to store the proto log for the test case + current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] + current_test = current_test.replace("]", "") + current_test = current_test.replace("[", "-") + + test_name = current_test.split("-")[0] + debug_full_sys = args.debug_blue_full_system + runtime_dir = f"{args.blue_full_system_runtime_dir}/test/{test_name}" + friendly_proto_unix_io = blue_full_system_proto_unix_io + + if args.run_yellow: + debug_full_sys = args.debug_yellow_full_system + runtime_dir = f"{args.yellow_full_system_runtime_dir}/test/{test_name}" + friendly_proto_unix_io = yellow_full_system_proto_unix_io + + estop_mode, estop_path = get_estop_config( + args.keyboard_estop, args.disable_communication + ) + + # Launch all binaries + with ( + FullSystem( + "software/unix_full_system", + full_system_runtime_dir=runtime_dir, + debug_full_system=debug_full_sys, + friendly_colour_yellow=args.run_yellow, + should_restart_on_crash=False, + ) as friendly_fs, + Gamecontroller( + # we would be using conventional port if and only if we are playing in robocup. + suppress_logs=(not args.show_gamecontroller_logs), + use_conventional_port=False, + ) as gamecontroller, + WifiCommunicationManager( + current_proto_unix_io=friendly_proto_unix_io, + multicast_channel=getRobotMulticastChannel(args.channel), + should_setup_full_system=True, + interface=args.interface, + referee_port=gamecontroller.get_referee_port() + if gamecontroller + else SSL_REFEREE_PORT, + ) as wifi_communication_manager, + RobotCommunication( + current_proto_unix_io=friendly_proto_unix_io, + communication_manager=wifi_communication_manager, + estop_mode=estop_mode, + estop_path=estop_path, + ) as rc_friendly, + ): + friendly_fs.setup_proto_unix_io(friendly_proto_unix_io) + + gamecontroller.setup_proto_unix_io( + blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, + simulator_proto_unix_io=simulator_proto_unix_io, + ) + # Inject the proto unix ios into thunderscope and start the test + tscope = Thunderscope( + configure_field_test_view( + simulator_proto_unix_io=simulator_proto_unix_io, + blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, + yellow_is_friendly=args.run_yellow, + ), + layout_path=None, + ) + + # Set control mode for all robots to AI so that packets are sent to the robots + for robot_id in range(MAX_ROBOT_IDS_PER_SIDE): + rc_friendly.toggle_individual_robot_control_mode( + robot_id, + IndividualRobotMode.AI, + ) + + # connect the keyboard estop toggle to the key event if needed + if estop_mode == EstopMode.KEYBOARD_ESTOP: + tscope.keyboard_estop_shortcut.activated.connect( + rc_friendly.toggle_keyboard_estop + ) + # we call this method to enable estop automatically when a field test starts + rc_friendly.toggle_keyboard_estop() + logger.warning( + "\x1b[31;20m" + + "Keyboard Estop Enabled, robots will start moving automatically when test starts!" + + "\x1b[0m" + ) + + time.sleep(LAUNCH_DELAY_S) + runner = FieldTestRunner( + test_name=current_test, + blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, + yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, + gamecontroller=gamecontroller, + thunderscope=tscope, + is_yellow_friendly=args.run_yellow, + ) + + friendly_proto_unix_io.register_observer(World, runner.world_buffer) + + yield runner diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index c571edff82..18dfbb6a8b 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -251,10 +251,11 @@ def runner( @override def run_test( self, - always_validation_sequence_set=[], - eventually_validation_sequence_set=[], - setup=(lambda: None), + setup=lambda: None, + always_validation_sequence_set=[[]], + eventually_validation_sequence_set=[[]], test_timeout_s=3, + # TODO (#3744): fix these extra arguments tick_duration_s=0.0166, # Default to 60hz ci_cmd_with_delay=[], run_till_end=True, diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 03e7d4ae85..7a6caef9c4 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -1,11 +1,8 @@ from proto.import_all_protos import * -from software.logger.logger import create_logger from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer from abc import abstractmethod from typing import Any -logger = create_logger(__name__) - class TbotsTestRunner: """An abstract class that represents a test runner""" @@ -120,34 +117,18 @@ def set_plays(self, blue_play: PlayName, yellow_play: PlayName): self.blue_full_system_proto_unix_io.send_proto(Play, Play(name=blue_play)) self.yellow_full_system_proto_unix_io.send_proto(Play, Play(name=yellow_play)) - def _create_assigned_tactic_params(self, tactics: dict[int, Any]): - """Converts dict of tactics to AssignedTacticPlayControlParams message - - :param tactics: dict of robot_id -> tactic - """ - params = AssignedTacticPlayControlParams() - - # Checks which oneof field in Tactic to assign the specified tactic to - for robot_id, specific_tactic in tactics.items(): - tactic = params.assigned_tactics[robot_id] - for field in tactic.DESCRIPTOR.oneofs_by_name["tactic"].fields: - if field.message_type == specific_tactic.DESCRIPTOR: - getattr(tactic, field.name).CopyFrom(specific_tactic) - break - - return params - @abstractmethod - def set_world_state(self, worldstate: WorldState): - """Sets the worldstate for the given team + def set_world_state(self, world_state: WorldState): + """Sets the initial world state of the test. - :param worldstate: the worldstate proto to use + :param world_state: The WorldState proto to use """ raise NotImplementedError("abstract class method called set_world_state") @abstractmethod def run_test( self, + setup=lambda: None, always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, @@ -159,3 +140,20 @@ def run_test( :param test_timeout_s: how long the test will run """ raise NotImplementedError("abstract method run_test called from base class") + + def _create_assigned_tactic_params(self, tactics: dict[int, Any]): + """Converts dict of tactics to AssignedTacticPlayControlParams message + + :param tactics: dict of robot_id -> tactic + """ + params = AssignedTacticPlayControlParams() + + # Checks which oneof field in Tactic to assign the specified tactic to + for robot_id, specific_tactic in tactics.items(): + tactic = params.assigned_tactics[robot_id] + for field in tactic.DESCRIPTOR.oneofs_by_name["tactic"].fields: + if field.message_type == specific_tactic.DESCRIPTOR: + getattr(tactic, field.name).CopyFrom(specific_tactic) + break + + return params From 33a39dde153ac62116928ece1d28b7788ef828fb Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Mon, 15 Jun 2026 13:10:59 -0700 Subject: [PATCH 06/45] Fix field test imports --- src/software/gameplay_tests/movement_robot_field_test.py | 3 ++- src/software/gameplay_tests/passing_field_test.py | 3 ++- src/software/gameplay_tests/pivot_kick_field_test.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index 475d83f7c6..f59abd6394 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -1,5 +1,6 @@ from proto.import_all_protos import * -from software.gameplay_tests.field_test_fixture import * +from software.gameplay_tests.fixture import * +from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT from software.gameplay_tests.util import pytest_main from software.logger.logger import create_logger diff --git a/src/software/gameplay_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py index 2e6f9dac3b..16a87ae4fe 100644 --- a/src/software/gameplay_tests/passing_field_test.py +++ b/src/software/gameplay_tests/passing_field_test.py @@ -1,6 +1,7 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * -from software.gameplay_tests.field_test_fixture import * +from software.gameplay_tests.fixture import * +from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py index 7db112fb20..077447c6d8 100644 --- a/src/software/gameplay_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -1,6 +1,7 @@ import math from proto.import_all_protos import * -from software.gameplay_tests.field_test_fixture import * +from software.gameplay_tests.fixture import * +from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT from software.logger.logger import create_logger from software.gameplay_tests.util import pytest_main From 85b71bfaba4c6c71d71e6857afb7d2e0b15a43cf Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Mon, 15 Jun 2026 14:38:18 -0700 Subject: [PATCH 07/45] Fix interface of tbots test runner and field test runner --- .../ball_placement_play_test.py | 12 ++++------ .../crease_defense_play_test.py | 3 +-- .../hl/stp/play/defense/defense_play_test.py | 12 ++++------ .../enemy_ball_placement_play_test.py | 6 ++--- .../enemy_free_kick_play_test.py | 6 ++--- .../hl/stp/play/example/example_play_test.py | 3 +-- .../stp/play/free_kick/free_kick_play_test.py | 3 +-- .../hl/stp/play/halt_play/halt_play_test.py | 6 ++--- .../kickoff_enemy/kickoff_enemy_play_test.py | 6 ++--- .../kickoff_friendly_play_test.py | 6 ++--- .../ai/hl/stp/play/kickoff_play_test.py | 4 ++-- .../hl/stp/play/offense/offense_play_test.py | 23 ++++++++---------- .../ai/hl/stp/play/passing_sim_test.py | 11 ++++----- .../penalty_kick_enemy_play_test.py | 3 +-- .../shoot_or_chip/shoot_or_chip_play_test.py | 3 +-- .../shoot_or_pass/shoot_or_pass_play_test.py | 3 +-- src/software/ai/hl/stp/play/stop_play_test.py | 3 +-- .../tactic/attacker/attacker_tactic_test.py | 9 +++---- .../ai/hl/stp/tactic/chip/chip_tactic_test.py | 3 +-- .../crease_defender_tactic_test.py | 24 +++++++------------ .../stp/tactic/dribble/dribble_tactic_test.py | 21 ++++++---------- .../stp/tactic/goalie/goalie_tactic_test.py | 12 ++++------ .../ai/hl/stp/tactic/halt/halt_tactic_test.py | 3 +-- .../ai/hl/stp/tactic/kick/kick_tactic_test.py | 3 +-- .../pass_defender_tactic_test.py | 18 +++++--------- .../penalty_kick/penalty_kick_tactic_test.py | 4 ++-- .../pivot_kick/pivot_kick_tactic_test.py | 6 ++--- .../tactic/receiver/receiver_tactic_test.py | 3 +-- .../trajectory/simulated_hrvo_test.py | 6 ++--- .../gameplay_tests/field_test_runner.py | 13 +++++++++- .../simulated_test_ball_model.py | 8 +++---- .../gameplay_tests/simulated_test_runner.py | 6 ++--- .../gameplay_tests/tbots_test_runner.py | 6 +++++ 33 files changed, 104 insertions(+), 154 deletions(-) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index e786c0de9a..77fc8d3f29 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -194,19 +194,15 @@ def run_ball_placement_scenario( "ball_placement_point": ball_placement_point, } ], - inv_always_validation_sequence_set=[[]], - inv_eventually_validation_sequence_set=placement_eventually_validation_sequence_set, - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=placement_eventually_validation_sequence_set, + always_validation_sequence_set=[[]], + eventually_validation_sequence_set=placement_eventually_validation_sequence_set, test_timeout_s=[30], ) simulated_test_runner.run_test( # setup argument isn't passed to preserve world state from previous test run - inv_always_validation_sequence_set=drop_ball_always_validation_sequence_set, - inv_eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, - ag_always_validation_sequence_set=drop_ball_always_validation_sequence_set, - ag_eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, + always_validation_sequence_set=drop_ball_always_validation_sequence_set, + eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, test_timeout_s=[10], ) diff --git a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py index 68bfbc1b92..3402432384 100644 --- a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py +++ b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py @@ -70,8 +70,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/defense/defense_play_test.py b/src/software/ai/hl/stp/play/defense/defense_play_test.py index 854c521278..54ef71b02e 100644 --- a/src/software/ai/hl/stp/play/defense/defense_play_test.py +++ b/src/software/ai/hl/stp/play/defense/defense_play_test.py @@ -60,18 +60,16 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0, 1, 2, 3, 4], # The aggregate test runs 5 times - inv_always_validation_sequence_set=[ + always_validation_sequence_set=[ [ BallNeverEntersRegion( regions=[tbots_cpp.Field.createSSLDivisionBField().friendlyGoal()] ) ] ], - inv_eventually_validation_sequence_set=[ + eventually_validation_sequence_set=[ [FriendlyEventuallyHasBallPossession(tolerance=0.2)] ], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[[]], test_timeout_s=20, ) @@ -125,18 +123,16 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0, 1, 2, 3, 4], # The aggregate test runs 5 times - inv_always_validation_sequence_set=[ + always_validation_sequence_set=[ [ BallNeverEntersRegion( regions=[tbots_cpp.Field.createSSLDivisionBField().friendlyGoal()] ) ] ], - inv_eventually_validation_sequence_set=[ + eventually_validation_sequence_set=[ [FriendlyEventuallyHasBallPossession(tolerance=0.1)] ], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[[]], test_timeout_s=30, ) diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py index 6c3b55293f..7c4d02199a 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py @@ -73,10 +73,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0], - inv_always_validation_sequence_set=always_validation_sequence_set, - inv_eventually_validation_sequence_set=[[]], - ag_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=[[]], + always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=[[]], test_timeout_s=15, ) diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py index b539afb1c3..0a96f33a23 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py @@ -142,10 +142,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0, 1, 2], - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=8, ) diff --git a/src/software/ai/hl/stp/play/example/example_play_test.py b/src/software/ai/hl/stp/play/example/example_play_test.py index a6d4f0ecbc..4de736fbf4 100644 --- a/src/software/ai/hl/stp/play/example/example_play_test.py +++ b/src/software/ai/hl/stp/play/example/example_play_test.py @@ -72,8 +72,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py index 4656e1756f..7cf9631276 100644 --- a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py @@ -89,8 +89,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index 343255cc9b..4ba5d1f0ae 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -53,12 +53,10 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, params=[0], - inv_always_validation_sequence_set=[[]], - inv_eventually_validation_sequence_set=[ + always_validation_sequence_set=[[]], + eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1e-3)] ], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1e-3)] ], ci_cmd_with_delay=[ diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index df0ba5cd3e..69a7ce3b9f 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -138,10 +138,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index fd49a3d78c..a100f61cd3 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -138,10 +138,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=60, ) diff --git a/src/software/ai/hl/stp/play/kickoff_play_test.py b/src/software/ai/hl/stp/play/kickoff_play_test.py index 79e9d44121..20923bb136 100644 --- a/src/software/ai/hl/stp/play/kickoff_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_play_test.py @@ -136,8 +136,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/offense/offense_play_test.py b/src/software/ai/hl/stp/play/offense/offense_play_test.py index 36cba8f00c..9a039954ea 100644 --- a/src/software/ai/hl/stp/play/offense/offense_play_test.py +++ b/src/software/ai/hl/stp/play/offense/offense_play_test.py @@ -10,9 +10,9 @@ def test_offense_play(simulated_test_runner): - def setup(start_point): - ball_initial_pos = start_point + start_point = tbots_cpp.Point(-4.4, 2.9) + def setup(): blue_bots = [ tbots_cpp.Point(-4.5, 3.0), tbots_cpp.Point(-2, 1.5), @@ -39,7 +39,7 @@ def setup(start_point): create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, - ball_location=ball_initial_pos, + ball_location=start_point, ball_velocity=tbots_cpp.Vector(0, 0), ), ) @@ -58,24 +58,21 @@ def setup(start_point): field = tbots_cpp.Field.createSSLDivisionBField() # Always Validation - inv_always_validation_sequence_set = [ + always_validation_sequence_set = [ [BallAlwaysStaysInRegion(regions=[field.fieldBoundary()])], [NeverExcessivelyDribbles()], + [FriendlyAlwaysHasBallPossession()], ] - ag_always_validation_sequence_set = [[FriendlyAlwaysHasBallPossession()]] - # Eventually Validation - inv_eventually_validation_sequence_set = [[]] - ag_eventually_validation_sequence_set = [[FriendlyTeamEventuallyScored()]] + eventually_validation_sequence_set = [ + [FriendlyTeamEventuallyScored()], + ] simulated_test_runner.run_test( - params=[tbots_cpp.Point(-4.4, 2.9)], setup=setup, - inv_eventually_validation_sequence_set=inv_eventually_validation_sequence_set, - inv_always_validation_sequence_set=inv_always_validation_sequence_set, - ag_eventually_validation_sequence_set=ag_eventually_validation_sequence_set, - ag_always_validation_sequence_set=ag_always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=15, run_till_end=True, ) diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index 72c8d818cc..9ef89a483a 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -244,7 +244,7 @@ def test_passing_receive_speed( ] simulated_test_runner.run_test( - setup=lambda param: setup_pass_and_robots( + setup=lambda: setup_pass_and_robots( ball_initial_position=ball_initial_position, ball_initial_velocity=ball_initial_velocity, attacker_robot_position=attacker_robot_position, @@ -254,9 +254,8 @@ def test_passing_receive_speed( simulated_test_runner=simulated_test_runner, receive_pass=True, ), - params=[0], - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, run_till_end=False, ) @@ -380,8 +379,8 @@ def test_passing_no_backwards_passes( ] simulated_test_runner.run_test( - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, run_till_end=False, ) diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py index 8308986390..cd11ddc189 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py @@ -171,8 +171,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=20, ) diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py index 5d96cec22e..0037966d0f 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py @@ -62,8 +62,7 @@ def setup(*args): # TODO (#3651): add validations simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[[]], + eventually_validation_sequence_set=[[]], test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 352c4993b0..4d2819287b 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -57,8 +57,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=15, run_till_end=False, ) diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index e8eaa7c8ad..ce22bb68aa 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -165,8 +165,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_always_validation_sequence_set=always_stop_play_rules, - ag_always_validation_sequence_set=always_stop_play_rules, + always_validation_sequence_set=always_stop_play_rules, test_timeout_s=6, ) diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index 1b07b8ac7a..89e243bd11 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -170,8 +170,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, test_timeout_s=7, ) @@ -281,8 +280,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -382,8 +380,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, test_timeout_s=9, ) diff --git a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py index 95a8f39fcf..5f2c2f310f 100644 --- a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py @@ -84,8 +84,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py index 2430b0e08d..a50171e50e 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py @@ -52,8 +52,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_always_validation_sequence_set=always_validations, - ag_always_validation_sequence_set=always_validations, + always_validation_sequence_set=always_validations, ) @@ -174,8 +173,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=5, ) @@ -259,10 +257,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=4, ) @@ -356,10 +352,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=3, ) @@ -437,10 +431,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index 33bf16d262..6b3bf732dd 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -177,8 +177,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=25, run_till_end=False, ) @@ -330,10 +329,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -378,8 +375,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=10, ) @@ -429,8 +425,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, ) @@ -475,10 +470,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, - inv_always_validation_sequence_set=always_validations, - ag_always_validation_sequence_set=always_validations, + eventually_validation_sequence_set=eventually_validations, + always_validation_sequence_set=always_validations, ) diff --git a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py index daa2b6e0e7..cad3b0dc5c 100644 --- a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py @@ -155,10 +155,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -239,10 +237,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, test_timeout_s=8, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py index 66b28f313b..0fa61787f8 100644 --- a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py @@ -61,8 +61,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py index dd9c2f8cfb..2a76491057 100644 --- a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py @@ -71,8 +71,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validations, - ag_eventually_validation_sequence_set=eventually_validations, + eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py index 9648711c5b..a6974ed700 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py @@ -73,10 +73,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -164,10 +162,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -285,10 +281,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index 9aa2a4f9a3..3a82016412 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -82,8 +82,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py index 3810a4d3b4..f5e8ee7356 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py @@ -69,10 +69,8 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_always_validation_sequence_set=[[]], + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=[[]], test_timeout_s=5, ) diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index 3696066ec3..52156efbf4 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -234,8 +234,7 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - ag_eventually_validation_sequence_set=eventually_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index e5c1eadb06..40e9be76bd 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -498,10 +498,8 @@ def test_robot_movement( simulated_test_runner, ), params=[0], - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, - ag_eventually_validation_sequence_set=[[]], - ag_always_validation_sequence_set=[[]], + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=timeout_s, run_till_end=run_till_end, ) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index be853a8bbb..aed73b1337 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -7,7 +7,7 @@ from proto.ssl_gc_common_pb2 import Team from proto.ssl_gc_state_pb2 import Command -from proto.import_all_protos import ValidationProtoSet +from proto.import_all_protos import ValidationProtoSet, WorldState from software.gameplay_tests.tbots_test_runner import TbotsTestRunner from software.gameplay_tests.validation import validation from software.logger.logger import create_logger @@ -94,6 +94,13 @@ def send_gamecontroller_command( final_ball_placement_point=final_ball_placement_point, ) + @override + def set_world_state(self, world_state: WorldState): + logger.warning( + "set_world_state called in field test: " + "assuming robots are initialized according to the given parameters" + ) + @override def run_test( self, @@ -101,6 +108,10 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, + # Unused parameters, here for compatibility with sim tests + tick_duration_s=None, + ci_cmd_with_delay=None, + run_till_end=None, ): """Run a test. In a field test this means beginning validation. diff --git a/src/software/gameplay_tests/simulated_test_ball_model.py b/src/software/gameplay_tests/simulated_test_ball_model.py index 08ee1159dd..87f7b79696 100644 --- a/src/software/gameplay_tests/simulated_test_ball_model.py +++ b/src/software/gameplay_tests/simulated_test_ball_model.py @@ -104,8 +104,8 @@ def test_simulator_move_ball( simulated_test_runner.run_test( test_timeout_s=8, - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) @@ -181,8 +181,8 @@ def test_ball_robot_collision(simulated_test_runner): ] simulated_test_runner.run_test( - inv_eventually_validation_sequence_set=eventually_validation_sequence_set, - inv_always_validation_sequence_set=always_validation_sequence_set, + eventually_validation_sequence_set=eventually_validation_sequence_set, + always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 18dfbb6a8b..290e990532 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -255,11 +255,9 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, - # TODO (#3744): fix these extra arguments - tick_duration_s=0.0166, # Default to 60hz + tick_duration_s=0.0166, ci_cmd_with_delay=[], run_till_end=True, - **kwargs, ): """Helper function to run a test, with thunderscope if enabled @@ -267,7 +265,7 @@ def run_test( :param eventually_validation_sequence_set: validation that should eventually be true :param setup: function that sets up the World state and the gamecontroller before running the test :param test_timeout_s: how long the test should run before timing out - :param tick_duration_s: length of a tick + :param tick_duration_s: length of a simulation tick :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. [ (time, command, team), diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 7a6caef9c4..87d30cd5ef 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -132,12 +132,18 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, + tick_duration_s=0.0166, + ci_cmd_with_delay=[], + run_till_end=True, ): """Begins validating a test based on incoming world protos :param always_validation_sequence_set: validation set that must always be true :param eventually_validation_sequence_set: validation set that must eventually be true :param test_timeout_s: how long the test will run + :param tick_duration_s: length of a simulation tick (used in simulated tests) + :param ci_cmd_with_delay: timed CI commands (used in simulated tests) + :param run_till_end: if true, test runs till the end even if eventually validation passes """ raise NotImplementedError("abstract method run_test called from base class") From bfa7db45ecd742f4976aa916f2f39aa794cd2e31 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Tue, 16 Jun 2026 15:04:30 -0700 Subject: [PATCH 08/45] Use get_runtime_dir actually --- src/software/gameplay_tests/fixture.py | 18 ------------------ src/software/gameplay_tests/util.py | 25 ++++++++++++++++++++++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 779568bea3..54bd50c769 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -32,24 +32,6 @@ LAUNCH_DELAY_S = 0.1 -def get_runtime_dir(): - """Gets the base runtime directory for the test execution. - - Creates a new persistent directory for each test so that tests - running in parallel do not interfere with each other. - - :return: The path to the runtime directory. - """ - import uuid - - runtime_dir = os.path.join("/tmp", f"tbots_{uuid.uuid4().hex[:8]}") - os.makedirs(runtime_dir, exist_ok=True) - return runtime_dir - - -RUNTIME_DIR = get_runtime_dir() - - @pytest.fixture def simulated_test_runner(): args = load_command_line_arguments() diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index afd524b486..80cc01fd96 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -1,9 +1,25 @@ import argparse +import os import sys import pytest +def get_runtime_dir(): + """Gets the base runtime directory for the test execution. + + Creates a new persistent directory for each test so that tests + running in parallel do not interfere with each other. + + :return: The path to the runtime directory. + """ + import uuid + + runtime_dir = os.path.join("/tmp", f"tbots_{uuid.uuid4().hex[:8]}") + os.makedirs(runtime_dir, exist_ok=True) + return runtime_dir + + def load_command_line_arguments(allow_unrecognized: bool = False): """Load in command-line arguments using argparse @@ -12,6 +28,9 @@ def load_command_line_arguments(allow_unrecognized: bool = False): :param allow_unrecognized: if true, does not raise an error for unrecognized arguments """ + + RUNTIME_DIR = get_runtime_dir() + parser = argparse.ArgumentParser( description="Run simulated or field gameplay tests" ) @@ -37,13 +56,13 @@ def load_command_line_arguments(allow_unrecognized: bool = False): "--blue_full_system_runtime_dir", type=str, help="blue full_system runtime directory", - default="/tmp/tbots/blue", + default=os.path.join(RUNTIME_DIR, "blue"), ) general_group.add_argument( "--yellow_full_system_runtime_dir", type=str, help="yellow full_system runtime directory", - default="/tmp/tbots/yellow", + default=os.path.join(RUNTIME_DIR, "yellow"), ) general_group.add_argument( "--layout", @@ -80,7 +99,7 @@ def load_command_line_arguments(allow_unrecognized: bool = False): "--simulator_runtime_dir", type=str, help="simulator runtime directory", - default="/tmp/tbots", + default=RUNTIME_DIR, ) simulated_group.add_argument( "--debug_simulator", From a49d25478ede390ef977d3d5f3a937f93796dcf5 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Tue, 16 Jun 2026 15:08:32 -0700 Subject: [PATCH 09/45] Update tests with new interface of test runners --- .../ball_placement/ball_placement_play_test.py | 16 +++++----------- .../crease_defense/crease_defense_play_test.py | 2 +- .../ai/hl/stp/play/defense/defense_play_test.py | 6 ++---- .../enemy_ball_placement_play_test.py | 3 +-- .../enemy_free_kick/enemy_free_kick_play_test.py | 3 +-- .../ai/hl/stp/play/example/example_play_test.py | 2 +- .../hl/stp/play/free_kick/free_kick_play_test.py | 2 +- .../ai/hl/stp/play/halt_play/halt_play_test.py | 6 +----- .../kickoff_enemy/kickoff_enemy_play_test.py | 2 +- .../kickoff_friendly_play_test.py | 2 +- src/software/ai/hl/stp/play/kickoff_play_test.py | 2 +- .../penalty_kick_enemy_play_test.py | 2 +- .../shoot_or_chip/shoot_or_chip_play_test.py | 2 +- .../shoot_or_pass/shoot_or_pass_play_test.py | 2 +- src/software/ai/hl/stp/play/stop_play_test.py | 2 +- .../stp/tactic/attacker/attacker_tactic_test.py | 6 +++--- .../ai/hl/stp/tactic/chip/chip_tactic_test.py | 2 +- .../crease_defender_tactic_test.py | 10 +++++----- .../hl/stp/tactic/dribble/dribble_tactic_test.py | 10 +++++----- .../hl/stp/tactic/goalie/goalie_tactic_test.py | 4 ++-- .../ai/hl/stp/tactic/halt/halt_tactic_test.py | 2 +- .../ai/hl/stp/tactic/kick/kick_tactic_test.py | 2 +- .../ai/hl/stp/tactic/move/move_tactic_test.py | 10 +++++----- .../pass_defender/pass_defender_tactic_test.py | 6 +++--- .../penalty_kick/penalty_kick_tactic_test.py | 2 +- .../tactic/pivot_kick/pivot_kick_tactic_test.py | 2 +- .../stp/tactic/receiver/receiver_tactic_test.py | 2 +- .../navigator/trajectory/simulated_hrvo_test.py | 3 +-- src/software/gameplay_tests/BUILD | 6 +++--- .../sensor_fusion/filter/ball_occlusion_test.py | 2 +- 30 files changed, 54 insertions(+), 69 deletions(-) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index 77fc8d3f29..3a897c08a6 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -182,28 +182,22 @@ def run_ball_placement_scenario( ] simulated_test_runner.run_test( - setup=lambda test_setup_arg: ball_placement_play_setup( - test_setup_arg["ball_start_point"], - test_setup_arg["ball_placement_point"], + setup=lambda: ball_placement_play_setup( + ball_start_point, + ball_placement_point, simulated_test_runner, blue_only, ), - params=[ - { - "ball_start_point": ball_start_point, - "ball_placement_point": ball_placement_point, - } - ], always_validation_sequence_set=[[]], eventually_validation_sequence_set=placement_eventually_validation_sequence_set, - test_timeout_s=[30], + test_timeout_s=30, ) simulated_test_runner.run_test( # setup argument isn't passed to preserve world state from previous test run always_validation_sequence_set=drop_ball_always_validation_sequence_set, eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, - test_timeout_s=[10], + test_timeout_s=10, ) diff --git a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py index 3402432384..594e9e05cc 100644 --- a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py +++ b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py @@ -17,7 +17,7 @@ def test_crease_defense_play(simulated_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() goalie_position = tbots_cpp.Point(-4.5, 0) - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/play/defense/defense_play_test.py b/src/software/ai/hl/stp/play/defense/defense_play_test.py index 54ef71b02e..6288f92160 100644 --- a/src/software/ai/hl/stp/play/defense/defense_play_test.py +++ b/src/software/ai/hl/stp/play/defense/defense_play_test.py @@ -34,7 +34,7 @@ ], ) def test_defense_play_ball_steal(simulated_test_runner, blue_bots, yellow_bots): - def setup(*args): + def setup(): ball_initial_pos = tbots_cpp.Point(0.93, 0) simulated_test_runner.set_world_state( @@ -59,7 +59,6 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - params=[0, 1, 2, 3, 4], # The aggregate test runs 5 times always_validation_sequence_set=[ [ BallNeverEntersRegion( @@ -97,7 +96,7 @@ def setup(*args): ], ) def test_defense_play(simulated_test_runner, blue_bots, yellow_bots): - def setup(*args): + def setup(): ball_initial_pos = tbots_cpp.Point(0.9, 2.85) simulated_test_runner.set_world_state( @@ -122,7 +121,6 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - params=[0, 1, 2, 3, 4], # The aggregate test runs 5 times always_validation_sequence_set=[ [ BallNeverEntersRegion( diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py index 7c4d02199a..82b988dc91 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py @@ -20,7 +20,7 @@ def test_two_ai_ball_placement( simulated_test_runner, ball_start_point, ball_placement_point ): - def setup(*args): + def setup(): blue_bots = [ tbots_cpp.Point(-4.5, 0), tbots_cpp.Point(-4, 0.5), @@ -72,7 +72,6 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - params=[0], always_validation_sequence_set=always_validation_sequence_set, eventually_validation_sequence_set=[[]], test_timeout_s=15, diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py index 0a96f33a23..702ea07b8d 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py @@ -97,7 +97,7 @@ def test_enemy_free_kick_play( simulated_test_runner, blue_bots, yellow_bots, ball_initial_pos ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, @@ -141,7 +141,6 @@ def setup(*args): simulated_test_runner.run_test( setup=setup, - params=[0, 1, 2], eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=8, diff --git a/src/software/ai/hl/stp/play/example/example_play_test.py b/src/software/ai/hl/stp/play/example/example_play_test.py index 4de736fbf4..adbd6e3ff3 100644 --- a/src/software/ai/hl/stp/play/example/example_play_test.py +++ b/src/software/ai/hl/stp/play/example/example_play_test.py @@ -13,7 +13,7 @@ def test_example_play(simulated_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) - def setup(*args): + def setup(): blue_bots = [ tbots_cpp.Point(-3, 2.5), tbots_cpp.Point(-3, 1.5), diff --git a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py index 7cf9631276..870b68aff7 100644 --- a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py @@ -30,7 +30,7 @@ ], ) def test_free_kick_play_friendly(ball_initial_pos, must_score, simulated_test_runner): - def setup(*args): + def setup(): blue_bots = [ tbots_cpp.Point(-4.5, 0), tbots_cpp.Point(-3, 1.5), diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index 4ba5d1f0ae..b5b2316b7c 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -8,7 +8,7 @@ # TODO issue #2599 - Remove Duration parameter from test # @pytest.mark.parametrize("run_enemy_ai,test_duration", [(False, 20), (True, 20)]) def test_halt_play(simulated_test_runner): - def setup(*args): + def setup(): ball_initial_pos = tbots_cpp.Point(0, 0) blue_bots = [ @@ -49,15 +49,11 @@ def setup(*args): gc_command=Command.Type.FORCE_START, team=Team.UNKNOWN ) - # params just have to be a list of length 1 to ensure the test runs at least once simulated_test_runner.run_test( setup=setup, - params=[0], always_validation_sequence_set=[[]], eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1e-3)] - ], - [RobotSpeedEventuallyBelowThreshold(1e-3)] ], ci_cmd_with_delay=[ (3, Command.Type.HALT, Team.BLUE), diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index 69a7ce3b9f..74e1434537 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -20,7 +20,7 @@ def test_kickoff_enemy_play(simulated_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): blue_bots = [ tbots_cpp.Point(-3, 2.5), tbots_cpp.Point(-2.8, 2.5), diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index a100f61cd3..90568954a1 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -23,7 +23,7 @@ def test_kickoff_friendly_play(simulated_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) - def setup(*args): + def setup(): field = tbots_cpp.Field.createSSLDivisionBField() blue_bots = [ diff --git a/src/software/ai/hl/stp/play/kickoff_play_test.py b/src/software/ai/hl/stp/play/kickoff_play_test.py index 20923bb136..403c3c0842 100644 --- a/src/software/ai/hl/stp/play/kickoff_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_play_test.py @@ -15,7 +15,7 @@ def test_kickoff_play(simulated_test_runner, is_friendly_test): ball_initial_pos = tbots_cpp.Point(0, 0) - def setup(*args): + def setup(): blue_bots = [ tbots_cpp.Point(-3, 2.5), tbots_cpp.Point(-3, 1.5), diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py index cd11ddc189..413fe698bb 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py @@ -101,7 +101,7 @@ def test_penalty_kick_enemy_play_setup( ball_initial_pos = field.enemyPenaltyMark() enemy_penalty_x = ball_initial_pos.x() - def setup(*args): + def setup(): # Enemy robots behind the penalty mark yellow_bots = [ tbots_cpp.Point(enemy_penalty_x + 0.3, 0), # kicker robot diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py index 0037966d0f..9ff52f511d 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py @@ -11,7 +11,7 @@ def test_shoot_or_chip_play(simulated_test_runner): - def setup(*args): + def setup(): ball_initial_pos = tbots_cpp.Point(-1.4, 2) ball_initial_vel = tbots_cpp.Vector(0, 0) diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 4d2819287b..259fe69638 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -17,7 +17,7 @@ def test_shoot_or_pass_play(simulated_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index ce22bb68aa..27eba11d2c 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -120,7 +120,7 @@ def test_stop_play(ball_position, blue_robot_positions, simulated_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_positions, diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index 89e243bd11..b965feff24 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -121,7 +121,7 @@ def test_attacker_passing( ball_velocity, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -232,7 +232,7 @@ def test_attacker_keep_away( field_top_left = field.fieldLines().negXPosYCorner() # Keep away near field corner (second test case from C++) - def setup(*args): + def setup(): robot_pos = field_top_left ball_pos = tbots_cpp.Point(field_top_left.x() + 0.05, field_top_left.y() - 0.2) enemy_positions = [ @@ -352,7 +352,7 @@ def test_attacker_shoot_goal( ): field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[robot_pos], diff --git a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py index 5f2c2f310f..d5607a7b97 100644 --- a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py @@ -50,7 +50,7 @@ def test_chip(ball_offset_from_robot, angle_to_chip_at, simulated_test_runner): robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py index a50171e50e..817364d14e 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py @@ -29,7 +29,7 @@ def test_not_bumping_ball_towards_net(simulated_test_runner): enemy_threat_point = tbots_cpp.Point(3, 0) - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(0, 0)], @@ -80,7 +80,7 @@ def test_crease_region_positioning( ): field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(-3, 1.5)], @@ -209,7 +209,7 @@ def test_crease_positioning( ball_initial_velocity, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], @@ -317,7 +317,7 @@ def test_crease_autochip( should_chip, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], @@ -387,7 +387,7 @@ def test_crease_get_ball( should_dribble, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index 6b3bf732dd..a254b4fd03 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -129,7 +129,7 @@ def test_dribble( ball_vel, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -296,7 +296,7 @@ def test_excessive_dribbling_without_enemies( simulated_test_runner, blue_robot_location, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_robot_location], @@ -339,7 +339,7 @@ def test_dribble_with_excessive_dribbling(simulated_test_runner): initial_position = tbots_cpp.Point(4.5, -3.0) dribble_orientation = tbots_cpp.Angle.half() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -389,7 +389,7 @@ def test_run_into_enemy_robot_knock_ball_away( ball_pos = tbots_cpp.Point(2, -2) ball_vel = tbots_cpp.Vector(2, 4) - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -438,7 +438,7 @@ def test_robot_not_bumping_ball_when_turning( robot_location = tbots_cpp.Point(-1, 0) ball_location = robot_location + tbots_cpp.Vector(ROBOT_MAX_RADIUS_METERS, 0) - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(-3, 2.5), robot_location], diff --git a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py index cad3b0dc5c..4e8f95ef32 100644 --- a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py @@ -112,7 +112,7 @@ def test_goalie_blocks_shot( robot_initial_position, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( [], @@ -187,7 +187,7 @@ def test_goalie_clears_from_dead_zone( should_clear, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( [], diff --git a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py index 0fa61787f8..343faade38 100644 --- a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py @@ -30,7 +30,7 @@ ], ) def test_robot_halt(blue_robot_locations, blue_robot_velocities, simulated_test_runner): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_locations, diff --git a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py index 2a76491057..e6ad83837e 100644 --- a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py @@ -40,7 +40,7 @@ def test_kick(ball_offset_from_robot, angle_to_kick_at, simulated_test_runner): robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py index c2e3dc823f..55dba2cd25 100644 --- a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py @@ -27,7 +27,7 @@ def test_move_across_field(simulated_test_runner): destination = tbots_cpp.Point(2.5, -1.1) field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -79,7 +79,7 @@ def test_autochip_move(simulated_test_runner): destination = tbots_cpp.Point(0, 1.5) field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ @@ -140,7 +140,7 @@ def test_autokick_move(simulated_test_runner): destination = tbots_cpp.Point(-1, -1) field = tbots_cpp.Field.createSSLDivisionBField() - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[initial_position], @@ -243,7 +243,7 @@ def setup(*args): def test_spinning_move( orientation, initial_position, destination, angular_velocity, simulated_test_runner ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[initial_position], @@ -296,7 +296,7 @@ def test_move_across_x_axis(simulated_test_runner): initial_position = tbots_cpp.Point(-4.4, 0) destination = tbots_cpp.Point(3, 0) - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py index a6974ed700..6d4198f84d 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py @@ -34,7 +34,7 @@ def test_ball_chipped_on_intercept( position_to_block_from, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( [], @@ -105,7 +105,7 @@ def test_avoid_intercept_scenario( position_to_block_from, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( [], @@ -236,7 +236,7 @@ def test_steal_ball( should_steal, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[position_to_block_from], diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index 3a82016412..d8140e41cd 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -55,7 +55,7 @@ def test_penalty_kick( field = tbots_cpp.Field.createSSLDivisionBField() ball_initial_pos = field.friendlyPenaltyMark() - def setup(*args): + def setup(): shooter_position = ball_initial_pos - tbots_cpp.Vector(0.1, 0) simulated_test_runner.set_world_state( diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py index f5e8ee7356..ab106b05cd 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py @@ -38,7 +38,7 @@ def test_pivot_kick(ball_offset_from_robot, angle_to_kick_at, simulated_test_run robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=[ diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index 52156efbf4..9da30fa543 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -189,7 +189,7 @@ def test_receiver( one_touch, simulated_test_runner, ): - def setup(*args): + def setup(): ball_velocity = calculate_ball_velocity( passer_point, receiver_point, pass_speed ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 40e9be76bd..1b7b79b9c1 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -489,7 +489,7 @@ def test_robot_movement( ) simulated_test_runner.run_test( - setup=lambda param: hrvo_setup( + setup=lambda: hrvo_setup( friendly_robot_positions, friendly_robot_destinations, friendly_robots_final_orientations, @@ -497,7 +497,6 @@ def test_robot_movement( enemy_robots_destinations, simulated_test_runner, ), - params=[0], eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=timeout_s, diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index c1fe0f09c2..701442ce70 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -73,12 +73,12 @@ py_library( "fixture.py", ], deps = [ - ":util", ":field_test_runner", ":simulated_test_runner", - "//software/thunderscope:estop_helpers", + ":util", "//software/logger:py_logger", - "//software/thunderscope:thunderscope", + "//software/thunderscope", + "//software/thunderscope:estop_helpers", "//software/thunderscope/binary_context_managers:full_system", "//software/thunderscope/binary_context_managers:game_controller", "//software/thunderscope/binary_context_managers:simulator", diff --git a/src/software/sensor_fusion/filter/ball_occlusion_test.py b/src/software/sensor_fusion/filter/ball_occlusion_test.py index 1d4c861933..50ca0c0d80 100644 --- a/src/software/sensor_fusion/filter/ball_occlusion_test.py +++ b/src/software/sensor_fusion/filter/ball_occlusion_test.py @@ -171,7 +171,7 @@ def test_ball_occlusion( yellow_robot_positions, simulated_test_runner, ): - def setup(*args): + def setup(): simulated_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_positions, From cc9c782ae301434914560617543d643183e18d86 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Tue, 16 Jun 2026 15:14:56 -0700 Subject: [PATCH 10/45] Fix offense play test --- src/software/ai/hl/stp/play/offense/offense_play_test.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/software/ai/hl/stp/play/offense/offense_play_test.py b/src/software/ai/hl/stp/play/offense/offense_play_test.py index 9a039954ea..dff3d1f6d9 100644 --- a/src/software/ai/hl/stp/play/offense/offense_play_test.py +++ b/src/software/ai/hl/stp/play/offense/offense_play_test.py @@ -2,7 +2,6 @@ from proto.play_pb2 import PlayName from software.gameplay_tests.validation.friendly_team_scored import * from software.gameplay_tests.validation.ball_enters_region import * -from software.gameplay_tests.validation.friendly_has_ball_possession import * from software.gameplay_tests.validation.excessive_dribbling import * from proto.message_translation.tbots_protobuf import create_world_state from proto.ssl_gc_common_pb2 import Team @@ -37,8 +36,8 @@ def setup(): simulated_test_runner.set_world_state( create_world_state( - yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, + yellow_robot_locations=yellow_bots, ball_location=start_point, ball_velocity=tbots_cpp.Vector(0, 0), ), @@ -61,7 +60,6 @@ def setup(): always_validation_sequence_set = [ [BallAlwaysStaysInRegion(regions=[field.fieldBoundary()])], [NeverExcessivelyDribbles()], - [FriendlyAlwaysHasBallPossession()], ] # Eventually Validation @@ -74,7 +72,6 @@ def setup(): eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=15, - run_till_end=True, ) From 7090e6f8ed1f014df7d883516b8a21e06f3b629a Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 11:44:13 -0700 Subject: [PATCH 11/45] Delete unused sim test fixture --- .../gameplay_tests/simulated_test_fixture.py | 648 ------------------ 1 file changed, 648 deletions(-) delete mode 100644 src/software/gameplay_tests/simulated_test_fixture.py diff --git a/src/software/gameplay_tests/simulated_test_fixture.py b/src/software/gameplay_tests/simulated_test_fixture.py deleted file mode 100644 index 71c14e1c85..0000000000 --- a/src/software/gameplay_tests/simulated_test_fixture.py +++ /dev/null @@ -1,648 +0,0 @@ -import threading -import queue -import argparse -import time -import sys -import os - -import pytest -from proto.import_all_protos import * - -from software.gameplay_tests.validation import validation -from software.gameplay_tests.tbots_test_runner import TbotsTestRunner -from software.thunderscope.thunderscope import Thunderscope -from software.thunderscope.proto_unix_io import ProtoUnixIO -from software.py_constants import MILLISECONDS_PER_SECOND -from software.thunderscope.binary_context_managers.full_system import FullSystem -from software.thunderscope.binary_context_managers.simulator import Simulator -from software.thunderscope.binary_context_managers.game_controller import Gamecontroller -from software.thunderscope.thunderscope_config import configure_simulated_test_view -from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer - -from software.logger.logger import create_logger -from typing import override - -logger = create_logger(__name__) - -LAUNCH_DELAY_S = 0.1 -WORLD_BUFFER_TIMEOUT = 0.5 -PROCESS_BUFFER_DELAY_S = 0.01 -PAUSE_AFTER_FAIL_DELAY_S = 3 - - -class SimulatedTestRunner(TbotsTestRunner): - """Run a simulated test""" - - def __init__( - self, - test_name, - thunderscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - ci_mode=False, - ): - """Initialize the SimulatorTestRunner - - :param test_name: The name of the test to run - :param thunderscope: The Thunderscope to use, None if not used - :param simulator_proto_unix_io: The simulator proto unix io to use - :param blue_full_system_proto_unix_io: The blue full system proto unix io to use - :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use - :param gamecontroller: The gamecontroller context managed instance - :param ci_mode: Run test as fast as possible - """ - super(SimulatedTestRunner, self).__init__( - test_name, - thunderscope, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - ) - self.simulator_proto_unix_io = simulator_proto_unix_io - self.ci_mode = ci_mode - - @override - def set_world_state(self, worldstate: WorldState): - """Sets the simulation worldstate - - :param worldstate: proto containing the desired worldstate - """ - self.simulator_proto_unix_io.send_proto(WorldState, worldstate) - - def excepthook(self, args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. - - :param args: The args passed in from the hook - """ - self.__stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception - - def __stopper(self, delay=PROCESS_BUFFER_DELAY_S): - """Stop running the test - - :param delay: How long to wait before closing everything, defaults - to PROCESS_BUFFER_DELAY_S to minimize buffer warnings - """ - time.sleep(delay) - - if self.thunderscope: - self.thunderscope.close() - - def sync_setup(self, setup, param): - """Run setup until simulator has received game state - - :param setup: Function that sets up the world state - :param param: Parameter passed into setup - """ - world_state_received_buffer = ThreadSafeBuffer(1, WorldStateReceivedTrigger) - self.simulator_proto_unix_io.register_observer( - WorldStateReceivedTrigger, world_state_received_buffer - ) - - while True: - setup(param) - - try: - world_state_received_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT - ) - except queue.Empty: - # Did not receive a response within timeout period - continue - else: - # Received a response from the simulator - break - - def runner( - self, - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, - ): - """Run a test - - :param always_validation_sequence_set: Validation functions that should - hold on every tick - :param eventually_validation_sequence_set: Validation that should - eventually be true, before the test ends - :param test_timeout_s: The timeout for the test, if any eventually_validations - remain after the timeout, the test fails. - :param tick_duration_s: The simulation step duration - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out - """ - time_elapsed_s = 0 - - eventually_validation_failure_msg = "Test Timed Out" - - while time_elapsed_s < test_timeout_s: - # get time before we execute the loop - processing_start_time = time.time() - - # Check for new CI commands at this time step - for delay, cmd, team in ci_cmd_with_delay: - # If delay matches time - if delay <= time_elapsed_s: - # send command - self.gamecontroller.send_gc_command(gc_command=cmd, team=team) - # remove command from the list - ci_cmd_with_delay.remove((delay, cmd, team)) - - tick = SimulatorTick(milliseconds=tick_duration_s * MILLISECONDS_PER_SECOND) - self.simulator_proto_unix_io.send_proto(SimulatorTick, tick) - time_elapsed_s += tick_duration_s - - while True: - try: - world = self.world_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False - ) - - # We block until the timeout for the new primitives from AI. if not found still, - # the SSL Wrapper packet is resent in a loop until we actually get a primitive set from AI - # Otherwise, if the AI misses the first SSL Wrapper packet and doesn't start - # the simulated test will continue to tick forward, causes syncing issues with the AI - self.primitive_set_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT, return_cached=False - ) - - break - except queue.Empty: - # If we timeout, that means full_system missed the last - # wrapper and robot status, lets resend it. - logger.warning("Fullsystem missed last wrapper, resending ...") - - ssl_wrapper = self.ssl_wrapper_buffer.get(block=False) - robot_status = self.robot_status_buffer.get(block=False) - - self.blue_full_system_proto_unix_io.send_proto( - SSL_WrapperPacket, ssl_wrapper - ) - self.blue_full_system_proto_unix_io.send_proto( - RobotStatus, robot_status - ) - - # get the time difference after we get the primitive (after any blocking that happened) - processing_time = time.time() - processing_start_time - - # if the time we have blocked is less than a tick, sleep for the remaining time (for Thunderscope only) - if ( - self.thunderscope - and not self.ci_mode - and tick_duration_s > processing_time - ): - time.sleep(tick_duration_s - processing_time) - - # Validate - ( - eventually_validation_proto_set, - always_validation_proto_set, - ) = validation.run_validation_sequence_sets( - world, - eventually_validation_sequence_set, - always_validation_sequence_set, - ) - - # Set the test name - eventually_validation_proto_set.test_name = self.test_name - always_validation_proto_set.test_name = self.test_name - - # Send out the validation proto to the full system - # for visualization and logging for replays. - if self.is_yellow_friendly: - self.yellow_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set - ) - self.yellow_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set - ) - else: - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set - ) - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set - ) - - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) - - if not run_till_end: - try: - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - self.__stopper() - return - except AssertionError as e: - eventually_validation_failure_msg = str(e) - - if not run_till_end: - raise AssertionError(eventually_validation_failure_msg) - - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - - self.__stopper() - - @override - def run_test( - self, - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s=3, - tick_duration_s=0.0166, # Default to 60hz - index=0, - ci_cmd_with_delay=[], - run_till_end=True, - **kwargs, - ): - """Helper function to run a test, with thunderscope if enabled - - :param always_validation_sequence_set: validation that should always be true - :param eventually_validation_sequence_set: validation that should eventually be true - :param test_timeout_s: how long the test should run before timing out - :param tick_duration_s: length of a tick - :param index: index of the current test. default is 0 (invariant test) - values can be passed in during aggregate testing for different timeout durations - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out - """ - test_timeout_duration = ( - test_timeout_s[index] if type(test_timeout_s) == list else test_timeout_s - ) - - # If thunderscope is enabled, run the test in a thread and show - # thunderscope on this thread. The excepthook is setup to catch - # any test failures and propagate them to the main thread - if self.thunderscope: - run_sim_thread = threading.Thread( - target=self.runner, - daemon=True, - args=[ - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_duration, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, - ], - ) - run_sim_thread.start() - self.thunderscope.show() - run_sim_thread.join() - - if self.last_exception: - pytest.fail(str(self.last_exception)) - - # If thunderscope is disabled, just run the test - else: - self.runner( - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_duration, - tick_duration_s, - ci_cmd_with_delay=ci_cmd_with_delay, - run_till_end=run_till_end, - ) - - -class InvariantTestRunner(SimulatedTestRunner): - """Runs a simulated test only once with a given parameter - - Test passes or fails based on the outcome of this test - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @override - def run_test( - self, - setup=(lambda x: None), - params=[0], - inv_always_validation_sequence_set=[[]], - inv_eventually_validation_sequence_set=[[]], - **kwargs, - ): - """Run an invariant test - - :param setup: Function that sets up the World state and the gamecontroller before running the test - :param params: List of parameters for each iteration of the test - (this method only uses the first element) - :param inv_always_validation_sequence_set: Validation functions for invariant testing - that should hold on every tick - :param inv_eventually_validation_sequence_set: Validation functions for invariant testing - that should eventually be true, before the test ends - """ - threading.excepthook = self.excepthook - - super().sync_setup(setup, params[0]) - - super().run_test( - inv_always_validation_sequence_set, - inv_eventually_validation_sequence_set, - **kwargs, - ) - - -class AggregateTestRunner(SimulatedTestRunner): - """Runs a simulated test multiple times with different given parameters - - Result of the test is determined by comparing the number of - passing iterations to a predetermined acceptable threshold - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @override - def run_test( - self, - setup=(lambda arg: None), - params=[], - ag_always_validation_sequence_set=[[]], - ag_eventually_validation_sequence_set=[[]], - **kwargs, - ): - """Run an aggregate test - - :param setup: Function that sets up the World state and the gamecontroller before running the test - :param params: List of parameters for each iteration of the test - :param ag_always_validation_sequence_set: Validation functions for aggregate testing - that should hold on every tick - :param ag_eventually_validation_sequence_set: Validation functions for aggregate testing - that should eventually be true, before the test end - """ - threading.excepthook = self.excepthook - - failed_tests = 0 - - # Runs the test once for each given parameter - # Catches Assertion Error thrown by failing test and increments counter - # Calculates overall results and prints them - for x in range(len(params)): - super().sync_setup(setup, params[x]) - - try: - super().run_test( - ag_always_validation_sequence_set, - ag_eventually_validation_sequence_set, - **kwargs, - ) - except AssertionError: - failed_tests += 1 - - # TODO (#2856) Fix validation and results output - - logger.info(f"{failed_tests} test failed") - - assert failed_tests == 0 - - -def get_runtime_dir(): - """Gets the base runtime directory for the test execution. - - TODO: Refactor #3744 - - Creates a new persistent directory for each test so that tests - running in parallel do not interfere with each other. - - :return: The path to the runtime directory. - """ - import uuid - - runtime_dir = os.path.join("/tmp", f"tbots_{uuid.uuid4().hex[:8]}") - os.makedirs(runtime_dir, exist_ok=True) - return runtime_dir - - -RUNTIME_DIR = get_runtime_dir() - - -def load_command_line_arguments(allow_unrecognized: bool = False) -> argparse.Namespace: - """Load command line arguments. - - We aren't using pytest.ini because it does not allow for dynamic defaults, - which we need to use TEST_TMPDIR when it's available. - - We aren't using conftest.py's pytest_addoption because we want to be able to - run the gamecontroller script directly from python without pytest. - Pytest does have a way to access parsed options (request.config.getoption), - but it doesn't seem to play nicely with bazel. We just use argparse instead. - - :param allow_unrecognized: if true, does not raise an error for unrecognized arguments - """ - parser = argparse.ArgumentParser(description="Run simulated pytests") - parser.add_argument( - "--enable_thunderscope", action="store_true", help="enable thunderscope" - ) - parser.add_argument( - "--ci_mode", - action="store_true", - default=False, - help="Run simulator at faster speed", - ) - parser.add_argument( - "--aggregate", action="store_true", default=False, help="Run aggregate test" - ) - parser.add_argument( - "--simulator_runtime_dir", - type=str, - help="simulator runtime directory", - default=RUNTIME_DIR, - ) - parser.add_argument( - "--blue_full_system_runtime_dir", - type=str, - help="blue full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "blue"), - ) - parser.add_argument( - "--yellow_full_system_runtime_dir", - type=str, - help="yellow full_system runtime directory", - default=os.path.join(RUNTIME_DIR, "yellow"), - ) - parser.add_argument( - "--layout", - action="store", - help="Which layout to run, if not specified the last layout will run", - ) - parser.add_argument( - "--debug_blue_full_system", - action="store_true", - default=False, - help="Debug blue full_system", - ) - parser.add_argument( - "--debug_yellow_full_system", - action="store_true", - default=False, - help="Debug yellow full_system", - ) - parser.add_argument( - "--debug_simulator", - action="store_true", - default=False, - help="Debug the simulator", - ) - parser.add_argument( - "--visualization_buffer_size", - action="store", - type=int, - default=5, - help="How many packets to buffer while rendering", - ) - parser.add_argument( - "--show_gamecontroller_logs", - action="store_true", - default=False, - help="Show gamecontroller logs", - ) - parser.add_argument( - "--test_filter", - action="store", - default="", - help="The test filter, if not specified all tests will run. " - + "See https://docs.pytest.org/en/latest/how-to/usage.html#specifying-tests-selecting-tests", - ) - parser.add_argument( - "--enable_realism", - action="store_true", - default=False, - help="Use realism in the simulator", - ) - return parser.parse_known_args()[0] if allow_unrecognized else parser.parse_args() - - -def pytest_main(file): - """Runs the pytest file - - :param file: The test file to run - """ - args = load_command_line_arguments(allow_unrecognized=True) - - # Run the test, -s disables all capturing at -vv increases verbosity - # -W ignore::DeprecationWarning ignores deprecation warnings that spam the output - sys.exit( - pytest.main( - ["-svv", "-W ignore::DeprecationWarning", "-k", args.test_filter, file] - ) - ) - - -@pytest.fixture -def simulated_test_runner(): - args = load_command_line_arguments() - tscope = None - - aggregate = args.aggregate - - simulator_proto_unix_io = ProtoUnixIO() - yellow_full_system_proto_unix_io = ProtoUnixIO() - blue_full_system_proto_unix_io = ProtoUnixIO() - - # Grab the current test name to store the proto log for the test case - current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] - current_test = current_test.replace("]", "") - current_test = current_test.replace("[", "-") - - # Truncate the test name to 25 characters for UNIX path length limits - test_name = current_test.split("-")[0][:25] - - # Launch all binaries - with Simulator( - f"{args.simulator_runtime_dir}/test/{test_name}", - args.debug_simulator, - args.enable_realism, - ) as simulator, FullSystem( - "software/unix_full_system", - f"{args.blue_full_system_runtime_dir}/test/{test_name}", - args.debug_blue_full_system, - False, - should_restart_on_crash=False, - running_in_realtime=args.enable_thunderscope and not args.ci_mode, - ) as blue_fs, FullSystem( - "software/unix_full_system", - f"{args.yellow_full_system_runtime_dir}/test/{test_name}", - args.debug_yellow_full_system, - True, - should_restart_on_crash=False, - running_in_realtime=args.enable_thunderscope and not args.ci_mode, - ) as yellow_fs: - with Gamecontroller( - suppress_logs=(not args.show_gamecontroller_logs) - ) as gamecontroller: - blue_fs.setup_proto_unix_io(blue_full_system_proto_unix_io) - yellow_fs.setup_proto_unix_io(yellow_full_system_proto_unix_io) - simulator.setup_proto_unix_io( - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - ProtoUnixIO(), - ) - gamecontroller.setup_proto_unix_io( - blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, - simulator_proto_unix_io=simulator_proto_unix_io, - ) - - # If we want to run thunderscope, inject the proto unix ios - # and start the test - if args.enable_thunderscope: - tscope = Thunderscope( - configure_simulated_test_view( - blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, - simulator_proto_unix_io=simulator_proto_unix_io, - ), - layout_path=args.layout, - ) - - time.sleep(LAUNCH_DELAY_S) - - runner = None - - # Initialise the right runner based on which testing mode is selected - if aggregate: - runner = AggregateTestRunner( - current_test, - tscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - args.ci_mode, - ) - else: - runner = InvariantTestRunner( - current_test, - tscope, - simulator_proto_unix_io, - blue_full_system_proto_unix_io, - yellow_full_system_proto_unix_io, - gamecontroller, - args.ci_mode, - ) - - yield runner From 69428d8b0e58b5ae3d0f672549992b35082e6af6 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:06:28 -0700 Subject: [PATCH 12/45] Unify test fixture as gameplay_test_runner --- .../ball_placement_play_test.py | 30 +++++++------- .../crease_defense_play_test.py | 10 ++--- .../hl/stp/play/defense/defense_play_test.py | 24 +++++------ .../enemy_ball_placement_play_test.py | 12 +++--- .../enemy_free_kick_play_test.py | 12 +++--- .../hl/stp/play/example/example_play_test.py | 14 +++---- .../stp/play/free_kick/free_kick_play_test.py | 14 +++---- .../hl/stp/play/halt_play/halt_play_test.py | 10 ++--- .../kickoff_enemy/kickoff_enemy_play_test.py | 14 +++---- .../kickoff_friendly_play_test.py | 14 +++---- .../ai/hl/stp/play/kickoff_play_test.py | 16 ++++---- .../hl/stp/play/offense/offense_play_test.py | 12 +++--- .../ai/hl/stp/play/passing_sim_test.py | 20 +++++----- .../penalty_kick_enemy_play_test.py | 12 +++--- .../shoot_or_chip/shoot_or_chip_play_test.py | 12 +++--- .../shoot_or_pass/shoot_or_pass_play_test.py | 12 +++--- src/software/ai/hl/stp/play/stop_play_test.py | 10 ++--- .../tactic/attacker/attacker_tactic_test.py | 24 +++++------ .../ai/hl/stp/tactic/chip/chip_tactic_test.py | 8 ++-- .../crease_defender_tactic_test.py | 40 +++++++++---------- .../stp/tactic/dribble/dribble_tactic_test.py | 40 +++++++++---------- .../stp/tactic/goalie/goalie_tactic_test.py | 16 ++++---- .../ai/hl/stp/tactic/halt/halt_tactic_test.py | 8 ++-- .../ai/hl/stp/tactic/kick/kick_tactic_test.py | 8 ++-- .../ai/hl/stp/tactic/move/move_tactic_test.py | 40 +++++++++---------- .../pass_defender_tactic_test.py | 24 +++++------ .../penalty_kick/penalty_kick_tactic_test.py | 8 ++-- .../pivot_kick/pivot_kick_tactic_test.py | 8 ++-- .../tactic/receiver/receiver_tactic_test.py | 8 ++-- .../trajectory/simulated_hrvo_test.py | 20 +++++----- src/software/conftest.py | 3 +- src/software/gameplay_tests/fixture.py | 12 ++++-- .../movement_robot_field_test.py | 22 +++++----- .../gameplay_tests/passing_field_test.py | 12 +++--- .../gameplay_tests/pivot_kick_field_test.py | 12 +++--- .../simulated_test_ball_model.py | 22 +++++----- .../filter/ball_occlusion_test.py | 10 ++--- 37 files changed, 299 insertions(+), 294 deletions(-) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index 3a897c08a6..787d2ce97c 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -31,10 +31,10 @@ ], ) def test_two_ai_ball_placement( - simulated_test_runner, ball_start_point, ball_placement_point + gameplay_test_runner, ball_start_point, ball_placement_point ): run_ball_placement_scenario( - simulated_test_runner, ball_start_point, ball_placement_point + gameplay_test_runner, ball_start_point, ball_placement_point ) @@ -62,21 +62,21 @@ def test_two_ai_ball_placement( ], ) def test_robocup_technical_challenge_placement( - simulated_test_runner, ball_start_point, ball_placement_point + gameplay_test_runner, ball_start_point, ball_placement_point ): run_ball_placement_scenario( - simulated_test_runner, ball_start_point, ball_placement_point, blue_only=True + gameplay_test_runner, ball_start_point, ball_placement_point, blue_only=True ) def ball_placement_play_setup( - ball_start_point, ball_placement_point, simulated_test_runner, blue_only + ball_start_point, ball_placement_point, gameplay_test_runner, blue_only ): """Set up ball placement test by initializing bot positions, ball placement targets, and test settings :param ball_start_point: Initial point of the ball :param ball_placement_point: Target point of the ball - :param simulated_test_runner: Simulated test runner + :param gameplay_test_runner: Simulated test runner :param blue_only: If True, only the blue team is active; the yellow team is ignored. """ # Setup blue robots @@ -111,7 +111,7 @@ def ball_placement_play_setup( ] # Create world state - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -121,28 +121,28 @@ def ball_placement_play_setup( ) # Game Controller Setup - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) # Pass in placement point here - not required for all play tests - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.BALL_PLACEMENT, team=Team.BLUE, final_ball_placement_point=ball_placement_point, ) # Force play override here - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.BallPlacementPlay, yellow_play=PlayName.HaltPlay ) def run_ball_placement_scenario( - simulated_test_runner, ball_start_point, ball_placement_point, blue_only=False + gameplay_test_runner, ball_start_point, ball_placement_point, blue_only=False ): """Runs a ball placement test scenario with the specified parameters. - :param simulated_test_runner: The test runner used to simulate robot and ball behavior. + :param gameplay_test_runner: The test runner used to simulate robot and ball behavior. :param ball_start_point: The initial position of the ball. :param ball_placement_point: The target position where the ball should be placed. :param blue_only: If True, only the blue team is active; the yellow team is ignored. @@ -181,11 +181,11 @@ def run_ball_placement_scenario( ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=lambda: ball_placement_play_setup( ball_start_point, ball_placement_point, - simulated_test_runner, + gameplay_test_runner, blue_only, ), always_validation_sequence_set=[[]], @@ -193,7 +193,7 @@ def run_ball_placement_scenario( test_timeout_s=30, ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( # setup argument isn't passed to preserve world state from previous test run always_validation_sequence_set=drop_ball_always_validation_sequence_set, eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py index 594e9e05cc..ffe948dded 100644 --- a/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py +++ b/src/software/ai/hl/stp/play/crease_defense/crease_defense_play_test.py @@ -13,12 +13,12 @@ from software.gameplay_tests.validation.delay_validation import DelayValidation -def test_crease_defense_play(simulated_test_runner): +def test_crease_defense_play(gameplay_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() goalie_position = tbots_cpp.Point(-4.5, 0) def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ goalie_position, @@ -41,11 +41,11 @@ def setup(): ), ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.CreaseDefensePlay, yellow_play=PlayName.HaltPlay ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) @@ -68,7 +68,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, diff --git a/src/software/ai/hl/stp/play/defense/defense_play_test.py b/src/software/ai/hl/stp/play/defense/defense_play_test.py index 6288f92160..fdf47e947c 100644 --- a/src/software/ai/hl/stp/play/defense/defense_play_test.py +++ b/src/software/ai/hl/stp/play/defense/defense_play_test.py @@ -33,11 +33,11 @@ ) ], ) -def test_defense_play_ball_steal(simulated_test_runner, blue_bots, yellow_bots): +def test_defense_play_ball_steal(gameplay_test_runner, blue_bots, yellow_bots): def setup(): ball_initial_pos = tbots_cpp.Point(0.93, 0) - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -46,18 +46,18 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.DefensePlay, yellow_play=PlayName.HaltPlay ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=[ [ @@ -95,11 +95,11 @@ def setup(): ) ], ) -def test_defense_play(simulated_test_runner, blue_bots, yellow_bots): +def test_defense_play(gameplay_test_runner, blue_bots, yellow_bots): def setup(): ball_initial_pos = tbots_cpp.Point(0.9, 2.85) - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -108,18 +108,18 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.DefensePlay, yellow_play=PlayName.ShootOrPassPlay ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=[ [ diff --git a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py index 82b988dc91..315314286f 100644 --- a/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_ball_placement/enemy_ball_placement_play_test.py @@ -18,7 +18,7 @@ ], ) def test_two_ai_ball_placement( - simulated_test_runner, ball_start_point, ball_placement_point + gameplay_test_runner, ball_start_point, ball_placement_point ): def setup(): blue_bots = [ @@ -43,7 +43,7 @@ def setup(): .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -52,16 +52,16 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.BALL_PLACEMENT, team=Team.YELLOW, final_ball_placement_point=ball_placement_point, ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.EnemyBallPlacementPlay, yellow_play=PlayName.BallPlacementPlay, ) @@ -70,7 +70,7 @@ def setup(): [RobotNeverEntersPlacementRegion(ball_placement_point)] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=always_validation_sequence_set, eventually_validation_sequence_set=[[]], diff --git a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py index 702ea07b8d..4eab23d230 100644 --- a/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/enemy_free_kick/enemy_free_kick_play_test.py @@ -95,10 +95,10 @@ "Disabling this test because OrValidation is passed both an always validation and eventually validation" ) def test_enemy_free_kick_play( - simulated_test_runner, blue_bots, yellow_bots, ball_initial_pos + gameplay_test_runner, blue_bots, yellow_bots, ball_initial_pos ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -107,14 +107,14 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.DIRECT, team=Team.YELLOW ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.EnemyFreeKickPlay, yellow_play=PlayName.FreeKickPlay ) @@ -139,7 +139,7 @@ def setup(): [RobotEventuallyEntersRegion(regions=[tbots_cpp.Circle(ball_initial_pos, 1)])] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/example/example_play_test.py b/src/software/ai/hl/stp/play/example/example_play_test.py index adbd6e3ff3..e980d4b4c9 100644 --- a/src/software/ai/hl/stp/play/example/example_play_test.py +++ b/src/software/ai/hl/stp/play/example/example_play_test.py @@ -10,7 +10,7 @@ from software.gameplay_tests.util import pytest_main -def test_example_play(simulated_test_runner): +def test_example_play(gameplay_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) def setup(): @@ -36,7 +36,7 @@ def setup(): .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -45,17 +45,17 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.NORMAL_START, team=Team.BLUE ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.DIRECT, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.ExamplePlay, yellow_play=PlayName.HaltPlay ) @@ -70,7 +70,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, diff --git a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py index 870b68aff7..115939945c 100644 --- a/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py +++ b/src/software/ai/hl/stp/play/free_kick/free_kick_play_test.py @@ -29,7 +29,7 @@ (tbots_cpp.Point(1.5, 0.5), True), ], ) -def test_free_kick_play_friendly(ball_initial_pos, must_score, simulated_test_runner): +def test_free_kick_play_friendly(ball_initial_pos, must_score, gameplay_test_runner): def setup(): blue_bots = [ tbots_cpp.Point(-4.5, 0), @@ -51,7 +51,7 @@ def setup(): .enemyDefenseArea() .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_bots, yellow_robot_locations=yellow_bots, @@ -60,17 +60,17 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.NORMAL_START, team=Team.BLUE ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.DIRECT, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.FreeKickPlay, yellow_play=PlayName.HaltPlay ) @@ -87,7 +87,7 @@ def setup(): ], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index b5b2316b7c..ea8a17b534 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -7,7 +7,7 @@ # TODO issue #2599 - Remove Duration parameter from test # @pytest.mark.parametrize("run_enemy_ai,test_duration", [(False, 20), (True, 20)]) -def test_halt_play(simulated_test_runner): +def test_halt_play(gameplay_test_runner): def setup(): ball_initial_pos = tbots_cpp.Point(0, 0) @@ -33,7 +33,7 @@ def setup(): .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -42,14 +42,14 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.UNKNOWN ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=[[]], eventually_validation_sequence_set=[ diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index 74e1434537..07a23d049b 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -16,7 +16,7 @@ from software.gameplay_tests.util import pytest_main -def test_kickoff_enemy_play(simulated_test_runner): +def test_kickoff_enemy_play(gameplay_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) field = tbots_cpp.Field.createSSLDivisionBField() @@ -39,7 +39,7 @@ def setup(): field.enemyDefenseArea().negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_bots, yellow_robot_locations=yellow_bots, @@ -48,22 +48,22 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.KICKOFF, team=Team.YELLOW ) # Let robots get ready before starting kickoff threading.Timer( 4.0, - lambda: simulated_test_runner.send_gamecontroller_command( + lambda: gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.NORMAL_START, team=Team.YELLOW ), ).start() - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.KickoffEnemyPlay, yellow_play=PlayName.KickoffFriendlyPlay, ) @@ -136,7 +136,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index 90568954a1..d60e4ee451 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -20,7 +20,7 @@ from software.gameplay_tests.util import pytest_main -def test_kickoff_friendly_play(simulated_test_runner): +def test_kickoff_friendly_play(gameplay_test_runner): ball_initial_pos = tbots_cpp.Point(0, 0) def setup(): @@ -44,7 +44,7 @@ def setup(): field.enemyDefenseArea().negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_bots, yellow_robot_locations=yellow_bots, @@ -53,22 +53,22 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.KICKOFF, team=Team.BLUE ) # Let robots get ready before starting kickoff threading.Timer( 4.0, - lambda: simulated_test_runner.send_gamecontroller_command( + lambda: gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.NORMAL_START, team=Team.BLUE ), ).start() - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.KickoffFriendlyPlay, yellow_play=PlayName.KickoffEnemyPlay, ) @@ -136,7 +136,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/kickoff_play_test.py b/src/software/ai/hl/stp/play/kickoff_play_test.py index 403c3c0842..c024286e9b 100644 --- a/src/software/ai/hl/stp/play/kickoff_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_play_test.py @@ -12,7 +12,7 @@ @pytest.mark.parametrize("is_friendly_test", [True, False]) -def test_kickoff_play(simulated_test_runner, is_friendly_test): +def test_kickoff_play(gameplay_test_runner, is_friendly_test): ball_initial_pos = tbots_cpp.Point(0, 0) def setup(): @@ -38,7 +38,7 @@ def setup(): .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=yellow_bots, blue_robot_locations=blue_bots, @@ -47,28 +47,28 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) if is_friendly_test: - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.KICKOFF, team=Team.BLUE ) blue_play = PlayName.KickoffFriendlyPlay yellow_play = PlayName.KickoffEnemyPlay else: - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.KICKOFF, team=Team.YELLOW ) blue_play = PlayName.KickoffEnemyPlay yellow_play = PlayName.KickoffFriendlyPlay - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.NORMAL_START, team=Team.BLUE ) - simulated_test_runner.set_plays(blue_play=blue_play, yellow_play=yellow_play) + gameplay_test_runner.set_plays(blue_play=blue_play, yellow_play=yellow_play) # TODO (#3650): fix validation logic @@ -134,7 +134,7 @@ def setup(): ) ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/offense/offense_play_test.py b/src/software/ai/hl/stp/play/offense/offense_play_test.py index dff3d1f6d9..630932d6b4 100644 --- a/src/software/ai/hl/stp/play/offense/offense_play_test.py +++ b/src/software/ai/hl/stp/play/offense/offense_play_test.py @@ -8,7 +8,7 @@ from software.gameplay_tests.util import pytest_main -def test_offense_play(simulated_test_runner): +def test_offense_play(gameplay_test_runner): start_point = tbots_cpp.Point(-4.4, 2.9) def setup(): @@ -34,7 +34,7 @@ def setup(): .negXPosYCorner(), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_bots, yellow_robot_locations=yellow_bots, @@ -43,14 +43,14 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.OffensePlay, yellow_play=PlayName.HaltPlay ) @@ -67,7 +67,7 @@ def setup(): [FriendlyTeamEventuallyScored()], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index 9ef89a483a..0fa3493722 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -26,7 +26,7 @@ def setup_pass_and_robots( friendly_orientations, enemy_robot_positions, receive_pass, - simulated_test_runner, + gameplay_test_runner, ): """Sets up a test involving 1 robot passing the ball With any number of friendly and enemy robots on the field @@ -38,13 +38,13 @@ def setup_pass_and_robots( :param friendly_orientations: the orientations of the friendly robots :param enemy_robot_positions: the positions of the enemy robots :param receive_pass: whether a friendly robot should try to receive the pass - :param simulated_test_runner: the test runner + :param gameplay_test_runner: the test runner :return: the best pass we generate """ blue_robot_locations = [attacker_robot_position, *receiver_robot_positions] # Setup the world state - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=enemy_robot_positions, blue_robot_locations=blue_robot_locations, @@ -137,7 +137,7 @@ def setup_pass_and_robots( blue_tactics[1] = ReceiverTactic(**receiver_args) - simulated_test_runner.set_tactics(blue_tactics=blue_tactics, yellow_tactics=None) + gameplay_test_runner.set_tactics(blue_tactics=blue_tactics, yellow_tactics=None) return best_pass @@ -225,7 +225,7 @@ def test_passing_receive_speed( receiver_robot_positions, friendly_orientations, enemy_robot_positions, - simulated_test_runner, + gameplay_test_runner, ): # Eventually Validation eventually_validation_sequence_set = [ @@ -243,7 +243,7 @@ def test_passing_receive_speed( [FriendlyAlwaysReceivesBallSlow(robot_id=1, max_receive_speed=2.1)], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=lambda: setup_pass_and_robots( ball_initial_position=ball_initial_position, ball_initial_velocity=ball_initial_velocity, @@ -251,7 +251,7 @@ def test_passing_receive_speed( receiver_robot_positions=receiver_robot_positions, friendly_orientations=friendly_orientations, enemy_robot_positions=enemy_robot_positions, - simulated_test_runner=simulated_test_runner, + gameplay_test_runner=gameplay_test_runner, receive_pass=True, ), eventually_validation_sequence_set=eventually_validation_sequence_set, @@ -339,7 +339,7 @@ def test_passing_no_backwards_passes( receiver_robot_positions, friendly_orientations, enemy_robot_positions, - simulated_test_runner, + gameplay_test_runner, ): field = tbots_cpp.Field.createSSLDivisionBField() best_pass = setup_pass_and_robots( @@ -350,7 +350,7 @@ def test_passing_no_backwards_passes( friendly_orientations=friendly_orientations, enemy_robot_positions=enemy_robot_positions, receive_pass=True, - simulated_test_runner=simulated_test_runner, + gameplay_test_runner=gameplay_test_runner, ) # Eventually Validation @@ -378,7 +378,7 @@ def test_passing_no_backwards_passes( ], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, diff --git a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py index 413fe698bb..62fca09a07 100644 --- a/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/penalty_kick_enemy/penalty_kick_enemy_play_test.py @@ -95,7 +95,7 @@ def test_penalty_kick_enemy_play_setup( friendly_robot_positions, enemy_distance_behind_ball, - simulated_test_runner, + gameplay_test_runner, ): field = tbots_cpp.Field.createSSLDivisionBField() ball_initial_pos = field.enemyPenaltyMark() @@ -124,7 +124,7 @@ def setup(): ), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=friendly_robot_positions, yellow_robot_locations=yellow_bots, @@ -133,14 +133,14 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.HALT, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.PENALTY, team=Team.YELLOW ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.PenaltyKickEnemyPlay, yellow_play=PlayName.HaltPlay, ) @@ -169,7 +169,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=20, diff --git a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py index 9ff52f511d..97bb09c0bb 100644 --- a/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_chip/shoot_or_chip_play_test.py @@ -10,7 +10,7 @@ from proto.geometry_pb2 import Point, Vector, Angle, AngularVelocity -def test_shoot_or_chip_play(simulated_test_runner): +def test_shoot_or_chip_play(gameplay_test_runner): def setup(): ball_initial_pos = tbots_cpp.Point(-1.4, 2) ball_initial_vel = tbots_cpp.Vector(0, 0) @@ -46,21 +46,21 @@ def setup(): world_state.yellow_robots[5].CopyFrom(last_robot) - simulated_test_runner.set_world_state(world_state) + gameplay_test_runner.set_world_state(world_state) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.ShootOrChipPlay, yellow_play=PlayName.HaltPlay ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) # TODO (#3651): add validations - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=[[]], test_timeout_s=10, diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 259fe69638..1f69af1708 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -14,11 +14,11 @@ @pytest.mark.skip( "Skipping test. TODO (#3233): attacker robot sometimes doesn't kick the ball towards the receiver" ) -def test_shoot_or_pass_play(simulated_test_runner): +def test_shoot_or_pass_play(gameplay_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ field.friendlyGoalCenter(), @@ -41,21 +41,21 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.ShootOrPassPlay, yellow_play=PlayName.HaltPlay ) # Eventually Validation eventually_validations = [[FriendlyTeamEventuallyScored()]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=15, diff --git a/src/software/ai/hl/stp/play/stop_play_test.py b/src/software/ai/hl/stp/play/stop_play_test.py index 27eba11d2c..5eee0982cd 100644 --- a/src/software/ai/hl/stp/play/stop_play_test.py +++ b/src/software/ai/hl/stp/play/stop_play_test.py @@ -117,11 +117,11 @@ # ), ], ) -def test_stop_play(ball_position, blue_robot_positions, simulated_test_runner): +def test_stop_play(ball_position, blue_robot_positions, gameplay_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_positions, yellow_robot_locations=[ @@ -137,11 +137,11 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.UNKNOWN ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.StopPlay, yellow_play=PlayName.HaltPlay ) @@ -163,7 +163,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=always_stop_play_rules, test_timeout_s=6, diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index b965feff24..66cef1d700 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -119,10 +119,10 @@ def test_attacker_passing( robot_pos, ball_pos, ball_velocity, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -140,7 +140,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: AttackerTactic( best_pass_so_far=Pass( @@ -168,7 +168,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, @@ -220,7 +220,7 @@ def test_attacker_keep_away( ball_velocity, enemy_positions, ignore_score_checks, - simulated_test_runner, + gameplay_test_runner, ): # TODO (#3638): Port C++ validation functions that don't exist in Python yet # In C++ test: @@ -242,7 +242,7 @@ def setup(): tbots_cpp.Point(-4, 2.75), ] - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -259,7 +259,7 @@ def setup(): ) receiver_point = tbots_cpp.Point(0, 0) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: AttackerTactic( best_pass_so_far=Pass( @@ -278,7 +278,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=always_validation_sequence_set, ) @@ -348,12 +348,12 @@ def setup(): ], ) def test_attacker_shoot_goal( - ball_pos, ball_velocity, robot_pos, enemy_positions, simulated_test_runner + ball_pos, ball_velocity, robot_pos, enemy_positions, gameplay_test_runner ): field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[robot_pos], yellow_robot_locations=enemy_positions, @@ -362,7 +362,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: AttackerTactic( chip_target=tbots_cpp.createPointProto( @@ -378,7 +378,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, diff --git a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py index d5607a7b97..8880ac87fb 100644 --- a/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/chip/chip_tactic_test.py @@ -46,12 +46,12 @@ ), ], ) -def test_chip(ball_offset_from_robot, angle_to_chip_at, simulated_test_runner): +def test_chip(ball_offset_from_robot, angle_to_chip_at, gameplay_test_runner): robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -63,7 +63,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: ChipTactic( chip_origin=tbots_cpp.createPointProto(ball_position), @@ -82,7 +82,7 @@ def setup(): ], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py index 817364d14e..e54397ef1a 100644 --- a/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/crease_defender/crease_defender_tactic_test.py @@ -26,11 +26,11 @@ from software.gameplay_tests.util import pytest_main -def test_not_bumping_ball_towards_net(simulated_test_runner): +def test_not_bumping_ball_towards_net(gameplay_test_runner): enemy_threat_point = tbots_cpp.Point(3, 0) def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(0, 0)], yellow_robot_locations=[tbots_cpp.Point(4, 0)], @@ -39,7 +39,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: CreaseDefenderTactic( enemy_threat_origin=tbots_cpp.createPointProto(enemy_threat_point), @@ -50,7 +50,7 @@ def setup(): always_validations = [[BallSpeedAlwaysBelowThreshold(0.001)]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=always_validations, ) @@ -76,12 +76,12 @@ def setup(): ], ) def test_crease_region_positioning( - enemy_threat_point, crease_alignment, region_index, simulated_test_runner + enemy_threat_point, crease_alignment, region_index, gameplay_test_runner ): field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(-3, 1.5)], yellow_robot_locations=[ @@ -97,7 +97,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: CreaseDefenderTactic( enemy_threat_origin=tbots_cpp.createPointProto(enemy_threat_point), @@ -171,7 +171,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=5, @@ -207,10 +207,10 @@ def test_crease_positioning( yellow_bots, ball_initial_pos, ball_initial_velocity, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], yellow_robot_locations=[yellow_bots], @@ -219,7 +219,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: CreaseDefenderTactic( enemy_threat_origin=tbots_cpp.createPointProto(ball_initial_pos), @@ -255,7 +255,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -315,10 +315,10 @@ def test_crease_autochip( ball_initial_pos, ball_initial_velocity, should_chip, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], yellow_robot_locations=[yellow_bots], @@ -327,7 +327,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: CreaseDefenderTactic( enemy_threat_origin=tbots_cpp.createPointProto(ball_initial_pos), @@ -350,7 +350,7 @@ def setup(): always_validation_sequence_set = [[]] eventually_validation_sequence_set = [[BallIsEventuallyOffGround()]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -385,10 +385,10 @@ def test_crease_get_ball( ball_initial_pos, ball_initial_velocity, should_dribble, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_bots], yellow_robot_locations=[yellow_bots], @@ -397,7 +397,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: CreaseDefenderTactic( enemy_threat_origin=tbots_cpp.createPointProto(ball_initial_pos), @@ -429,7 +429,7 @@ def setup(): RobotNeverEntersRegion(regions=[tbots_cpp.Circle(ball_initial_pos, 0.2)]) ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index a254b4fd03..ecfc71aaec 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -127,10 +127,10 @@ def test_dribble( dribble_orientation, ball_pos, ball_vel, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -154,7 +154,7 @@ def setup(): tbots_cpp.createAngleProto(dribble_orientation) ) - simulated_test_runner.set_tactics(blue_tactics={1: dribble_params}) + gameplay_test_runner.set_tactics(blue_tactics={1: dribble_params}) eventually_validations = [[RobotEventuallyReceivedBall(robot_id=1)]] @@ -175,7 +175,7 @@ def setup(): # TODO (#2514): tune dribbling and re-enable # Robot always not excessively dribbling - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=25, @@ -293,11 +293,11 @@ def test_excessive_dribbling_without_enemies( dribble_destination, final_dribble_orientation, should_excessively_dribble, - simulated_test_runner, + gameplay_test_runner, blue_robot_location, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[blue_robot_location], yellow_robot_locations=[], @@ -306,7 +306,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: DribbleTactic( dribble_destination=tbots_cpp.createPointProto(dribble_destination), @@ -327,20 +327,20 @@ def setup(): always_validation_sequence_set = [[NeverExcessivelyDribbles()]] eventually_validation_sequence_set = [[]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, ) -def test_dribble_with_excessive_dribbling(simulated_test_runner): +def test_dribble_with_excessive_dribbling(gameplay_test_runner): dribble_destination = tbots_cpp.Point(3, 2) initial_position = tbots_cpp.Point(4.5, -3.0) dribble_orientation = tbots_cpp.Angle.half() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -352,7 +352,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: DribbleTactic( dribble_destination=tbots_cpp.createPointProto(dribble_destination), @@ -373,7 +373,7 @@ def setup(): [EventuallyStartsExcessivelyDribbling()], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=10, @@ -381,7 +381,7 @@ def setup(): def test_run_into_enemy_robot_knock_ball_away( - simulated_test_runner, + gameplay_test_runner, ): initial_position = tbots_cpp.Point(-2, 1.5) dribble_destination = tbots_cpp.Point(-1, 2) @@ -390,7 +390,7 @@ def test_run_into_enemy_robot_knock_ball_away( ball_vel = tbots_cpp.Vector(2, 4) def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -409,7 +409,7 @@ def setup(): final_dribble_orientation=tbots_cpp.createAngleProto(dribble_orientation), ) - simulated_test_runner.set_tactics(blue_tactics={1: dribble_params}) + gameplay_test_runner.set_tactics(blue_tactics={1: dribble_params}) eventually_validations = [ [ @@ -423,7 +423,7 @@ def setup(): # TODO (#2514): tune dribbling and re-enable # Robot always not excessively dribbling - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=10, @@ -431,7 +431,7 @@ def setup(): def test_robot_not_bumping_ball_when_turning( - simulated_test_runner, + gameplay_test_runner, ): # The ball is placed right behind the friendly robot. Verify that the robot # does not bump the ball away when turning around to dribble it. @@ -439,7 +439,7 @@ def test_robot_not_bumping_ball_when_turning( ball_location = robot_location + tbots_cpp.Vector(ROBOT_MAX_RADIUS_METERS, 0) def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[tbots_cpp.Point(-3, 2.5), robot_location], blue_robot_orientations=[ @@ -454,7 +454,7 @@ def setup(): ) dribble_params = DribbleTactic() - simulated_test_runner.set_tactics(blue_tactics={1: dribble_params}) + gameplay_test_runner.set_tactics(blue_tactics={1: dribble_params}) eventually_validations = [ [ @@ -468,7 +468,7 @@ def setup(): [BallAlwaysStaysInRegion([tbots_cpp.Circle(ball_location, 0.05)])] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, always_validation_sequence_set=always_validations, diff --git a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py index 4e8f95ef32..7a320f95b1 100644 --- a/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/goalie/goalie_tactic_test.py @@ -110,10 +110,10 @@ def test_goalie_blocks_shot( ball_initial_position, ball_initial_velocity, robot_initial_position, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( [], blue_robot_locations=[robot_initial_position], @@ -122,7 +122,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: GoalieTactic( max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT @@ -153,7 +153,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -185,10 +185,10 @@ def setup(): def test_goalie_clears_from_dead_zone( ball_position, should_clear, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( [], blue_robot_locations=[ @@ -201,7 +201,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: GoalieTactic( max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT @@ -234,7 +234,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, test_timeout_s=8, eventually_validation_sequence_set=eventually_validation_sequence_set, diff --git a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py index 343faade38..563df6975e 100644 --- a/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/halt/halt_tactic_test.py @@ -29,9 +29,9 @@ ), ], ) -def test_robot_halt(blue_robot_locations, blue_robot_velocities, simulated_test_runner): +def test_robot_halt(blue_robot_locations, blue_robot_velocities, gameplay_test_runner): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_locations, blue_robot_velocities=blue_robot_velocities, @@ -41,7 +41,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics(blue_tactics={1: HaltTactic()}) + gameplay_test_runner.set_tactics(blue_tactics={1: HaltTactic()}) robot_stopped_validation = RobotSpeedEventuallyBelowThreshold(speed_threshold=0.001) @@ -59,7 +59,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py index e6ad83837e..886bf0d711 100644 --- a/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/kick/kick_tactic_test.py @@ -36,12 +36,12 @@ (tbots_cpp.Vector(ROBOT_MAX_RADIUS_METERS, 0), tbots_cpp.Angle.zero()), ], ) -def test_kick(ball_offset_from_robot, angle_to_kick_at, simulated_test_runner): +def test_kick(ball_offset_from_robot, angle_to_kick_at, gameplay_test_runner): robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -53,7 +53,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: KickTactic( kick_origin=tbots_cpp.createPointProto(ball_position), @@ -69,7 +69,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validations, ) diff --git a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py index 55dba2cd25..aebb1bb6a2 100644 --- a/src/software/ai/hl/stp/tactic/move/move_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/move/move_tactic_test.py @@ -22,13 +22,13 @@ from proto.import_all_protos import * -def test_move_across_field(simulated_test_runner): +def test_move_across_field(gameplay_test_runner): initial_position = tbots_cpp.Point(-3, 1.5) destination = tbots_cpp.Point(2.5, -1.1) field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -47,7 +47,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: MoveTactic( destination=tbots_cpp.createPointProto(destination), @@ -67,20 +67,20 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, ) -def test_autochip_move(simulated_test_runner): +def test_autochip_move(gameplay_test_runner): initial_position = tbots_cpp.Point(-3, 1.5) destination = tbots_cpp.Point(0, 1.5) field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -99,7 +99,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: MoveTactic( destination=tbots_cpp.createPointProto(destination), @@ -128,20 +128,20 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=10, ) -def test_autokick_move(simulated_test_runner): +def test_autokick_move(gameplay_test_runner): initial_position = tbots_cpp.Point(-1, -0.5) destination = tbots_cpp.Point(-1, -1) field = tbots_cpp.Field.createSSLDivisionBField() def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[initial_position], blue_robot_orientations=[tbots_cpp.Angle.threeQuarter()], @@ -158,7 +158,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: MoveTactic( destination=tbots_cpp.createPointProto(destination), @@ -186,7 +186,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, @@ -241,10 +241,10 @@ def setup(): ], ) def test_spinning_move( - orientation, initial_position, destination, angular_velocity, simulated_test_runner + orientation, initial_position, destination, angular_velocity, gameplay_test_runner ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[initial_position], yellow_robot_locations=[tbots_cpp.Point(4, 0)], @@ -253,7 +253,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: MoveTactic( destination=tbots_cpp.createPointProto(destination), @@ -285,19 +285,19 @@ def setup(): ], ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=5, ) -def test_move_across_x_axis(simulated_test_runner): +def test_move_across_x_axis(gameplay_test_runner): initial_position = tbots_cpp.Point(-4.4, 0) destination = tbots_cpp.Point(3, 0) def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ initial_position, @@ -308,7 +308,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: MoveTactic( destination=tbots_cpp.createPointProto(destination), @@ -328,7 +328,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, test_timeout_s=6, diff --git a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py index 6d4198f84d..9693f16ddc 100644 --- a/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pass_defender/pass_defender_tactic_test.py @@ -32,10 +32,10 @@ def test_ball_chipped_on_intercept( ball_initial_position, ball_initial_velocity, position_to_block_from, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( [], blue_robot_locations=[position_to_block_from], @@ -44,7 +44,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: PassDefenderTactic( position_to_block_from=tbots_cpp.createPointProto( @@ -71,7 +71,7 @@ def setup(): eventually_validation_sequence_set = [[BallSpeedEventuallyBelowThreshold(0.4)]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -103,10 +103,10 @@ def test_avoid_intercept_scenario( ball_initial_position, ball_initial_velocity, position_to_block_from, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( [], blue_robot_locations=[position_to_block_from], @@ -115,7 +115,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: PassDefenderTactic( position_to_block_from=tbots_cpp.createPointProto( @@ -160,7 +160,7 @@ def setup(): eventually_validation_sequence_set = [[BallSpeedEventuallyBelowThreshold(0.4)]] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -234,10 +234,10 @@ def test_steal_ball( position_to_block_from, enemy_kicker_position, should_steal, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[position_to_block_from], yellow_robot_locations=[enemy_kicker_position], @@ -246,7 +246,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 0: PassDefenderTactic( position_to_block_from=tbots_cpp.createPointProto( @@ -279,7 +279,7 @@ def setup(): FriendlyNeverHasBallPossession(tolerance=0.05) ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index d8140e41cd..9153aa2de0 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -50,7 +50,7 @@ "Disabling this test because of poor dribbling controls, does not consistently score goal. TODO (#2232)" ) def test_penalty_kick( - enemy_robot_location, enemy_robot_velocity, simulated_test_runner + enemy_robot_location, enemy_robot_velocity, gameplay_test_runner ): field = tbots_cpp.Field.createSSLDivisionBField() ball_initial_pos = field.friendlyPenaltyMark() @@ -58,7 +58,7 @@ def test_penalty_kick( def setup(): shooter_position = ball_initial_pos - tbots_cpp.Vector(0.1, 0) - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[shooter_position], yellow_robot_locations=[enemy_robot_location], @@ -67,7 +67,7 @@ def setup(): ), ) - simulated_test_runner.set_tactics(blue_tactics={0: PenaltyKickTactic()}) + gameplay_test_runner.set_tactics(blue_tactics={0: PenaltyKickTactic()}) eventually_validation_sequence_set = [ [ @@ -80,7 +80,7 @@ def setup(): [BallAlwaysMovesForward(ball_initial_pos), NeverExcessivelyDribbles()] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py index ab106b05cd..3527d4c071 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_test.py @@ -34,12 +34,12 @@ @pytest.mark.skip( "Disabling this flaky test. TODO (#2859): the robot does not dribble far enough into the ball" ) -def test_pivot_kick(ball_offset_from_robot, angle_to_kick_at, simulated_test_runner): +def test_pivot_kick(ball_offset_from_robot, angle_to_kick_at, gameplay_test_runner): robot_position = tbots_cpp.Point(0, 0) ball_position = robot_position + ball_offset_from_robot def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -51,7 +51,7 @@ def setup(): ) ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ 1: PivotKickTactic( kick_origin=tbots_cpp.createPointProto(ball_position), @@ -67,7 +67,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=[[]], diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index 9da30fa543..b4e5fd04fd 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -187,14 +187,14 @@ def test_receiver( robot_pos, robot_orientation, one_touch, - simulated_test_runner, + gameplay_test_runner, ): def setup(): ball_velocity = calculate_ball_velocity( passer_point, receiver_point, pass_speed ) - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ tbots_cpp.Point(-3, 2.5), @@ -216,7 +216,7 @@ def setup(): "disable_one_touch_shot": not one_touch, } - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={1: ReceiverTactic(**receiver_args)} ) @@ -232,7 +232,7 @@ def setup(): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, run_till_end=False, diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 1b7b79b9c1..6c6e541fac 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -165,7 +165,7 @@ def hrvo_setup( friendly_robots_final_orientations: list[tbots.Angle], enemy_robots_positions: list[tbots.Point], enemy_robots_destinations: list[tbots.Point], - simulated_test_runner, + gameplay_test_runner, ): """Setup for the hrvo tests @@ -174,7 +174,7 @@ def hrvo_setup( :param friendly_robots_final_orientations: final orientations of friendly robots :param enemy_robots_positions: starting positions of enemy robots :param enemy_robots_destinations: destinations of enemy robots - :param simulated_test_runner: the current test runner being used + :param gameplay_test_runner: the current test runner being used """ desired_orientation = tbots.Angle.fromRadians(0) @@ -182,7 +182,7 @@ def hrvo_setup( ball_initial_vel = tbots.Point(0, 0) - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( yellow_robot_locations=enemy_robots_positions, blue_robot_locations=friendly_robots_positions, @@ -191,13 +191,13 @@ def hrvo_setup( ) ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.BLUE ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.STOP, team=Team.YELLOW ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.FORCE_START, team=Team.BLUE ) @@ -223,7 +223,7 @@ def hrvo_setup( tbots.Angle.fromRadians(0), ) - simulated_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics=blue_tactics, yellow_tactics=yellow_tactics ) @@ -469,7 +469,7 @@ def hrvo_setup( ], ) def test_robot_movement( - simulated_test_runner, + gameplay_test_runner, friendly_robot_positions: list[tbots.Point], friendly_robot_destinations: list[tbots.Point], friendly_robots_final_orientations: list[tbots.Angle], @@ -488,14 +488,14 @@ def test_robot_movement( else get_reached_destination_validation(friendly_robot_destinations) ) - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=lambda: hrvo_setup( friendly_robot_positions, friendly_robot_destinations, friendly_robots_final_orientations, enemy_robots_positions, enemy_robots_destinations, - simulated_test_runner, + gameplay_test_runner, ), eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, diff --git a/src/software/conftest.py b/src/software/conftest.py index b66836a7b4..aed4da95be 100644 --- a/src/software/conftest.py +++ b/src/software/conftest.py @@ -1,6 +1,5 @@ import pytest # noqa: F401 -from software.gameplay_tests.fixture import simulated_test_runner # noqa: F401 -from software.gameplay_tests.fixture import field_test_runner # noqa: F401 +from software.gameplay_tests.fixture import gameplay_test_runner # noqa: F401 # Pytest requires that all tests fixtures shared across a package be defined # in a single conftest.py file in the parent directory of the package. diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 54bd50c769..a651cfbf92 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -33,8 +33,15 @@ @pytest.fixture -def simulated_test_runner(): +def gameplay_test_runner(): args = load_command_line_arguments() + if args.run_field_test: + yield from (args) + else: + yield from simulated_test_runner(args) + + +def simulated_test_runner(args): tscope = None simulator_proto_unix_io = ProtoUnixIO() @@ -118,7 +125,7 @@ def simulated_test_runner(): @pytest.fixture -def field_test_runner(): +def field_test_runner(args): """Runs a field test :return: yields the runner to the test fixture @@ -126,7 +133,6 @@ def field_test_runner(): simulator_proto_unix_io = ProtoUnixIO() yellow_full_system_proto_unix_io = ProtoUnixIO() blue_full_system_proto_unix_io = ProtoUnixIO() - args = load_command_line_arguments() # Grab the current test name to store the proto log for the test case current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index f59abd6394..441150519b 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -1,6 +1,6 @@ from proto.import_all_protos import * from software.gameplay_tests.fixture import * -from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT +from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT from software.gameplay_tests.util import pytest_main from software.logger.logger import create_logger @@ -69,10 +69,10 @@ # this test can only be run on the field -def test_basic_rotation(field_test_runner): +def test_basic_rotation(gameplay_test_runner): test_angles = [0, 45, 90, 180, 270, 0] - world = field_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) + world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) if len(world.friendly_team.team_robots) == 0: raise Exception("The first world received had no robots in it!") @@ -104,16 +104,16 @@ def test_basic_rotation(field_test_runner): move_tactic.obstacle_avoidance_mode = ObstacleAvoidanceMode.SAFE # Setup Tactic - field_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={id: move_tactic}, yellow_tactics=None ) - field_test_runner.run_test( + gameplay_test_runner.run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=5, ) # Send a halt tactic after the test finishes - field_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={id: HaltTactic()}, yellow_tactics=None ) @@ -123,8 +123,8 @@ def test_basic_rotation(field_test_runner): time.sleep(2) -def test_one_robots_square(field_test_runner): - world = field_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) +def test_one_robots_square(gameplay_test_runner): + world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) if len(world.friendly_team.team_robots) == 0: raise Exception("The first world received had no robots in it!") @@ -185,20 +185,20 @@ def test_one_robots_square(field_test_runner): for tactic in tactics: print(f"Going to {tactic.destination}") - field_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ id: tactic, }, yellow_tactics=None, ) - field_test_runner.run_test( + gameplay_test_runner.run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=4, ) # Send a halt tactic after the test finishes - field_test_runner.set_tactics(blue_tactics={id: HaltTactic()}, yellow_tactics=None) + gameplay_test_runner.set_tactics(blue_tactics={id: HaltTactic()}, yellow_tactics=None) if __name__ == "__main__": diff --git a/src/software/gameplay_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py index 16a87ae4fe..dacb54869d 100644 --- a/src/software/gameplay_tests/passing_field_test.py +++ b/src/software/gameplay_tests/passing_field_test.py @@ -1,19 +1,19 @@ import software.python_bindings as tbots_cpp from proto.import_all_protos import * from software.gameplay_tests.fixture import * -from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT +from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) from software.gameplay_tests.util import pytest_main -def test_passing(field_test_runner): +def test_passing(gameplay_test_runner): passer_robot_id = 3 receiver_robot_id = 5 should_receive_pass = True - world = field_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) + world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) passer_point = tbots_cpp.createPoint(world.ball.current_state.global_position) receiver_point = None for robot in world.friendly_team.team_robots: @@ -84,15 +84,15 @@ def test_passing(field_test_runner): ] ] - field_test_runner.set_tactics(blue_tactics=blue_tactics, yellow_tactics=None) - field_test_runner.run_test( + gameplay_test_runner.set_tactics(blue_tactics=blue_tactics, yellow_tactics=None) + gameplay_test_runner.run_test( always_validation_sequence_set=always_validation_sequence_set, eventually_validation_sequence_set=[[]], test_timeout_s=5, ) # Send a halt tactic after the test finishes - field_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ passer_robot_id: HaltTactic(), receiver_robot_id: HaltTactic(), diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py index 077447c6d8..10713be612 100644 --- a/src/software/gameplay_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -1,7 +1,7 @@ import math from proto.import_all_protos import * from software.gameplay_tests.fixture import * -from software.gameplay_tests.field_test_runner import WORLD_BUFFER_TIMEOUT +from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT from software.logger.logger import create_logger from software.gameplay_tests.util import pytest_main @@ -9,10 +9,10 @@ logger = create_logger(__name__) -def test_pivot_kick(field_test_runner): +def test_pivot_kick(gameplay_test_runner): id = 5 - world = field_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) + world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) print("Here are the robots:") print( [ @@ -21,7 +21,7 @@ def test_pivot_kick(field_test_runner): ] ) - field_test_runner.set_tactics( + gameplay_test_runner.set_tactics( blue_tactics={ id: PivotKickTactic( kick_origin=Point(x_meters=-1.13, y_meters=0.75), @@ -31,14 +31,14 @@ def test_pivot_kick(field_test_runner): }, yellow_tactics=None, ) - field_test_runner.run_test( + gameplay_test_runner.run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=15, ) # Send a halt tactic after the test finishes - field_test_runner.set_tactics(blue_tactics={id: HaltTactic()}, yellow_tactics=None) + gameplay_test_runner.set_tactics(blue_tactics={id: HaltTactic()}, yellow_tactics=None) if __name__ == "__main__": diff --git a/src/software/gameplay_tests/simulated_test_ball_model.py b/src/software/gameplay_tests/simulated_test_ball_model.py index 87f7b79696..58f171292c 100644 --- a/src/software/gameplay_tests/simulated_test_ball_model.py +++ b/src/software/gameplay_tests/simulated_test_ball_model.py @@ -44,10 +44,10 @@ def test_simulator_move_ball( ball_initial_position, ball_initial_velocity, - simulated_test_runner, + gameplay_test_runner, ): # Setup Ball - simulated_test_runner.simulator_proto_unix_io.send_proto( + gameplay_test_runner.simulator_proto_unix_io.send_proto( WorldState, create_world_state( [], @@ -60,13 +60,13 @@ def test_simulator_move_ball( # Setup Tactic params = AssignedTacticPlayControlParams() - simulated_test_runner.blue_full_system_proto_unix_io.send_proto( + gameplay_test_runner.blue_full_system_proto_unix_io.send_proto( AssignedTacticPlayControlParams, params ) # Setup no tactics on the enemy side params = AssignedTacticPlayControlParams() - simulated_test_runner.yellow_full_system_proto_unix_io.send_proto( + gameplay_test_runner.yellow_full_system_proto_unix_io.send_proto( AssignedTacticPlayControlParams, params ) @@ -102,20 +102,20 @@ def test_simulator_move_ball( ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( test_timeout_s=8, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, ) -def test_ball_robot_collision(simulated_test_runner): +def test_ball_robot_collision(gameplay_test_runner): ball_initial_position = tbots_cpp.Field.createSSLDivisionBField().centerPoint() ball_initial_velocity = tbots_cpp.Vector(2.5, 0) robot_position = tbots_cpp.Point(2.5, 0) # Setup Robot - simulated_test_runner.simulator_proto_unix_io.send_proto( + gameplay_test_runner.simulator_proto_unix_io.send_proto( WorldState, create_world_state( [], @@ -126,7 +126,7 @@ def test_ball_robot_collision(simulated_test_runner): ) # Setup Ball - simulated_test_runner.simulator_proto_unix_io.send_proto( + gameplay_test_runner.simulator_proto_unix_io.send_proto( WorldState, create_world_state( [], @@ -139,13 +139,13 @@ def test_ball_robot_collision(simulated_test_runner): # Setup Tactic params = AssignedTacticPlayControlParams() - simulated_test_runner.blue_full_system_proto_unix_io.send_proto( + gameplay_test_runner.blue_full_system_proto_unix_io.send_proto( AssignedTacticPlayControlParams, params ) # Setup no tactics on the enemy side params = AssignedTacticPlayControlParams() - simulated_test_runner.yellow_full_system_proto_unix_io.send_proto( + gameplay_test_runner.yellow_full_system_proto_unix_io.send_proto( AssignedTacticPlayControlParams, params ) @@ -180,7 +180,7 @@ def test_ball_robot_collision(simulated_test_runner): ] ] - simulated_test_runner.run_test( + gameplay_test_runner.run_test( eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/sensor_fusion/filter/ball_occlusion_test.py b/src/software/sensor_fusion/filter/ball_occlusion_test.py index 50ca0c0d80..54d15e52d4 100644 --- a/src/software/sensor_fusion/filter/ball_occlusion_test.py +++ b/src/software/sensor_fusion/filter/ball_occlusion_test.py @@ -169,10 +169,10 @@ def test_ball_occlusion( ball_velocity, blue_robot_positions, yellow_robot_positions, - simulated_test_runner, + gameplay_test_runner, ): def setup(): - simulated_test_runner.set_world_state( + gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=blue_robot_positions, yellow_robot_locations=yellow_robot_positions, @@ -181,18 +181,18 @@ def setup(): ), ) - simulated_test_runner.send_gamecontroller_command( + gameplay_test_runner.send_gamecontroller_command( gc_command=Command.Type.HALT, team=Team.UNKNOWN ) - simulated_test_runner.set_plays( + gameplay_test_runner.set_plays( blue_play=PlayName.HaltPlay, yellow_play=PlayName.HaltPlay ) # This test validates that the ball tracking/filter handles occlusion correctly. # The validation simply waits for the simulation to run long enough (10s). # If the system crashes or has tracking issues, the test will fail. - simulated_test_runner.run_test( + gameplay_test_runner.run_test( setup=setup, test_timeout_s=10, ) From 6c3c3f1c9446d987a7b7ac97ff5374834835287c Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:13:22 -0700 Subject: [PATCH 13/45] Pull out helper function --- src/software/gameplay_tests/fixture.py | 35 ++++++++++++-------------- src/software/gameplay_tests/util.py | 26 +++++++++++++++++++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index a651cfbf92..600835ff2e 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -1,4 +1,3 @@ -import os import time import pytest @@ -11,7 +10,11 @@ from proto.import_all_protos import World from software.gameplay_tests.field_test_runner import FieldTestRunner from software.gameplay_tests.simulated_test_runner import SimulatedTestRunner -from software.gameplay_tests.util import load_command_line_arguments +from software.gameplay_tests.util import ( + load_command_line_arguments, + get_pytest_name, + get_pytest_path_name, +) from software.logger.logger import create_logger from software.thunderscope.binary_context_managers.full_system import FullSystem from software.thunderscope.binary_context_managers.game_controller import Gamecontroller @@ -49,23 +52,19 @@ def simulated_test_runner(args): blue_full_system_proto_unix_io = ProtoUnixIO() # Grab the current test name to store the proto log for the test case - current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] - current_test = current_test.replace("]", "") - current_test = current_test.replace("[", "-") - - # Truncate the test name to 25 characters for UNIX path length limits - test_name = current_test.split("-")[0][:25] + test_name = get_pytest_name() + test_path_name = get_pytest_path_name() # Launch all binaries with ( Simulator( - f"{args.simulator_runtime_dir}/test/{test_name}", + f"{args.simulator_runtime_dir}/test/{test_path_name}", args.debug_simulator, args.enable_realism, ) as simulator, FullSystem( "software/unix_full_system", - f"{args.blue_full_system_runtime_dir}/test/{test_name}", + f"{args.blue_full_system_runtime_dir}/test/{test_path_name}", args.debug_blue_full_system, False, should_restart_on_crash=False, @@ -73,7 +72,7 @@ def simulated_test_runner(args): ) as blue_fs, FullSystem( "software/unix_full_system", - f"{args.yellow_full_system_runtime_dir}/test/{test_name}", + f"{args.yellow_full_system_runtime_dir}/test/{test_path_name}", args.debug_yellow_full_system, True, should_restart_on_crash=False, @@ -112,7 +111,7 @@ def simulated_test_runner(args): time.sleep(LAUNCH_DELAY_S) runner = SimulatedTestRunner( - current_test, + test_name, tscope, simulator_proto_unix_io, blue_full_system_proto_unix_io, @@ -135,18 +134,16 @@ def field_test_runner(args): blue_full_system_proto_unix_io = ProtoUnixIO() # Grab the current test name to store the proto log for the test case - current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] - current_test = current_test.replace("]", "") - current_test = current_test.replace("[", "-") + test_name = get_pytest_name() + test_path_name = get_pytest_path_name() - test_name = current_test.split("-")[0] debug_full_sys = args.debug_blue_full_system - runtime_dir = f"{args.blue_full_system_runtime_dir}/test/{test_name}" + runtime_dir = f"{args.blue_full_system_runtime_dir}/test/{test_path_name}" friendly_proto_unix_io = blue_full_system_proto_unix_io if args.run_yellow: debug_full_sys = args.debug_yellow_full_system - runtime_dir = f"{args.yellow_full_system_runtime_dir}/test/{test_name}" + runtime_dir = f"{args.yellow_full_system_runtime_dir}/test/{test_path_name}" friendly_proto_unix_io = yellow_full_system_proto_unix_io estop_mode, estop_path = get_estop_config( @@ -223,7 +220,7 @@ def field_test_runner(args): time.sleep(LAUNCH_DELAY_S) runner = FieldTestRunner( - test_name=current_test, + test_name=test_name, blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, gamecontroller=gamecontroller, diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index 80cc01fd96..c506239052 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -20,6 +20,32 @@ def get_runtime_dir(): return runtime_dir +def get_pytest_name(): + """Gets the name of the currently running pytest test. + + Sanitizes the test name by replacing square brackets with hyphens. + + :return: The sanitized test name. + """ + current_test = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0] + current_test = current_test.replace("]", "") + current_test = current_test.replace("[", "-") + return current_test + + +def get_pytest_path_name(): + """Gets the base test component name, truncated for filesystem compatibility. + + Extracts just the test name (before any parametrize suffix) and truncates + to 25 characters to adhere to UNIX path length limits. + + :return: The truncated base test name. + """ + + # Truncate the test name to 25 characters for UNIX path length limits + return get_pytest_name().split("-")[0][:25] + + def load_command_line_arguments(allow_unrecognized: bool = False): """Load in command-line arguments using argparse From f9f32d7d338e9fb69546f30c54fed7ad96ebe83a Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:17:31 -0700 Subject: [PATCH 14/45] Minor fix and doc comment updates --- src/software/gameplay_tests/fixture.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 600835ff2e..13062adaac 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -37,14 +37,23 @@ @pytest.fixture def gameplay_test_runner(): + """Dispatches to the field or simulated test runner based on --run_field_test. + + :yields: A SimulatedTestRunner or FieldTestRunner. + """ args = load_command_line_arguments() if args.run_field_test: - yield from (args) + yield from field_test_runner(args) else: yield from simulated_test_runner(args) def simulated_test_runner(args): + """Starts the simulator, blue/yellow full systems, and gamecontroller binaries. + + :param args: Parsed command-line arguments. + :yields: A SimulatedTestRunner. + """ tscope = None simulator_proto_unix_io = ProtoUnixIO() @@ -123,11 +132,11 @@ def simulated_test_runner(args): yield runner -@pytest.fixture def field_test_runner(args): - """Runs a field test + """Starts the friendly full system, gamecontroller, WiFi, and robot communication. - :return: yields the runner to the test fixture + :param args: Parsed command-line arguments. + :yields: A FieldTestRunner. """ simulator_proto_unix_io = ProtoUnixIO() yellow_full_system_proto_unix_io = ProtoUnixIO() From 73562f3222fa743766ef9600a6b46227296b8c05 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:21:53 -0700 Subject: [PATCH 15/45] Move enable_thunderscope to sim only args --- src/software/gameplay_tests/util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index c506239052..84a61bfb4c 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -68,9 +68,6 @@ def load_command_line_arguments(allow_unrecognized: bool = False): default=False, help="Runs test as a field test instead of a simulated test", ) - general_group.add_argument( - "--enable_thunderscope", action="store_true", help="enable thunderscope" - ) general_group.add_argument( "--test_filter", action="store", @@ -133,6 +130,9 @@ def load_command_line_arguments(allow_unrecognized: bool = False): default=False, help="Debug the simulator", ) + simulated_group.add_argument( + "--enable_thunderscope", action="store_true", help="enable thunderscope" + ) simulated_group.add_argument( "--enable_realism", action="store_true", From 8e8856abc48a1d8ab783b97938d173535d125bb8 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:24:04 -0700 Subject: [PATCH 16/45] Fix capitalization --- src/software/gameplay_tests/util.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index 84a61bfb4c..89107abede 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -78,13 +78,13 @@ def load_command_line_arguments(allow_unrecognized: bool = False): general_group.add_argument( "--blue_full_system_runtime_dir", type=str, - help="blue full_system runtime directory", + help="Blue full_system runtime directory", default=os.path.join(RUNTIME_DIR, "blue"), ) general_group.add_argument( "--yellow_full_system_runtime_dir", type=str, - help="yellow full_system runtime directory", + help="Yellow full_system runtime directory", default=os.path.join(RUNTIME_DIR, "yellow"), ) general_group.add_argument( @@ -121,7 +121,7 @@ def load_command_line_arguments(allow_unrecognized: bool = False): simulated_group.add_argument( "--simulator_runtime_dir", type=str, - help="simulator runtime directory", + help="Simulator runtime directory", default=RUNTIME_DIR, ) simulated_group.add_argument( @@ -131,7 +131,7 @@ def load_command_line_arguments(allow_unrecognized: bool = False): help="Debug the simulator", ) simulated_group.add_argument( - "--enable_thunderscope", action="store_true", help="enable thunderscope" + "--enable_thunderscope", action="store_true", help="Enable thunderscope" ) simulated_group.add_argument( "--enable_realism", From 46021c3481eada12bfd8f28f0f872d9801faa44c Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 12:29:15 -0700 Subject: [PATCH 17/45] Port field test runner updates from #3783 --- .../gameplay_tests/field_test_runner.py | 87 ++++++++++++++----- src/software/gameplay_tests/fixture.py | 1 + 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index aed73b1337..d199e26eb4 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -18,7 +18,6 @@ PROCESS_BUFFER_DELAY_S = 0.01 PAUSE_AFTER_FAIL_DELAY_S = 3 LAUNCH_DELAY_S = 0.1 -TEST_END_DELAY = 0.3 class FieldTestRunner(TbotsTestRunner): @@ -31,6 +30,7 @@ def __init__( blue_full_system_proto_unix_io, yellow_full_system_proto_unix_io, gamecontroller, + robot_communication, publish_validation_protos=True, is_yellow_friendly=False, ): @@ -41,6 +41,7 @@ def __init__( :param blue_full_system_proto_unix_io: The blue full system proto unix io to use :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use :param gamecontroller: The gamecontroller context managed instance + :param robot_communication: The robot communication instance :param publish_validation_protos: whether to publish validation protos :param: is_yellow_friendly: if yellow is the friendly team """ @@ -54,26 +55,45 @@ def __init__( ) self.publish_validation_protos = publish_validation_protos self.is_yellow_friendly = is_yellow_friendly + self.robot_communication = robot_communication logger.info("determining robots on field") # survey field for available robot ids - try: - world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - self.initial_world = world - self.friendly_robot_ids_field = [ - robot.id for robot in world.friendly_team.team_robots - ] - - logger.info(f"friendly team ids {self.friendly_robot_ids_field}") - - if len(self.friendly_robot_ids_field) == 0: - raise Exception("no friendly robots found on field") - - except queue.Empty: - raise Exception( - f"No Worlds were received with in {WORLD_BUFFER_TIMEOUT} seconds. \ - Please make sure atleast 1 robot and 1 ball is present on the field." - ) + survey_start_time = time.time() + self.friendly_robot_ids_field = [] + while time.time() - survey_start_time < WORLD_BUFFER_TIMEOUT: + try: + world = self.world_buffer.get(block=True, timeout=0.1) + self.initial_world = world + self.friendly_robot_ids_field = [ + robot.id for robot in world.friendly_team.team_robots + ] + + if len(self.friendly_robot_ids_field) > 0: + logger.info(f"friendly team ids {self.friendly_robot_ids_field}") + break + except queue.Empty: + continue + + if len(self.friendly_robot_ids_field) == 0: + raise Exception("no friendly robots found on field within timeout") + + def wait_for_estop_play(self): + """Blocks until the estop is in the PLAY state""" + if self.robot_communication.estop_is_playing: + return + + logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") + while not self.robot_communication.estop_is_playing: + # We must process events if Thunderscope is running to keep it responsive + if self.thunderscope: + from pyqtgraph.Qt import QtWidgets + + QtWidgets.QApplication.processEvents() + time.sleep(0.1) + logger.info( + "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" + ) @override def send_gamecontroller_command( @@ -88,7 +108,7 @@ def send_gamecontroller_command( :param team: The team which the command as attributed to :param final_ball_placement_point: The ball placement point """ - self.gamecontroller.send_ci_input( + self.gamecontroller.send_gc_command( gc_command=gc_command, team=team, final_ball_placement_point=final_ball_placement_point, @@ -125,14 +145,19 @@ def run_test( def stop_test(delay): time.sleep(delay) - if self.thunderscope: - self.thunderscope.close() + # We no longer close thunderscope here, because a test might call run_test multiple times. + # Thunderscope will be closed when the fixture is torn down. def __runner(): time.sleep(LAUNCH_DELAY_S) test_end_time = time.time() + test_timeout_s + # Keep track if we started with any eventually validations + has_eventually_validations = any( + len(seq) > 0 for seq in eventually_validation_sequence_set + ) + while time.time() < test_end_time: while True: try: @@ -173,9 +198,15 @@ def __runner(): # Check that all always validations are always valid validation.check_validation(always_validation_proto_set) - # Check that all eventually validations are eventually valid + # Break if eventually validation passes + if has_eventually_validations and all( + len(seq) == 0 for seq in eventually_validation_sequence_set + ): + break + validation.check_validation(eventually_validation_proto_set) - stop_test(TEST_END_DELAY) + + stop_test(delay=PROCESS_BUFFER_DELAY_S) def excepthook(args): """This function is _critical_ for show_thunderscope to work. @@ -190,10 +221,18 @@ def excepthook(args): threading.excepthook = excepthook + # If visualization is enabled, we need to be careful. + # Thunderscope.show() is blocking. if self.thunderscope: run_test_thread = threading.Thread(target=__runner, daemon=True) run_test_thread.start() - self.thunderscope.show() + + # Only call show if the window is not already open. + # If it IS open, it means we are ALREADY in the Qt event loop, + # which can only happen if we are running this run_test in a background thread. + if not self.thunderscope.is_open(): + self.thunderscope.show() + run_test_thread.join() if self.last_exception: diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 13062adaac..f04f606681 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -234,6 +234,7 @@ def field_test_runner(args): yellow_full_system_proto_unix_io=yellow_full_system_proto_unix_io, gamecontroller=gamecontroller, thunderscope=tscope, + robot_communication=rc_friendly, is_yellow_friendly=args.run_yellow, ) From 71553d99644c0148193ec55d1a7211633dec5c69 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 13:15:11 -0700 Subject: [PATCH 18/45] Clean up field and simulated test runner classes --- .../gameplay_tests/field_test_runner.py | 253 +++++++++--------- .../gameplay_tests/simulated_test_runner.py | 144 +++++----- 2 files changed, 192 insertions(+), 205 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index d199e26eb4..fd3e62c466 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -57,62 +57,7 @@ def __init__( self.is_yellow_friendly = is_yellow_friendly self.robot_communication = robot_communication - logger.info("determining robots on field") - # survey field for available robot ids - survey_start_time = time.time() - self.friendly_robot_ids_field = [] - while time.time() - survey_start_time < WORLD_BUFFER_TIMEOUT: - try: - world = self.world_buffer.get(block=True, timeout=0.1) - self.initial_world = world - self.friendly_robot_ids_field = [ - robot.id for robot in world.friendly_team.team_robots - ] - - if len(self.friendly_robot_ids_field) > 0: - logger.info(f"friendly team ids {self.friendly_robot_ids_field}") - break - except queue.Empty: - continue - - if len(self.friendly_robot_ids_field) == 0: - raise Exception("no friendly robots found on field within timeout") - - def wait_for_estop_play(self): - """Blocks until the estop is in the PLAY state""" - if self.robot_communication.estop_is_playing: - return - - logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") - while not self.robot_communication.estop_is_playing: - # We must process events if Thunderscope is running to keep it responsive - if self.thunderscope: - from pyqtgraph.Qt import QtWidgets - - QtWidgets.QApplication.processEvents() - time.sleep(0.1) - logger.info( - "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" - ) - - @override - def send_gamecontroller_command( - self, - gc_command: Command, - team: Team, - final_ball_placement_point=None, - ): - """Send a command to the gamecontroller - - :param gc_command: The command to send - :param team: The team which the command as attributed to - :param final_ball_placement_point: The ball placement point - """ - self.gamecontroller.send_gc_command( - gc_command=gc_command, - team=team, - final_ball_placement_point=final_ball_placement_point, - ) + self._survey_field_robots() @override def set_world_state(self, world_state: WorldState): @@ -143,100 +88,142 @@ def run_test( remain after the timeout, the test fails. """ - def stop_test(delay): - time.sleep(delay) - # We no longer close thunderscope here, because a test might call run_test multiple times. - # Thunderscope will be closed when the fixture is torn down. + threading.excepthook = self._excepthook - def __runner(): - time.sleep(LAUNCH_DELAY_S) + run_test_thread = threading.Thread( + target=self._runner, + daemon=True, + args=[ + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + ], + ) - test_end_time = time.time() + test_timeout_s + run_test_thread.start() - # Keep track if we started with any eventually validations - has_eventually_validations = any( - len(seq) > 0 for seq in eventually_validation_sequence_set - ) + self.thunderscope.show() - while time.time() < test_end_time: - while True: - try: - world = self.world_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT - ) - break - except queue.Empty: - # If we timeout, that means full_system missed the last - # wrapper and robot status, lets resend it. - logger.warning( - f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." - ) - - # Validate - ( - eventually_validation_proto_set, - always_validation_proto_set, - ) = validation.run_validation_sequence_sets( - world, - eventually_validation_sequence_set, - always_validation_sequence_set, - ) + run_test_thread.join() - if self.publish_validation_protos: - # Set the test name - eventually_validation_proto_set.test_name = self.test_name - always_validation_proto_set.test_name = self.test_name + if self.last_exception: + pytest.fail(str(self.last_exception)) - # Send out the validation proto to thunderscope - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, eventually_validation_proto_set + def _wait_for_estop_play(self): + """Blocks until the estop is in the PLAY state""" + if self.robot_communication.estop_is_playing: + return + + logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") + while not self.robot_communication.estop_is_playing: + # We must process events if Thunderscope is running to keep it responsive + if self.thunderscope: + from pyqtgraph.Qt import QtWidgets + + QtWidgets.QApplication.processEvents() + time.sleep(0.1) + logger.info( + "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" + ) + + def _stop_test(self, delay): + time.sleep(delay) + self.thunderscope.close() + + def _runner( + self, + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + ): + time.sleep(LAUNCH_DELAY_S) + + test_end_time = time.time() + test_timeout_s + + # Keep track if we started with any eventually validations + has_eventually_validations = any( + len(seq) > 0 for seq in eventually_validation_sequence_set + ) + + while time.time() < test_end_time: + while True: + try: + world = self.world_buffer.get( + block=True, timeout=WORLD_BUFFER_TIMEOUT ) - self.blue_full_system_proto_unix_io.send_proto( - ValidationProtoSet, always_validation_proto_set + break + except queue.Empty: + # If we timeout, that means full_system missed the last + # wrapper and robot status, lets resend it. + logger.warning( + f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." ) - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) + # Validate + ( + eventually_validation_proto_set, + always_validation_proto_set, + ) = validation.run_validation_sequence_sets( + world, + eventually_validation_sequence_set, + always_validation_sequence_set, + ) - # Break if eventually validation passes - if has_eventually_validations and all( - len(seq) == 0 for seq in eventually_validation_sequence_set - ): - break + if self.publish_validation_protos: + # Set the test name + eventually_validation_proto_set.test_name = self.test_name + always_validation_proto_set.test_name = self.test_name - validation.check_validation(eventually_validation_proto_set) + # Send out the validation proto to thunderscope + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, eventually_validation_proto_set + ) + self.blue_full_system_proto_unix_io.send_proto( + ValidationProtoSet, always_validation_proto_set + ) - stop_test(delay=PROCESS_BUFFER_DELAY_S) + # Check that all always validations are always valid + validation.check_validation(always_validation_proto_set) - def excepthook(args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. + # Break if eventually validation passes + if has_eventually_validations and all( + len(seq) == 0 for seq in eventually_validation_sequence_set + ): + break - :param args: The args passed in from the hook - """ - stop_test(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception + validation.check_validation(eventually_validation_proto_set) - threading.excepthook = excepthook + self._stop_test(delay=PROCESS_BUFFER_DELAY_S) - # If visualization is enabled, we need to be careful. - # Thunderscope.show() is blocking. - if self.thunderscope: - run_test_thread = threading.Thread(target=__runner, daemon=True) - run_test_thread.start() + def _excepthook(self, args): + """This function is _critical_ for show_thunderscope to work. + If the test Thread will raises an exception we won't be able to close + the window from the main thread. - # Only call show if the window is not already open. - # If it IS open, it means we are ALREADY in the Qt event loop, - # which can only happen if we are running this run_test in a background thread. - if not self.thunderscope.is_open(): - self.thunderscope.show() + :param args: The args passed in from the hook + """ + self._stop_test(delay=PAUSE_AFTER_FAIL_DELAY_S) + self.last_exception = args.exc_value + raise self.last_exception - run_test_thread.join() + def _survey_field_robots(self): + logger.info("determining robots on field") + # survey field for available robot ids + survey_start_time = time.time() + self.friendly_robot_ids_field = [] + while time.time() - survey_start_time < WORLD_BUFFER_TIMEOUT: + try: + world = self.world_buffer.get(block=True, timeout=0.1) + self.initial_world = world + self.friendly_robot_ids_field = [ + robot.id for robot in world.friendly_team.team_robots + ] - if self.last_exception: - pytest.fail(str(self.last_exception)) + if len(self.friendly_robot_ids_field) > 0: + logger.info(f"friendly team ids {self.friendly_robot_ids_field}") + break + except queue.Empty: + continue - else: - __runner() + if len(self.friendly_robot_ids_field) == 0: + raise Exception("no friendly robots found on field within timeout") diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 290e990532..1d046eb564 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -61,18 +61,83 @@ def set_world_state(self, worldstate: WorldState): """ self.simulator_proto_unix_io.send_proto(WorldState, worldstate) - def excepthook(self, args): + @override + def run_test( + self, + setup=lambda: None, + always_validation_sequence_set=[[]], + eventually_validation_sequence_set=[[]], + test_timeout_s=3, + tick_duration_s=0.0166, + ci_cmd_with_delay=[], + run_till_end=True, + ): + """Helper function to run a test, with thunderscope if enabled + + :param always_validation_sequence_set: validation that should always be true + :param eventually_validation_sequence_set: validation that should eventually be true + :param setup: function that sets up the World state and the gamecontroller before running the test + :param test_timeout_s: how long the test should run before timing out + :param tick_duration_s: length of a simulation tick + :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. + [ + (time, command, team), + (time, command, team), + ... + ] + :param run_till_end: If true, test runs till the end even if eventually validation passes + If false, test stops once eventually validation passes and fails if time out + """ + threading.excepthook = self._excepthook + + self._sync_setup(setup) + + # If thunderscope is enabled, run the test in a thread and show + # thunderscope on this thread. The excepthook is setup to catch + # any test failures and propagate them to the main thread + if self.thunderscope: + run_sim_thread = threading.Thread( + target=self._runner, + daemon=True, + args=[ + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + tick_duration_s, + ci_cmd_with_delay, + run_till_end, + ], + ) + run_sim_thread.start() + self.thunderscope.show() + run_sim_thread.join() + + if self.last_exception: + pytest.fail(str(self.last_exception)) + + # If thunderscope is disabled, just run the test + else: + self._runner( + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + tick_duration_s, + ci_cmd_with_delay=ci_cmd_with_delay, + run_till_end=run_till_end, + ) + + def _excepthook(self, args): """This function is _critical_ for show_thunderscope to work. If the test Thread will raises an exception we won't be able to close the window from the main thread. :param args: The args passed in from the hook """ - self.__stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) + self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) self.last_exception = args.exc_value raise self.last_exception - def __stopper(self, delay=PROCESS_BUFFER_DELAY_S): + def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): """Stop running the test :param delay: How long to wait before closing everything, defaults @@ -83,7 +148,7 @@ def __stopper(self, delay=PROCESS_BUFFER_DELAY_S): if self.thunderscope: self.thunderscope.close() - def sync_setup(self, setup): + def _sync_setup(self, setup): """Run setup until simulator has received game state :param setup: Function that sets up the world state @@ -108,7 +173,7 @@ def sync_setup(self, setup): # Received a response from the simulator break - def runner( + def _runner( self, always_validation_sequence_set, eventually_validation_sequence_set, @@ -235,7 +300,7 @@ def runner( try: # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) - self.__stopper() + self._stopper() return except AssertionError as e: eventually_validation_failure_msg = str(e) @@ -246,69 +311,4 @@ def runner( # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) - self.__stopper() - - @override - def run_test( - self, - setup=lambda: None, - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], - test_timeout_s=3, - tick_duration_s=0.0166, - ci_cmd_with_delay=[], - run_till_end=True, - ): - """Helper function to run a test, with thunderscope if enabled - - :param always_validation_sequence_set: validation that should always be true - :param eventually_validation_sequence_set: validation that should eventually be true - :param setup: function that sets up the World state and the gamecontroller before running the test - :param test_timeout_s: how long the test should run before timing out - :param tick_duration_s: length of a simulation tick - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out - """ - threading.excepthook = self.excepthook - - self.sync_setup(setup) - - # If thunderscope is enabled, run the test in a thread and show - # thunderscope on this thread. The excepthook is setup to catch - # any test failures and propagate them to the main thread - if self.thunderscope: - run_sim_thread = threading.Thread( - target=self.runner, - daemon=True, - args=[ - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, - ], - ) - run_sim_thread.start() - self.thunderscope.show() - run_sim_thread.join() - - if self.last_exception: - pytest.fail(str(self.last_exception)) - - # If thunderscope is disabled, just run the test - else: - self.runner( - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - tick_duration_s, - ci_cmd_with_delay=ci_cmd_with_delay, - run_till_end=run_till_end, - ) + self._stopper() From 3edd9ed8a6e31c3eab8a6026355bb5401c06690a Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 13:15:36 -0700 Subject: [PATCH 19/45] Rewrite field test in sim test style --- .../movement_robot_field_test.py | 288 +++++++----------- 1 file changed, 117 insertions(+), 171 deletions(-) diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index 99daffb608..7036aa3417 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -1,192 +1,138 @@ import math -import threading -from proto.import_all_protos import * -from software.gameplay_tests.fixture import * -from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT +import pytest +import software.python_bindings as tbots_cpp +from proto.import_all_protos import * +from proto.message_translation.tbots_protobuf import create_world_state from software.gameplay_tests.util import pytest_main -from software.logger.logger import create_logger - -logger = create_logger(__name__) - - -# this test can only be run on the field -def test_basic_rotation(gameplay_test_runner): - test_angles = [0, 45, 90, 180, 270, 0] - - world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - if len(world.friendly_team.team_robots) == 0: - raise Exception("The first world received had no robots in it!") - - print("Here are the robots:") - print( - [ - robot.current_state.global_position - for robot in world.friendly_team.team_robots - ] - ) - - id = world.friendly_team.team_robots[0].id - print(f"Running test on robot {id}") - - robot = world.friendly_team.team_robots[0] - rob_pos_p = robot.current_state.global_position - - def execute_test(): - # Wait for the user to flip the estop to PLAY - field_test_runner.wait_for_estop_play() - - # Force start the game automatically - field_test_runner.gamecontroller.send_gc_command( - proto.ssl_gc_state_pb2.Command.FORCE_START, - proto.ssl_gc_common_pb2.Team.UNKNOWN, - final_ball_placement_point=None, +from software.gameplay_tests.validation.delay_validation import DelayValidation +from software.gameplay_tests.validation.duration_validation import DurationValidation +from software.gameplay_tests.validation.robot_at_orientation import ( + RobotEventuallyAtOrientation, +) +from software.gameplay_tests.validation.robot_enters_region import ( + RobotEventuallyEntersRegion, +) + + +@pytest.mark.parametrize( + "angle", + [0, 45, 90, 180, 270, 0], +) +def test_basic_rotation(angle, gameplay_test_runner): + target_angle = tbots_cpp.Angle.fromDegrees(angle) + start_position = tbots_cpp.Point(-1.5, 0.6) + + def setup(): + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[ + start_position, + ], + yellow_robot_locations=[], + ball_location=tbots_cpp.Point(0, 0), + ball_velocity=tbots_cpp.Vector(0, 0), + ), ) - for angle in test_angles: - print(f"Rotating to {angle} degrees") - move_tactic = MoveTactic() - move_tactic.destination.CopyFrom(rob_pos_p) - move_tactic.dribbler_mode = DribblerMode.OFF - move_tactic.final_orientation.CopyFrom( - Angle(radians=angle * math.pi / 180.0) - ) - move_tactic.ball_collision_type = BallCollisionType.AVOID - move_tactic.auto_chip_or_kick.CopyFrom( - AutoChipOrKick(autokick_speed_m_per_s=0.0) - ) - move_tactic.max_allowed_speed_mode = MaxAllowedSpeedMode.PHYSICAL_LIMIT - move_tactic.obstacle_avoidance_mode = ObstacleAvoidanceMode.SAFE - - # Setup Tactic - field_test_runner.set_tactics( - blue_tactics={id: move_tactic}, yellow_tactics=None - ) - field_test_runner.run_test( - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], - test_timeout_s=5, - ) - - # validate by eye - logger.info(f"robot set to {angle} orientation") - time.sleep(2) + move_tactic = MoveTactic( + destination=tbots_cpp.createPointProto(start_position), + dribbler_mode=DribblerMode.OFF, + final_orientation=tbots_cpp.createAngleProto(target_angle), + ball_collision_type=BallCollisionType.AVOID, + auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), + max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, + obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, + ) - # Send a halt tactic after the test finishes gameplay_test_runner.set_tactics( - blue_tactics={id: HaltTactic()}, yellow_tactics=None + blue_tactics={ + 0: move_tactic, + }, ) - test_thread = threading.Thread(target=execute_test, daemon=True) - test_thread.start() - - # If thunderscope is enabled, show it in the main thread (blocking) - if field_test_runner.thunderscope: - field_test_runner.thunderscope.show() - - test_thread.join() - - -def test_one_robots_square(gameplay_test_runner): - world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - if len(world.friendly_team.team_robots) == 0: - raise Exception("The first world received had no robots in it!") - - print("Here are the robots:") - print( - [ - robot.current_state.global_position - for robot in world.friendly_team.team_robots - ] + gameplay_test_runner.run_test( + setup=setup, + test_timeout_s=5, + eventually_validation_sequence_set=[ + [ + DurationValidation( + duration_s=1, + validation=RobotEventuallyAtOrientation(0, target_angle), + ), + ] + ], ) - id = world.friendly_team.team_robots[0].id - print(f"Running test on robot {id}") - - point1 = Point(x_meters=0.4, y_meters=0.4) - point2 = Point(x_meters=0.4, y_meters=-0.4) - point3 = Point(x_meters=-0.4, y_meters=-0.4) - point4 = Point(x_meters=-0.4, y_meters=0.4) - - tactic_0 = MoveTactic( - destination=point1, - dribbler_mode=DribblerMode.OFF, - final_orientation=Angle(radians=-math.pi / 2), - ball_collision_type=BallCollisionType.AVOID, - auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), - max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, - obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, - ) - tactic_1 = MoveTactic( - destination=point2, - dribbler_mode=DribblerMode.OFF, - final_orientation=Angle(radians=-math.pi / 2), - ball_collision_type=BallCollisionType.AVOID, - auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), - max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, - obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, - ) - tactic_2 = MoveTactic( - destination=point3, - dribbler_mode=DribblerMode.OFF, - final_orientation=Angle(radians=-math.pi / 2), - ball_collision_type=BallCollisionType.AVOID, - auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), - max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, - obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, - ) - tactic_3 = MoveTactic( - destination=point4, - dribbler_mode=DribblerMode.OFF, - final_orientation=Angle(radians=-math.pi / 2), - ball_collision_type=BallCollisionType.AVOID, - auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), - max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, - obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, - ) - tactics = [tactic_0, tactic_1, tactic_2, tactic_3, tactic_0] - - def execute_test(): - # Wait for the user to flip the estop to PLAY - field_test_runner.wait_for_estop_play() - - # Force start the game automatically - field_test_runner.gamecontroller.send_gc_command( - proto.ssl_gc_state_pb2.Command.FORCE_START, - proto.ssl_gc_common_pb2.Team.UNKNOWN, - final_ball_placement_point=None, +@pytest.mark.parametrize( + "start_position, end_position", + [ + ( + tbots_cpp.Point(-1.5, 0.6), + tbots_cpp.Point(-0.3, 0.6), + ), + ( + tbots_cpp.Point(-0.3, 0.6), + tbots_cpp.Point(-0.3, -0.6), + ), + ( + tbots_cpp.Point(-0.3, -0.6), + tbots_cpp.Point(-1.5, -0.6), + ), + ( + tbots_cpp.Point(-1.5, -0.6), + tbots_cpp.Point(-1.5, 0.6), + ), + ], +) +def test_one_robots_square(start_position, end_position, gameplay_test_runner): + def setup(): + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[ + start_position, + ], + yellow_robot_locations=[], + ball_location=tbots_cpp.Point(0, 0), + ball_velocity=tbots_cpp.Vector(0, 0), + ), ) - for tactic in tactics: - print(f"Going to {tactic.destination}") - - field_test_runner.set_tactics( - blue_tactics={ - id: tactic, - }, - yellow_tactics=None, - ) - field_test_runner.run_test( - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], - test_timeout_s=8, - ) - - # Send a halt tactic after the test finishes - field_test_runner.set_tactics( - blue_tactics={id: HaltTactic()}, yellow_tactics=None + tactic = MoveTactic( + destination=tbots_cpp.createPointProto(end_position), + dribbler_mode=DribblerMode.OFF, + final_orientation=Angle(radians=-math.pi / 2), + ball_collision_type=BallCollisionType.AVOID, + auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=0.0), + max_allowed_speed_mode=MaxAllowedSpeedMode.PHYSICAL_LIMIT, + obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, ) - test_thread = threading.Thread(target=execute_test, daemon=True) - test_thread.start() - - # If thunderscope is enabled, show it in the main thread (blocking) - if field_test_runner.thunderscope: - field_test_runner.thunderscope.show() + gameplay_test_runner.set_tactics( + blue_tactics={ + 0: tactic, + }, + ) - test_thread.join() + gameplay_test_runner.run_test( + setup=setup, + test_timeout_s=4, + eventually_validation_sequence_set=[ + [ + RobotEventuallyEntersRegion( + regions=[tbots_cpp.Circle(end_position, 0.05)] + ), + DelayValidation( + delay_s=1, + validation=RobotEventuallyEntersRegion( + regions=[tbots_cpp.Circle(end_position, 0.05)] + ), + ), + ] + ], + ) if __name__ == "__main__": From f98a7926b62b83aef82b9a677e2b4cdaf61c0fed Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 13:17:23 -0700 Subject: [PATCH 20/45] Remove unused imports --- src/software/gameplay_tests/field_test_runner.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index fd3e62c466..4ee466714a 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -4,8 +4,6 @@ from typing import override import pytest -from proto.ssl_gc_common_pb2 import Team -from proto.ssl_gc_state_pb2 import Command from proto.import_all_protos import ValidationProtoSet, WorldState from software.gameplay_tests.tbots_test_runner import TbotsTestRunner From c1fe6c1be1b8ea63b9bb1874f4cb9e40eba4ea92 Mon Sep 17 00:00:00 2001 From: Thunderbots Date: Wed, 17 Jun 2026 13:42:12 -0700 Subject: [PATCH 21/45] WIP: WORKING ON FIELD TEST STUFF KINDA WORKS RN --- .../gameplay_tests/field_test_runner.py | 22 +++++++++------- src/software/gameplay_tests/fixture.py | 1 + .../movement_robot_field_test.py | 25 ++++++++++++------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 4ee466714a..575be7ef8c 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -55,7 +55,7 @@ def __init__( self.is_yellow_friendly = is_yellow_friendly self.robot_communication = robot_communication - self._survey_field_robots() + # self._survey_field_robots() @override def set_world_state(self, world_state: WorldState): @@ -115,18 +115,20 @@ def _wait_for_estop_play(self): logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") while not self.robot_communication.estop_is_playing: # We must process events if Thunderscope is running to keep it responsive - if self.thunderscope: - from pyqtgraph.Qt import QtWidgets - - QtWidgets.QApplication.processEvents() + # if self.thunderscope: + # from pyqtgraph.Qt import QtWidgets + # + # QtWidgets.QApplication.processEvents() time.sleep(0.1) logger.info( "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" ) - def _stop_test(self, delay): + def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): time.sleep(delay) - self.thunderscope.close() + + if self.thunderscope: + self.thunderscope.close() def _runner( self, @@ -136,6 +138,8 @@ def _runner( ): time.sleep(LAUNCH_DELAY_S) + self._wait_for_estop_play() + test_end_time = time.time() + test_timeout_s # Keep track if we started with any eventually validations @@ -191,7 +195,7 @@ def _runner( validation.check_validation(eventually_validation_proto_set) - self._stop_test(delay=PROCESS_BUFFER_DELAY_S) + self._stopper() def _excepthook(self, args): """This function is _critical_ for show_thunderscope to work. @@ -200,7 +204,7 @@ def _excepthook(self, args): :param args: The args passed in from the hook """ - self._stop_test(delay=PAUSE_AFTER_FAIL_DELAY_S) + self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) self.last_exception = args.exc_value raise self.last_exception diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index f04f606681..bd059e2291 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -228,6 +228,7 @@ def field_test_runner(args): ) time.sleep(LAUNCH_DELAY_S) + runner = FieldTestRunner( test_name=test_name, blue_full_system_proto_unix_io=blue_full_system_proto_unix_io, diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index 7036aa3417..03626af01f 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -18,16 +18,19 @@ @pytest.mark.parametrize( "angle", - [0, 45, 90, 180, 270, 0], + [0, 45, 90, 180, 270, 360], ) def test_basic_rotation(angle, gameplay_test_runner): target_angle = tbots_cpp.Angle.fromDegrees(angle) start_position = tbots_cpp.Point(-1.5, 0.6) + robot_id = 2 def setup(): gameplay_test_runner.set_world_state( create_world_state( blue_robot_locations=[ + tbots_cpp.Point(0.0, 0.0), + tbots_cpp.Point(0.0, 1.0), start_position, ], yellow_robot_locations=[], @@ -46,21 +49,25 @@ def setup(): obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, ) - gameplay_test_runner.set_tactics( - blue_tactics={ - 0: move_tactic, - }, - ) + # gameplay_test_runner.set_tactics( + # blue_tactics={ + # robot_id: move_tactic, + # }, + # ) gameplay_test_runner.run_test( setup=setup, - test_timeout_s=5, + # TODO AVAH FIX ACTUAL TIME VS THE OTHER TIME THING + test_timeout_s=50, eventually_validation_sequence_set=[ [ DurationValidation( - duration_s=1, - validation=RobotEventuallyAtOrientation(0, target_angle), + duration_s=20, + validation=RobotEventuallyAtOrientation(robot_id, target_angle), ), + ], + [ + RobotEventuallyAtOrientation(robot_id, target_angle) ] ], ) From 61576ef165b0d948fb09efd99ad235d81d3bbaf6 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 15:39:40 -0700 Subject: [PATCH 22/45] Get rid of run_till_end and tick_duration_s arguments --- .../hl/stp/play/halt_play/halt_play_test.py | 2 +- .../kickoff_enemy/kickoff_enemy_play_test.py | 9 +--- .../kickoff_friendly_play_test.py | 9 +--- .../ai/hl/stp/play/passing_sim_test.py | 2 - .../shoot_or_pass/shoot_or_pass_play_test.py | 1 - .../tactic/attacker/attacker_tactic_test.py | 2 - .../stp/tactic/dribble/dribble_tactic_test.py | 1 - .../tactic/receiver/receiver_tactic_test.py | 1 - .../trajectory/simulated_hrvo_test.py | 26 ++------- .../gameplay_tests/field_test_runner.py | 13 +++-- .../gameplay_tests/simulated_test_runner.py | 53 +++++-------------- .../gameplay_tests/tbots_test_runner.py | 8 +-- 12 files changed, 31 insertions(+), 96 deletions(-) diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index ea8a17b534..c1099e1b2d 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -55,7 +55,7 @@ def setup(): eventually_validation_sequence_set=[ [RobotSpeedEventuallyBelowThreshold(1e-3)] ], - ci_cmd_with_delay=[ + gc_cmd_with_delay=[ (3, Command.Type.HALT, Team.BLUE), (3, Command.Type.HALT, Team.YELLOW), ], diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index 07a23d049b..9f1d7da0bc 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -55,14 +55,6 @@ def setup(): gc_command=Command.Type.KICKOFF, team=Team.YELLOW ) - # Let robots get ready before starting kickoff - threading.Timer( - 4.0, - lambda: gameplay_test_runner.send_gamecontroller_command( - gc_command=Command.Type.NORMAL_START, team=Team.YELLOW - ), - ).start() - gameplay_test_runner.set_plays( blue_play=PlayName.KickoffEnemyPlay, yellow_play=PlayName.KickoffFriendlyPlay, @@ -141,6 +133,7 @@ def setup(): eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, + gc_cmd_with_delay=[(4, Command.Type.NORMAL_START, Team.YELLOW)], ) diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index d60e4ee451..e3ca3723a6 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -60,14 +60,6 @@ def setup(): gc_command=Command.Type.KICKOFF, team=Team.BLUE ) - # Let robots get ready before starting kickoff - threading.Timer( - 4.0, - lambda: gameplay_test_runner.send_gamecontroller_command( - gc_command=Command.Type.NORMAL_START, team=Team.BLUE - ), - ).start() - gameplay_test_runner.set_plays( blue_play=PlayName.KickoffFriendlyPlay, yellow_play=PlayName.KickoffEnemyPlay, @@ -141,6 +133,7 @@ def setup(): eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=60, + gc_cmd_with_delay=[4, Command.Type.NORMAL_START, Team.BLUE], ) diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index 0fa3493722..29a4b74ec8 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -257,7 +257,6 @@ def test_passing_receive_speed( eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, - run_till_end=False, ) @@ -382,7 +381,6 @@ def test_passing_no_backwards_passes( eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, - run_till_end=False, ) diff --git a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py index 1f69af1708..b5558a95a1 100644 --- a/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py +++ b/src/software/ai/hl/stp/play/shoot_or_pass/shoot_or_pass_play_test.py @@ -59,7 +59,6 @@ def setup(): setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=15, - run_till_end=False, ) diff --git a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py index 66cef1d700..405ab78758 100644 --- a/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/attacker/attacker_tactic_test.py @@ -171,7 +171,6 @@ def setup(): gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, - run_till_end=False, test_timeout_s=7, ) @@ -381,7 +380,6 @@ def setup(): gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, - run_till_end=False, test_timeout_s=9, ) diff --git a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py index ecfc71aaec..79c2af67de 100644 --- a/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/dribble/dribble_tactic_test.py @@ -179,7 +179,6 @@ def setup(): setup=setup, eventually_validation_sequence_set=eventually_validations, test_timeout_s=25, - run_till_end=False, ) diff --git a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py index b4e5fd04fd..fc76fd49c3 100644 --- a/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/receiver/receiver_tactic_test.py @@ -235,7 +235,6 @@ def setup(): gameplay_test_runner.run_test( setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, - run_till_end=False, ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index 6c6e541fac..fa0851b53d 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -230,7 +230,7 @@ def hrvo_setup( @pytest.mark.parametrize( "friendly_robot_positions,friendly_robot_destinations,friendly_robots_final_orientations," - + "enemy_robots_positions,enemy_robots_destinations,timeout_s,run_till_end", + + "enemy_robots_positions,enemy_robots_destinations,timeout_s", [ # robot moving straight with no obstacles ( @@ -240,7 +240,6 @@ def hrvo_setup( [], [], 10, - False, ), # robot moving straight with no obstacles while turning from 0 to 180 degrees ( @@ -250,7 +249,6 @@ def hrvo_setup( [], [], 10, - False, ), # robot moving straight with a moving enemy robot behind it ( @@ -260,7 +258,6 @@ def hrvo_setup( [tbots.Point(-2.5, 0)], [tbots.Point(-2.1, 0)], 10, - False, ), # robot moving straight with a moving enemy robot to its side ( @@ -270,7 +267,6 @@ def hrvo_setup( [tbots.Point(-1, -0.8)], [tbots.Point(1, 0)], 10, - False, ), # robot moving straight with a moving enemy robot moving straight towards it ( @@ -280,7 +276,6 @@ def hrvo_setup( [tbots.Point(2.8, 0)], [tbots.Point(2.5, 0)], 10, - False, ), # robot moving with a stationary friendly robot in front of it ( @@ -290,7 +285,6 @@ def hrvo_setup( [], [], 12, - False, ), # robot moving with a stationary enemy robot in front of it ( @@ -300,7 +294,6 @@ def hrvo_setup( [tbots.Point(1, 0)], [], 12, - False, ), # robot moving straight with a moving friendly robot moving straight towards it ( @@ -310,7 +303,6 @@ def hrvo_setup( [], [], 10, - False, ), # robot moving straight with a moving friendly robot moving straight towards it # while both are turning from 0 to 180 degrees @@ -321,7 +313,6 @@ def hrvo_setup( [], [], 10, - False, ), # robot moving with a 3 enemy robot wall in front of it ( @@ -335,7 +326,6 @@ def hrvo_setup( ], [], 10, - False, ), # robot moving with various stationary enemy robots in front of it # walls generated by range(start_x, start_x + step * num_robots, step) @@ -346,7 +336,6 @@ def hrvo_setup( [tbots.Point(float(x_meters) / 10, 0) for x_meters in range(20, 32, 2)], [], 25, - True, ), # robot moving in a local minima (enemy robots in a curve around it) ( @@ -364,7 +353,6 @@ def hrvo_setup( ], [], 25, - False, ), # robot moving in a local minima (enemy robots in a curve around it) with an opening in the middle ( @@ -382,7 +370,6 @@ def hrvo_setup( ], [], 10, - False, ), # robot moving in a zig zag path around enemy robots create_zig_zag_path_test_params(-2, 0.3, 3, 5), @@ -394,7 +381,6 @@ def hrvo_setup( [], [], 15, - False, ), # friendly robots in a circle moving along each diameter # while turning from 0 to 180 degrees @@ -405,7 +391,6 @@ def hrvo_setup( [], [], 20, - False, ), # half enemy half friendly robots in a circle moving along each diameter ( @@ -445,7 +430,6 @@ def hrvo_setup( if index % 2 == 1 ], 10, - False, ), ], ids=[ @@ -476,16 +460,13 @@ def test_robot_movement( enemy_robots_positions: list[tbots.Point], enemy_robots_destinations: list[tbots.Point], timeout_s: int, - run_till_end: bool, ): # Always Validation always_validation_sequence_set = [[RobotsDoNotCollide()]] # Eventually Validation - eventually_validation_sequence_set = ( - [[]] - if run_till_end - else get_reached_destination_validation(friendly_robot_destinations) + eventually_validation_sequence_set = get_reached_destination_validation( + friendly_robot_destinations ) gameplay_test_runner.run_test( @@ -500,7 +481,6 @@ def test_robot_movement( eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=timeout_s, - run_till_end=run_till_end, ) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 575be7ef8c..4f7a82454f 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -71,10 +71,7 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, - # Unused parameters, here for compatibility with sim tests - tick_duration_s=None, - ci_cmd_with_delay=None, - run_till_end=None, + gc_cmd_with_delay=[], ): """Run a test. In a field test this means beginning validation. @@ -84,6 +81,12 @@ def run_test( eventually be true, before the test ends :param test_timeout_s: The timeout for the test, if any eventually_validations remain after the timeout, the test fails. + :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. + [ + (time, command, team), + (time, command, team), + ... + ] """ threading.excepthook = self._excepthook @@ -95,6 +98,7 @@ def run_test( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, + gc_cmd_with_delay, ], ) @@ -135,6 +139,7 @@ def _runner( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, + gc_cmd_with_delay, # TODO (#3744): implement this ): time.sleep(LAUNCH_DELAY_S) diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 1d046eb564..d0c23c5d2a 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -18,6 +18,7 @@ WORLD_BUFFER_TIMEOUT = 0.5 PROCESS_BUFFER_DELAY_S = 0.01 PAUSE_AFTER_FAIL_DELAY_S = 3 +TICK_DURATION_S = 1 / 60 class SimulatedTestRunner(TbotsTestRunner): @@ -68,9 +69,7 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, - tick_duration_s=0.0166, - ci_cmd_with_delay=[], - run_till_end=True, + gc_cmd_with_delay=[], ): """Helper function to run a test, with thunderscope if enabled @@ -78,15 +77,12 @@ def run_test( :param eventually_validation_sequence_set: validation that should eventually be true :param setup: function that sets up the World state and the gamecontroller before running the test :param test_timeout_s: how long the test should run before timing out - :param tick_duration_s: length of a simulation tick - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. + :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. [ (time, command, team), (time, command, team), ... ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out """ threading.excepthook = self._excepthook @@ -103,9 +99,7 @@ def run_test( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, + gc_cmd_with_delay, ], ) run_sim_thread.start() @@ -121,9 +115,7 @@ def run_test( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, - tick_duration_s, - ci_cmd_with_delay=ci_cmd_with_delay, - run_till_end=run_till_end, + gc_cmd_with_delay=gc_cmd_with_delay, ) def _excepthook(self, args): @@ -178,9 +170,7 @@ def _runner( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, - tick_duration_s, - ci_cmd_with_delay, - run_till_end, + gc_cmd_with_delay, ): """Run a test @@ -190,15 +180,12 @@ def _runner( eventually be true, before the test ends :param test_timeout_s: The timeout for the test, if any eventually_validations remain after the timeout, the test fails. - :param tick_duration_s: The simulation step duration - :param ci_cmd_with_delay: A list consisting of tuples with a duration and CI command, e.g. + :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. [ (time, command, team), (time, command, team), ... ] - :param run_till_end: If true, test runs till the end even if eventually validation passes - If false, test stops once eventually validation passes and fails if time out """ time_elapsed_s = 0 @@ -208,18 +195,18 @@ def _runner( # get time before we execute the loop processing_start_time = time.time() - # Check for new CI commands at this time step - for delay, cmd, team in ci_cmd_with_delay: + # Check for new GC commands at this time step + for delay, cmd, team in gc_cmd_with_delay: # If delay matches time if delay <= time_elapsed_s: # send command self.gamecontroller.send_gc_command(gc_command=cmd, team=team) # remove command from the list - ci_cmd_with_delay.remove((delay, cmd, team)) + gc_cmd_with_delay.remove((delay, cmd, team)) - tick = SimulatorTick(milliseconds=tick_duration_s * MILLISECONDS_PER_SECOND) + tick = SimulatorTick(milliseconds=TICK_DURATION_S * MILLISECONDS_PER_SECOND) self.simulator_proto_unix_io.send_proto(SimulatorTick, tick) - time_elapsed_s += tick_duration_s + time_elapsed_s += TICK_DURATION_S while True: try: @@ -258,9 +245,9 @@ def _runner( if ( self.thunderscope and not self.ci_mode - and tick_duration_s > processing_time + and TICK_DURATION_S > processing_time ): - time.sleep(tick_duration_s - processing_time) + time.sleep(TICK_DURATION_S - processing_time) # Validate ( @@ -296,18 +283,6 @@ def _runner( # Check that all always validations are always valid validation.check_validation(always_validation_proto_set) - if not run_till_end: - try: - # Check that all eventually validations are eventually valid - validation.check_validation(eventually_validation_proto_set) - self._stopper() - return - except AssertionError as e: - eventually_validation_failure_msg = str(e) - - if not run_till_end: - raise AssertionError(eventually_validation_failure_msg) - # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 87d30cd5ef..2272183d03 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -132,18 +132,14 @@ def run_test( always_validation_sequence_set=[[]], eventually_validation_sequence_set=[[]], test_timeout_s=3, - tick_duration_s=0.0166, - ci_cmd_with_delay=[], - run_till_end=True, + gc_cmd_with_delay=[], ): """Begins validating a test based on incoming world protos :param always_validation_sequence_set: validation set that must always be true :param eventually_validation_sequence_set: validation set that must eventually be true :param test_timeout_s: how long the test will run - :param tick_duration_s: length of a simulation tick (used in simulated tests) - :param ci_cmd_with_delay: timed CI commands (used in simulated tests) - :param run_till_end: if true, test runs till the end even if eventually validation passes + :param gc_cmd_with_delay: timed GC commands """ raise NotImplementedError("abstract method run_test called from base class") From 168aad30b76cd34e27b4de089421809cdb290e66 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 19:09:34 -0700 Subject: [PATCH 23/45] Fix test params --- .../hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py | 2 +- src/software/ai/navigator/trajectory/simulated_hrvo_test.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index e3ca3723a6..988d76f870 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -133,7 +133,7 @@ def setup(): eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=60, - gc_cmd_with_delay=[4, Command.Type.NORMAL_START, Team.BLUE], + gc_cmd_with_delay=[(4, Command.Type.NORMAL_START, Team.BLUE)], ) diff --git a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py index fa0851b53d..5580206446 100644 --- a/src/software/ai/navigator/trajectory/simulated_hrvo_test.py +++ b/src/software/ai/navigator/trajectory/simulated_hrvo_test.py @@ -135,7 +135,6 @@ def create_zig_zag_path_test_params( ], [], 20, - False, ) From d32d3a1452738c9f4dce82f9aa709ffd62c4d60e Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 19:19:20 -0700 Subject: [PATCH 24/45] Combine some common code from field and sim test runners --- .../gameplay_tests/field_test_runner.py | 24 ++------------- .../movement_robot_field_test.py | 10 +++---- .../gameplay_tests/simulated_test_runner.py | 30 ++----------------- .../gameplay_tests/tbots_test_runner.py | 27 +++++++++++++++++ 4 files changed, 36 insertions(+), 55 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 4f7a82454f..7b0b035720 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -13,8 +13,6 @@ logger = create_logger(__name__) WORLD_BUFFER_TIMEOUT = 5.0 -PROCESS_BUFFER_DELAY_S = 0.01 -PAUSE_AFTER_FAIL_DELAY_S = 3 LAUNCH_DELAY_S = 0.1 @@ -88,8 +86,7 @@ def run_test( ... ] """ - - threading.excepthook = self._excepthook + threading.excepthook = self.excepthook run_test_thread = threading.Thread( target=self._runner, @@ -128,12 +125,6 @@ def _wait_for_estop_play(self): "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" ) - def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): - time.sleep(delay) - - if self.thunderscope: - self.thunderscope.close() - def _runner( self, always_validation_sequence_set, @@ -200,18 +191,7 @@ def _runner( validation.check_validation(eventually_validation_proto_set) - self._stopper() - - def _excepthook(self, args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. - - :param args: The args passed in from the hook - """ - self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception + self.stopper() def _survey_field_robots(self): logger.info("determining robots on field") diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index 03626af01f..a9fcfc1abd 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -49,11 +49,11 @@ def setup(): obstacle_avoidance_mode=ObstacleAvoidanceMode.SAFE, ) - # gameplay_test_runner.set_tactics( - # blue_tactics={ - # robot_id: move_tactic, - # }, - # ) + gameplay_test_runner.set_tactics( + blue_tactics={ + robot_id: move_tactic, + }, + ) gameplay_test_runner.run_test( setup=setup, diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index d0c23c5d2a..a818e897e3 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -16,8 +16,6 @@ LAUNCH_DELAY_S = 0.1 WORLD_BUFFER_TIMEOUT = 0.5 -PROCESS_BUFFER_DELAY_S = 0.01 -PAUSE_AFTER_FAIL_DELAY_S = 3 TICK_DURATION_S = 1 / 60 @@ -84,7 +82,7 @@ def run_test( ... ] """ - threading.excepthook = self._excepthook + threading.excepthook = self.excepthook self._sync_setup(setup) @@ -118,28 +116,6 @@ def run_test( gc_cmd_with_delay=gc_cmd_with_delay, ) - def _excepthook(self, args): - """This function is _critical_ for show_thunderscope to work. - If the test Thread will raises an exception we won't be able to close - the window from the main thread. - - :param args: The args passed in from the hook - """ - self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) - self.last_exception = args.exc_value - raise self.last_exception - - def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): - """Stop running the test - - :param delay: How long to wait before closing everything, defaults - to PROCESS_BUFFER_DELAY_S to minimize buffer warnings - """ - time.sleep(delay) - - if self.thunderscope: - self.thunderscope.close() - def _sync_setup(self, setup): """Run setup until simulator has received game state @@ -189,8 +165,6 @@ def _runner( """ time_elapsed_s = 0 - eventually_validation_failure_msg = "Test Timed Out" - while time_elapsed_s < test_timeout_s: # get time before we execute the loop processing_start_time = time.time() @@ -286,4 +260,4 @@ def _runner( # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) - self._stopper() + self.stopper() diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 2272183d03..56825ac827 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -1,8 +1,13 @@ +import time + from proto.import_all_protos import * from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer from abc import abstractmethod from typing import Any +PAUSE_AFTER_FAIL_DELAY_S = 3 +PROCESS_BUFFER_DELAY_S = 0.01 + class TbotsTestRunner: """An abstract class that represents a test runner""" @@ -143,6 +148,28 @@ def run_test( """ raise NotImplementedError("abstract method run_test called from base class") + def stopper(self, delay=PROCESS_BUFFER_DELAY_S): + """Stop running the test + + :param delay: How long to wait before closing everything, defaults + to PROCESS_BUFFER_DELAY_S to minimize buffer warnings + """ + time.sleep(delay) + + if self.thunderscope: + self.thunderscope.close() + + def excepthook(self, args): + """This function is _critical_ for show_thunderscope to work. + If the test Thread will raises an exception we won't be able to close + the window from the main thread. + + :param args: The args passed in from the hook + """ + self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) + self.last_exception = args.exc_value + raise self.last_exception + def _create_assigned_tactic_params(self, tactics: dict[int, Any]): """Converts dict of tactics to AssignedTacticPlayControlParams message From 33dca21671ef5a187c0e4ba3e6fb76b40df06a51 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 19:26:29 -0700 Subject: [PATCH 25/45] Reimplement run till end but better --- .../gameplay_tests/field_test_runner.py | 27 +++++++++---------- .../gameplay_tests/simulated_test_runner.py | 16 ++++++++--- .../gameplay_tests/tbots_test_runner.py | 6 ++--- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 7b0b035720..09cd3b25ce 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -66,8 +66,8 @@ def set_world_state(self, world_state: WorldState): def run_test( self, setup=lambda: None, - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], + always_validation_sequence_set=[], + eventually_validation_sequence_set=[], test_timeout_s=3, gc_cmd_with_delay=[], ): @@ -138,11 +138,6 @@ def _runner( test_end_time = time.time() + test_timeout_s - # Keep track if we started with any eventually validations - has_eventually_validations = any( - len(seq) > 0 for seq in eventually_validation_sequence_set - ) - while time.time() < test_end_time: while True: try: @@ -180,14 +175,16 @@ def _runner( ValidationProtoSet, always_validation_proto_set ) - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) - - # Break if eventually validation passes - if has_eventually_validations and all( - len(seq) == 0 for seq in eventually_validation_sequence_set - ): - break + if len(always_validation_sequence_set) != 0: + # Check that all always validations are always valid + validation.check_validation(always_validation_proto_set) + else: + # If there are no always validations, check eventually validations to end test early + try: + validation.check_validation(eventually_validation_proto_set) + break + except AssertionError: + pass validation.check_validation(eventually_validation_proto_set) diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index a818e897e3..030c591fdd 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -64,8 +64,8 @@ def set_world_state(self, worldstate: WorldState): def run_test( self, setup=lambda: None, - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], + always_validation_sequence_set=[], + eventually_validation_sequence_set=[], test_timeout_s=3, gc_cmd_with_delay=[], ): @@ -254,8 +254,16 @@ def _runner( ValidationProtoSet, always_validation_proto_set ) - # Check that all always validations are always valid - validation.check_validation(always_validation_proto_set) + if len(always_validation_sequence_set) != 0: + # Check that all always validations are always valid + validation.check_validation(always_validation_proto_set) + else: + # If there are no always validations, check eventually validations to end test early + try: + validation.check_validation(eventually_validation_proto_set) + break + except AssertionError: + pass # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 56825ac827..34bb1ea4e1 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -134,8 +134,8 @@ def set_world_state(self, world_state: WorldState): def run_test( self, setup=lambda: None, - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], + always_validation_sequence_set=[], + eventually_validation_sequence_set=[], test_timeout_s=3, gc_cmd_with_delay=[], ): @@ -166,7 +166,7 @@ def excepthook(self, args): :param args: The args passed in from the hook """ - self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) + self.stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) self.last_exception = args.exc_value raise self.last_exception From 340617803cae2c41ba2f5a4956df3c93b33f37b1 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 20:42:46 -0700 Subject: [PATCH 26/45] Small refactors --- .../gameplay_tests/field_test_runner.py | 19 +++++++++++++--- .../gameplay_tests/simulated_test_runner.py | 22 ++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 09cd3b25ce..8ac0fbd9ca 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -132,13 +132,24 @@ def _runner( test_timeout_s, gc_cmd_with_delay, # TODO (#3744): implement this ): + self._wait_for_estop_play() + time.sleep(LAUNCH_DELAY_S) - self._wait_for_estop_play() + time_elapsed_s = 0 - test_end_time = time.time() + test_timeout_s + while time_elapsed_s < test_timeout_s: + processing_start_time = time.time() + + # Check for new GC commands at this time step + for delay, cmd, team in gc_cmd_with_delay: + # If delay matches time + if delay <= time_elapsed_s: + # send command + self.gamecontroller.send_gc_command(gc_command=cmd, team=team) + # remove command from the list + gc_cmd_with_delay.remove((delay, cmd, team)) - while time.time() < test_end_time: while True: try: world = self.world_buffer.get( @@ -186,6 +197,8 @@ def _runner( except AssertionError: pass + time_elapsed_s += time.time() - processing_start_time + validation.check_validation(eventually_validation_proto_set) self.stopper() diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 030c591fdd..a9fd7a6a8a 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -82,10 +82,10 @@ def run_test( ... ] """ - threading.excepthook = self.excepthook - self._sync_setup(setup) + threading.excepthook = self.excepthook + # If thunderscope is enabled, run the test in a thread and show # thunderscope on this thread. The excepthook is setup to catch # any test failures and propagate them to the main thread @@ -148,25 +148,11 @@ def _runner( test_timeout_s, gc_cmd_with_delay, ): - """Run a test - - :param always_validation_sequence_set: Validation functions that should - hold on every tick - :param eventually_validation_sequence_set: Validation that should - eventually be true, before the test ends - :param test_timeout_s: The timeout for the test, if any eventually_validations - remain after the timeout, the test fails. - :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - """ + time.sleep(LAUNCH_DELAY_S) + time_elapsed_s = 0 while time_elapsed_s < test_timeout_s: - # get time before we execute the loop processing_start_time = time.time() # Check for new GC commands at this time step From a2f379af67a723bc27a2c966b5f77eb6266a8faf Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 20:56:02 -0700 Subject: [PATCH 27/45] Refactor out common code in run_test --- .../gameplay_tests/field_test_runner.py | 84 ++++--------------- .../gameplay_tests/simulated_test_runner.py | 63 +------------- .../gameplay_tests/tbots_test_runner.py | 57 +++++++++++-- 3 files changed, 73 insertions(+), 131 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 8ac0fbd9ca..14d31b56e3 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -1,10 +1,7 @@ import queue -import threading import time from typing import override -import pytest - from proto.import_all_protos import ValidationProtoSet, WorldState from software.gameplay_tests.tbots_test_runner import TbotsTestRunner from software.gameplay_tests.validation import validation @@ -63,68 +60,6 @@ def set_world_state(self, world_state: WorldState): ) @override - def run_test( - self, - setup=lambda: None, - always_validation_sequence_set=[], - eventually_validation_sequence_set=[], - test_timeout_s=3, - gc_cmd_with_delay=[], - ): - """Run a test. In a field test this means beginning validation. - - :param always_validation_sequence_set: Validation functions that should - hold on every tick - :param eventually_validation_sequence_set: Validation that should - eventually be true, before the test ends - :param test_timeout_s: The timeout for the test, if any eventually_validations - remain after the timeout, the test fails. - :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - """ - threading.excepthook = self.excepthook - - run_test_thread = threading.Thread( - target=self._runner, - daemon=True, - args=[ - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - gc_cmd_with_delay, - ], - ) - - run_test_thread.start() - - self.thunderscope.show() - - run_test_thread.join() - - if self.last_exception: - pytest.fail(str(self.last_exception)) - - def _wait_for_estop_play(self): - """Blocks until the estop is in the PLAY state""" - if self.robot_communication.estop_is_playing: - return - - logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") - while not self.robot_communication.estop_is_playing: - # We must process events if Thunderscope is running to keep it responsive - # if self.thunderscope: - # from pyqtgraph.Qt import QtWidgets - # - # QtWidgets.QApplication.processEvents() - time.sleep(0.1) - logger.info( - "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" - ) - def _runner( self, always_validation_sequence_set, @@ -201,7 +136,24 @@ def _runner( validation.check_validation(eventually_validation_proto_set) - self.stopper() + self._stopper() + + def _wait_for_estop_play(self): + """Blocks until the estop is in the PLAY state""" + if self.robot_communication.estop_is_playing: + return + + logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") + while not self.robot_communication.estop_is_playing: + # We must process events if Thunderscope is running to keep it responsive + # if self.thunderscope: + # from pyqtgraph.Qt import QtWidgets + # + # QtWidgets.QApplication.processEvents() + time.sleep(0.1) + logger.info( + "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" + ) def _survey_field_robots(self): logger.info("determining robots on field") diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index a9fd7a6a8a..5c0723d721 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -1,9 +1,7 @@ import queue -import threading import time from typing import override -import pytest from software.py_constants import MILLISECONDS_PER_SECOND from proto.import_all_protos import * @@ -61,66 +59,10 @@ def set_world_state(self, worldstate: WorldState): self.simulator_proto_unix_io.send_proto(WorldState, worldstate) @override - def run_test( - self, - setup=lambda: None, - always_validation_sequence_set=[], - eventually_validation_sequence_set=[], - test_timeout_s=3, - gc_cmd_with_delay=[], - ): - """Helper function to run a test, with thunderscope if enabled - - :param always_validation_sequence_set: validation that should always be true - :param eventually_validation_sequence_set: validation that should eventually be true - :param setup: function that sets up the World state and the gamecontroller before running the test - :param test_timeout_s: how long the test should run before timing out - :param gc_cmd_with_delay: A list consisting of tuples with a duration and GC command, e.g. - [ - (time, command, team), - (time, command, team), - ... - ] - """ - self._sync_setup(setup) - - threading.excepthook = self.excepthook - - # If thunderscope is enabled, run the test in a thread and show - # thunderscope on this thread. The excepthook is setup to catch - # any test failures and propagate them to the main thread - if self.thunderscope: - run_sim_thread = threading.Thread( - target=self._runner, - daemon=True, - args=[ - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - gc_cmd_with_delay, - ], - ) - run_sim_thread.start() - self.thunderscope.show() - run_sim_thread.join() - - if self.last_exception: - pytest.fail(str(self.last_exception)) - - # If thunderscope is disabled, just run the test - else: - self._runner( - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - gc_cmd_with_delay=gc_cmd_with_delay, - ) - - def _sync_setup(self, setup): + def _pre_run_setup(self, setup: (lambda: None)): """Run setup until simulator has received game state :param setup: Function that sets up the world state - :param param: Parameter passed into setup """ world_state_received_buffer = ThreadSafeBuffer(1, WorldStateReceivedTrigger) self.simulator_proto_unix_io.register_observer( @@ -141,6 +83,7 @@ def _sync_setup(self, setup): # Received a response from the simulator break + @override def _runner( self, always_validation_sequence_set, @@ -254,4 +197,4 @@ def _runner( # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) - self.stopper() + self._stopper() diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 34bb1ea4e1..449a3a7c9f 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -1,5 +1,8 @@ +import threading import time +import pytest + from proto.import_all_protos import * from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer from abc import abstractmethod @@ -131,6 +134,27 @@ def set_world_state(self, world_state: WorldState): raise NotImplementedError("abstract class method called set_world_state") @abstractmethod + def _runner( + self, + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + gc_cmd_with_delay, + ): + """Internal test loop; implemented by subclasses. + + See run_test() method for param docs. + """ + raise NotImplementedError("abstract class method called _runner") + + def _pre_run_setup(self, setup: (lambda: None)): + """Hook called before the test loop starts. Override in subclasses that + need to synchronize setup with external systems (e.g. simulator). + + :param setup: Function that sets up the world state + """ + setup() + def run_test( self, setup=lambda: None, @@ -139,16 +163,39 @@ def run_test( test_timeout_s=3, gc_cmd_with_delay=[], ): - """Begins validating a test based on incoming world protos + """Begins validating a test based on incoming world protos. + Runs the test in a background thread if thunderscope is enabled. :param always_validation_sequence_set: validation set that must always be true :param eventually_validation_sequence_set: validation set that must eventually be true :param test_timeout_s: how long the test will run :param gc_cmd_with_delay: timed GC commands """ - raise NotImplementedError("abstract method run_test called from base class") + self._pre_run_setup(setup) + + threading.excepthook = self._excepthook + + args = ( + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + gc_cmd_with_delay, + ) + + if self.thunderscope: + run_test_thread = threading.Thread( + target=self._runner, daemon=True, args=args + ) + run_test_thread.start() + self.thunderscope.show() + run_test_thread.join() + + if self.last_exception: + pytest.fail(str(self.last_exception)) + else: + self._runner(*args) - def stopper(self, delay=PROCESS_BUFFER_DELAY_S): + def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): """Stop running the test :param delay: How long to wait before closing everything, defaults @@ -159,14 +206,14 @@ def stopper(self, delay=PROCESS_BUFFER_DELAY_S): if self.thunderscope: self.thunderscope.close() - def excepthook(self, args): + def _excepthook(self, args): """This function is _critical_ for show_thunderscope to work. If the test Thread will raises an exception we won't be able to close the window from the main thread. :param args: The args passed in from the hook """ - self.stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) + self._stopper(delay=PAUSE_AFTER_FAIL_DELAY_S) self.last_exception = args.exc_value raise self.last_exception From 70df4a1ffecbac92f5c78446b6c4d5252405e98e Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Wed, 17 Jun 2026 21:03:46 -0700 Subject: [PATCH 28/45] Minor moving around code --- .../gameplay_tests/field_test_runner.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 14d31b56e3..b374fc2c36 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -24,7 +24,6 @@ def __init__( yellow_full_system_proto_unix_io, gamecontroller, robot_communication, - publish_validation_protos=True, is_yellow_friendly=False, ): """Initialize the FieldTestRunner @@ -35,7 +34,6 @@ def __init__( :param yellow_full_system_proto_unix_io: The yellow full system proto unix io to use :param gamecontroller: The gamecontroller context managed instance :param robot_communication: The robot communication instance - :param publish_validation_protos: whether to publish validation protos :param: is_yellow_friendly: if yellow is the friendly team """ super(FieldTestRunner, self).__init__( @@ -46,7 +44,6 @@ def __init__( gamecontroller, is_yellow_friendly, ) - self.publish_validation_protos = publish_validation_protos self.is_yellow_friendly = is_yellow_friendly self.robot_communication = robot_communication @@ -54,6 +51,7 @@ def __init__( @override def set_world_state(self, world_state: WorldState): + # TODO (#3369): add visualization for setup instead of just logging warning logger.warning( "set_world_state called in field test: " "assuming robots are initialized according to the given parameters" @@ -65,7 +63,7 @@ def _runner( always_validation_sequence_set, eventually_validation_sequence_set, test_timeout_s, - gc_cmd_with_delay, # TODO (#3744): implement this + gc_cmd_with_delay, ): self._wait_for_estop_play() @@ -108,12 +106,18 @@ def _runner( always_validation_sequence_set, ) - if self.publish_validation_protos: - # Set the test name - eventually_validation_proto_set.test_name = self.test_name - always_validation_proto_set.test_name = self.test_name + # Set the test name + eventually_validation_proto_set.test_name = self.test_name + always_validation_proto_set.test_name = self.test_name - # Send out the validation proto to thunderscope + if self.is_yellow_friendly: + self.yellow_full_system_proto_unix_io.send_proto( + ValidationProtoSet, eventually_validation_proto_set + ) + self.yellow_full_system_proto_unix_io.send_proto( + ValidationProtoSet, always_validation_proto_set + ) + else: self.blue_full_system_proto_unix_io.send_proto( ValidationProtoSet, eventually_validation_proto_set ) @@ -134,6 +138,7 @@ def _runner( time_elapsed_s += time.time() - processing_start_time + # Check that all eventually validations are eventually valid validation.check_validation(eventually_validation_proto_set) self._stopper() From 45e95479cff40576769c22b088f37442575c5cd5 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 09:45:46 -0700 Subject: [PATCH 29/45] Tag TODO for delay and duration validations --- src/software/gameplay_tests/simulated_test_runner.py | 7 +++++-- src/software/gameplay_tests/validation/delay_validation.py | 2 ++ .../gameplay_tests/validation/duration_validation.py | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 5c0723d721..26c851710a 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -2,7 +2,10 @@ import time from typing import override -from software.py_constants import MILLISECONDS_PER_SECOND +from software.py_constants import ( + DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK, + MILLISECONDS_PER_SECOND, +) from proto.import_all_protos import * from software.gameplay_tests.tbots_test_runner import TbotsTestRunner @@ -14,7 +17,7 @@ LAUNCH_DELAY_S = 0.1 WORLD_BUFFER_TIMEOUT = 0.5 -TICK_DURATION_S = 1 / 60 +TICK_DURATION_S = DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK class SimulatedTestRunner(TbotsTestRunner): diff --git a/src/software/gameplay_tests/validation/delay_validation.py b/src/software/gameplay_tests/validation/delay_validation.py index 29f455ba6b..7e0164f233 100644 --- a/src/software/gameplay_tests/validation/delay_validation.py +++ b/src/software/gameplay_tests/validation/delay_validation.py @@ -10,6 +10,8 @@ class DelayValidation(Validation): def __init__(self, delay_s, validation): """A validation wrapper that adds a delay to given validation before being evaluated""" + + # TODO (#3786): rewrite without DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK for field tests self.delay_s = delay_s self.ticks_so_far = 0 self.delay_ticks = int(delay_s / DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK) diff --git a/src/software/gameplay_tests/validation/duration_validation.py b/src/software/gameplay_tests/validation/duration_validation.py index c400493a61..5457967df6 100644 --- a/src/software/gameplay_tests/validation/duration_validation.py +++ b/src/software/gameplay_tests/validation/duration_validation.py @@ -15,6 +15,7 @@ def __init__(self, duration_s, validation): "Type of validation needs to be EVENTUALLY for DurationValidation" ) + # TODO (#3786): rewrite without DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK for field tests self.duration_s = duration_s self.passing_ticks = 0 self.duration_ticks = int( From 1f737c914102d94be53413aa2b0bfe9eae981aa7 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 11:10:39 -0700 Subject: [PATCH 30/45] Fix up the 3 field tests --- .../movement_robot_field_test.py | 16 +-- .../gameplay_tests/passing_field_test.py | 121 ++++++++---------- .../gameplay_tests/pivot_kick_field_test.py | 65 +++++----- 3 files changed, 92 insertions(+), 110 deletions(-) diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/gameplay_tests/movement_robot_field_test.py index a9fcfc1abd..9796815790 100644 --- a/src/software/gameplay_tests/movement_robot_field_test.py +++ b/src/software/gameplay_tests/movement_robot_field_test.py @@ -6,7 +6,6 @@ from proto.import_all_protos import * from proto.message_translation.tbots_protobuf import create_world_state from software.gameplay_tests.util import pytest_main -from software.gameplay_tests.validation.delay_validation import DelayValidation from software.gameplay_tests.validation.duration_validation import DurationValidation from software.gameplay_tests.validation.robot_at_orientation import ( RobotEventuallyAtOrientation, @@ -57,18 +56,14 @@ def setup(): gameplay_test_runner.run_test( setup=setup, - # TODO AVAH FIX ACTUAL TIME VS THE OTHER TIME THING - test_timeout_s=50, + test_timeout_s=5, eventually_validation_sequence_set=[ [ DurationValidation( - duration_s=20, + duration_s=1, validation=RobotEventuallyAtOrientation(robot_id, target_angle), ), ], - [ - RobotEventuallyAtOrientation(robot_id, target_angle) - ] ], ) @@ -128,11 +123,8 @@ def setup(): test_timeout_s=4, eventually_validation_sequence_set=[ [ - RobotEventuallyEntersRegion( - regions=[tbots_cpp.Circle(end_position, 0.05)] - ), - DelayValidation( - delay_s=1, + DurationValidation( + duration_s=1, validation=RobotEventuallyEntersRegion( regions=[tbots_cpp.Circle(end_position, 0.05)] ), diff --git a/src/software/gameplay_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py index dacb54869d..37a5e59938 100644 --- a/src/software/gameplay_tests/passing_field_test.py +++ b/src/software/gameplay_tests/passing_field_test.py @@ -1,77 +1,76 @@ import software.python_bindings as tbots_cpp + from proto.import_all_protos import * -from software.gameplay_tests.fixture import * -from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT +from proto.message_translation.tbots_protobuf import create_world_state +from software.gameplay_tests.util import pytest_main from software.gameplay_tests.validation.friendly_receives_ball_slow import ( FriendlyAlwaysReceivesBallSlow, ) -from software.gameplay_tests.util import pytest_main def test_passing(gameplay_test_runner): - passer_robot_id = 3 - receiver_robot_id = 5 + passer_robot_id = 0 + receiver_robot_id = 1 + should_receive_pass = True + passer_point = tbots_cpp.Point(0, 0) + receiver_point = tbots_cpp.Point(2, 0) - world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - passer_point = tbots_cpp.createPoint(world.ball.current_state.global_position) - receiver_point = None - for robot in world.friendly_team.team_robots: - if robot.id == receiver_robot_id: - receiver_point = tbots_cpp.createPoint(robot.current_state.global_position) - - receive_speed_m_per_s = 2.0 - min_pass_speed_m_per_s = 1.0 - max_pass_speed_m_per_s = 4.0 - - pass_to_test = tbots_cpp.Pass.fromDestReceiveSpeed( - passer_point, - receiver_point, - receive_speed_m_per_s, - min_pass_speed_m_per_s, - max_pass_speed_m_per_s, - ) + def setup(): + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[tbots_cpp.Point(1, 1), receiver_point], + yellow_robot_locations=[], + ball_location=passer_point, + ball_velocity=tbots_cpp.Vector(0, 0), + ) + ) - kick_vec = tbots_cpp.Vector( - pass_to_test.receiverPoint().x() - pass_to_test.passerPoint().x(), - pass_to_test.receiverPoint().y() - pass_to_test.passerPoint().y(), - ) + receive_speed_m_per_s = 2.0 + min_pass_speed_m_per_s = 1.0 + max_pass_speed_m_per_s = 4.0 - # Setup the passer's tactic - # We use KickTactic since AttackerTactic shoots towards the goal instead if open - # KickTactic just does the kick we want - blue_tactics = {} - blue_tactics[passer_robot_id] = KickTactic( - kick_origin=Point( - x_meters=pass_to_test.passerPoint().x(), - y_meters=pass_to_test.passerPoint().y(), - ), - kick_direction=Angle(radians=kick_vec.orientation().toRadians()), - kick_speed_meters_per_second=pass_to_test.speed(), - ) + pass_to_test = tbots_cpp.Pass.fromDestReceiveSpeed( + passer_point, + receiver_point, + receive_speed_m_per_s, + min_pass_speed_m_per_s, + max_pass_speed_m_per_s, + ) - # if we want a friendly robot to receive the pass - if should_receive_pass: - # arguments for a ReceiverTactic - receiver_args = { - "pass": Pass( - passer_point=Point( + kick_vec = pass_to_test.receiverPoint() - pass_to_test.passerPoint() + + # Setup the passer's tactic + # We use KickTactic since AttackerTactic shoots towards the goal instead if open + # KickTactic just does the kick we want + blue_tactics = { + passer_robot_id: KickTactic( + kick_origin=Point( x_meters=pass_to_test.passerPoint().x(), y_meters=pass_to_test.passerPoint().y(), ), - receiver_point=Point( - x_meters=pass_to_test.receiverPoint().x(), - y_meters=pass_to_test.receiverPoint().y(), - ), - pass_speed_m_per_s=pass_to_test.speed(), - ), - "disable_one_touch_shot": True, + kick_direction=Angle(radians=kick_vec.orientation().toRadians()), + kick_speed_meters_per_second=pass_to_test.speed(), + ) } - blue_tactics[receiver_robot_id] = ReceiverTactic(**receiver_args) + # if we want a friendly robot to receive the pass + if should_receive_pass: + # arguments for a ReceiverTactic + receiver_args = { + "pass": Pass( + passer_point=tbots_cpp.createPointProto(pass_to_test.passerPoint()), + receiver_point=tbots_cpp.createPointProto( + pass_to_test.receiverPoint() + ), + pass_speed_m_per_s=pass_to_test.speed(), + ), + "disable_one_touch_shot": True, + } - field = tbots_cpp.Field.createSSLDivisionBField() - tbots_cpp.EighteenZonePitchDivision(field) + blue_tactics[receiver_robot_id] = ReceiverTactic(**receiver_args) + + gameplay_test_runner.set_tactics(blue_tactics=blue_tactics) # Validate that the ball is always received by the other robot # slower than the max receive speed @@ -84,22 +83,12 @@ def test_passing(gameplay_test_runner): ] ] - gameplay_test_runner.set_tactics(blue_tactics=blue_tactics, yellow_tactics=None) gameplay_test_runner.run_test( + setup=setup, always_validation_sequence_set=always_validation_sequence_set, - eventually_validation_sequence_set=[[]], test_timeout_s=5, ) - # Send a halt tactic after the test finishes - gameplay_test_runner.set_tactics( - blue_tactics={ - passer_robot_id: HaltTactic(), - receiver_robot_id: HaltTactic(), - }, - yellow_tactics=None, - ) - if __name__ == "__main__": pytest_main(__file__) diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/gameplay_tests/pivot_kick_field_test.py index 10713be612..7f86290b4f 100644 --- a/src/software/gameplay_tests/pivot_kick_field_test.py +++ b/src/software/gameplay_tests/pivot_kick_field_test.py @@ -1,45 +1,46 @@ -import math -from proto.import_all_protos import * -from software.gameplay_tests.fixture import * -from software.gameplay_tests.gameplay_test_runner import WORLD_BUFFER_TIMEOUT +import software.python_bindings as tbots_cpp -from software.logger.logger import create_logger +from proto.import_all_protos import * +from proto.message_translation.tbots_protobuf import create_world_state from software.gameplay_tests.util import pytest_main - -logger = create_logger(__name__) +from software.gameplay_tests.validation.ball_kicked_in_direction import ( + BallEventuallyKickedInDirection, +) def test_pivot_kick(gameplay_test_runner): - id = 5 - - world = gameplay_test_runner.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - print("Here are the robots:") - print( - [ - robot.current_state.global_position - for robot in world.friendly_team.team_robots - ] - ) - - gameplay_test_runner.set_tactics( - blue_tactics={ - id: PivotKickTactic( - kick_origin=Point(x_meters=-1.13, y_meters=0.75), - kick_direction=Angle(radians=-math.pi / 2), - auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=5.0), + robot_id = 0 + + ball_location = tbots_cpp.Point(-1.13, 0.75) + angle_to_kick_at = tbots_cpp.Angle.threeQuarter() + + def setup(): + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[tbots_cpp.Point(1, 1)], + yellow_robot_locations=[], + ball_location=ball_location, + ball_velocity=tbots_cpp.Vector(0, 0), ) - }, - yellow_tactics=None, - ) + ) + gameplay_test_runner.set_tactics( + blue_tactics={ + robot_id: PivotKickTactic( + kick_origin=tbots_cpp.createPointProto(ball_location), + kick_direction=tbots_cpp.createAngleProto(angle_to_kick_at), + auto_chip_or_kick=AutoChipOrKick(autokick_speed_m_per_s=5.0), + ) + } + ) + gameplay_test_runner.run_test( - always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[[]], + setup=setup, + eventually_validation_sequence_set=[ + [BallEventuallyKickedInDirection(angle_to_kick_at)] + ], test_timeout_s=15, ) - # Send a halt tactic after the test finishes - gameplay_test_runner.set_tactics(blue_tactics={id: HaltTactic()}, yellow_tactics=None) - if __name__ == "__main__": pytest_main(__file__) From 2ee7d3815ec2a595c0dd268eabea4bc45419b1a0 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 12:08:51 -0700 Subject: [PATCH 31/45] Make setup mandatory and fix misuse of test runner --- .../ball_placement_play_test.py | 38 ++---- .../ai/hl/stp/play/passing_sim_test.py | 113 +++++++++++------- .../simulated_test_ball_model.py | 85 ++++--------- .../gameplay_tests/tbots_test_runner.py | 47 ++++---- 4 files changed, 130 insertions(+), 153 deletions(-) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index 787d2ce97c..54fbca86d7 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -154,23 +154,9 @@ def run_ball_placement_scenario( BallEventuallyEntersRegion( regions=[tbots_cpp.Circle(ball_placement_point, 0.15)] ), - ] - ] - - # Drop Ball Always Validation - drop_ball_always_validation_sequence_set = [ - [ - BallAlwaysStaysInRegion( - regions=[tbots_cpp.Circle(ball_placement_point, 0.15)] - ), - ] - ] - - # Drop Ball Eventually Validation - # Non free kick after ball placement, the robot must be 0.5 away from the ball after the placement - # See detailed rules here: https://robocup-ssl.github.io/ssl-rules/sslrules.html#_ball_placement - drop_ball_eventually_validation_sequence_set = [ - [ + # Drop Ball Eventually Validation + # Non free kick after ball placement, the robot must be 0.5 away from the ball after the placement + # See detailed rules here: https://robocup-ssl.github.io/ssl-rules/sslrules.html#_ball_placement RobotEventuallyExitsRegion( regions=[ tbots_cpp.Circle( @@ -181,6 +167,16 @@ def run_ball_placement_scenario( ] ] + # Make sure ball never moves after robot moves away + # TODO (#3503): Enable this with an OrValidation checking robot is moving back + # drop_ball_always_validation_sequence_set = [ + # [ + # BallAlwaysStaysInRegion( + # regions=[tbots_cpp.Circle(ball_placement_point, 0.15)] + # ), + # ] + # ] + gameplay_test_runner.run_test( setup=lambda: ball_placement_play_setup( ball_start_point, @@ -188,18 +184,10 @@ def run_ball_placement_scenario( gameplay_test_runner, blue_only, ), - always_validation_sequence_set=[[]], eventually_validation_sequence_set=placement_eventually_validation_sequence_set, test_timeout_s=30, ) - gameplay_test_runner.run_test( - # setup argument isn't passed to preserve world state from previous test run - always_validation_sequence_set=drop_ball_always_validation_sequence_set, - eventually_validation_sequence_set=drop_ball_eventually_validation_sequence_set, - test_timeout_s=10, - ) - if __name__ == "__main__": pytest_main(__file__) diff --git a/src/software/ai/hl/stp/play/passing_sim_test.py b/src/software/ai/hl/stp/play/passing_sim_test.py index 29a4b74ec8..bf9f9a44b0 100644 --- a/src/software/ai/hl/stp/play/passing_sim_test.py +++ b/src/software/ai/hl/stp/play/passing_sim_test.py @@ -18,42 +18,12 @@ ) -def setup_pass_and_robots( +def calculate_best_pass( ball_initial_position, ball_initial_velocity, - attacker_robot_position, - receiver_robot_positions, - friendly_orientations, - enemy_robot_positions, - receive_pass, - gameplay_test_runner, + blue_robot_locations, + yellow_robot_locations, ): - """Sets up a test involving 1 robot passing the ball - With any number of friendly and enemy robots on the field - Can specify if the first friendly robot should receive the pass or not - :param ball_initial_position: the initial position of the ball - :param ball_initial_velocity: the initial velocity of the ball - :param attacker_robot_position: the position of the robot doing the pass - :param receiver_robot_positions: the positions of the friendly robots - :param friendly_orientations: the orientations of the friendly robots - :param enemy_robot_positions: the positions of the enemy robots - :param receive_pass: whether a friendly robot should try to receive the pass - :param gameplay_test_runner: the test runner - :return: the best pass we generate - """ - blue_robot_locations = [attacker_robot_position, *receiver_robot_positions] - - # Setup the world state - gameplay_test_runner.set_world_state( - create_world_state( - yellow_robot_locations=enemy_robot_positions, - blue_robot_locations=blue_robot_locations, - ball_location=ball_initial_position, - ball_velocity=ball_initial_velocity, - blue_robot_orientations=friendly_orientations, - ), - ) - # construct a world object to match the one sent to the test runner world = tbots_cpp.World( tbots_cpp.Field.createSSLDivisionBField(), @@ -83,7 +53,7 @@ def setup_pass_and_robots( tbots_cpp.Angle(), tbots_cpp.Timestamp(), ) - for index, location in enumerate(enemy_robot_positions) + for index, location in enumerate(yellow_robot_locations) ] ), ) @@ -102,6 +72,51 @@ def setup_pass_and_robots( best_pass_with_score = pass_generator.getBestPass(world, robots_to_ignore) best_pass = best_pass_with_score.pass_value + return best_pass + + +def setup_pass_and_robots( + ball_initial_position, + ball_initial_velocity, + attacker_robot_position, + receiver_robot_positions, + friendly_orientations, + enemy_robot_positions, + receive_pass, + gameplay_test_runner, +): + """Sets up a test involving 1 robot passing the ball + With any number of friendly and enemy robots on the field + Can specify if the first friendly robot should receive the pass or not + :param ball_initial_position: the initial position of the ball + :param ball_initial_velocity: the initial velocity of the ball + :param attacker_robot_position: the position of the robot doing the pass + :param receiver_robot_positions: the positions of the friendly robots + :param friendly_orientations: the orientations of the friendly robots + :param enemy_robot_positions: the positions of the enemy robots + :param receive_pass: whether a friendly robot should try to receive the pass + :param gameplay_test_runner: the test runner + :return: the best pass we generate + """ + blue_robot_locations = [attacker_robot_position, *receiver_robot_positions] + + # Setup the world state + gameplay_test_runner.set_world_state( + create_world_state( + yellow_robot_locations=enemy_robot_positions, + blue_robot_locations=blue_robot_locations, + ball_location=ball_initial_position, + ball_velocity=ball_initial_velocity, + blue_robot_orientations=friendly_orientations, + ), + ) + + best_pass = calculate_best_pass( + ball_initial_position, + ball_initial_velocity, + blue_robot_locations, + enemy_robot_positions, + ) kick_vec = best_pass.receiverPoint() - best_pass.passerPoint() # Setup the passer's tactic @@ -340,18 +355,27 @@ def test_passing_no_backwards_passes( enemy_robot_positions, gameplay_test_runner, ): - field = tbots_cpp.Field.createSSLDivisionBField() - best_pass = setup_pass_and_robots( - ball_initial_position=ball_initial_position, - ball_initial_velocity=ball_initial_velocity, - attacker_robot_position=attacker_robot_position, - receiver_robot_positions=receiver_robot_positions, - friendly_orientations=friendly_orientations, - enemy_robot_positions=enemy_robot_positions, - receive_pass=True, - gameplay_test_runner=gameplay_test_runner, + best_pass = calculate_best_pass( + ball_initial_position, + ball_initial_velocity, + [attacker_robot_position, *receiver_robot_positions], + enemy_robot_positions, ) + def setup(): + setup_pass_and_robots( + ball_initial_position=ball_initial_position, + ball_initial_velocity=ball_initial_velocity, + attacker_robot_position=attacker_robot_position, + receiver_robot_positions=receiver_robot_positions, + friendly_orientations=friendly_orientations, + enemy_robot_positions=enemy_robot_positions, + receive_pass=True, + gameplay_test_runner=gameplay_test_runner, + ) + + field = tbots_cpp.Field.createSSLDivisionBField() + # Eventually Validation eventually_validation_sequence_set = [ [ @@ -378,6 +402,7 @@ def test_passing_no_backwards_passes( ] gameplay_test_runner.run_test( + setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, test_timeout_s=10, diff --git a/src/software/gameplay_tests/simulated_test_ball_model.py b/src/software/gameplay_tests/simulated_test_ball_model.py index 58f171292c..27344d1e2d 100644 --- a/src/software/gameplay_tests/simulated_test_ball_model.py +++ b/src/software/gameplay_tests/simulated_test_ball_model.py @@ -46,29 +46,17 @@ def test_simulator_move_ball( ball_initial_velocity, gameplay_test_runner, ): - # Setup Ball - gameplay_test_runner.simulator_proto_unix_io.send_proto( - WorldState, - create_world_state( - [], - blue_robot_locations=[], - ball_location=ball_initial_position, - ball_velocity=ball_initial_velocity, - ), - ) - - # Setup Tactic - params = AssignedTacticPlayControlParams() - - gameplay_test_runner.blue_full_system_proto_unix_io.send_proto( - AssignedTacticPlayControlParams, params - ) - - # Setup no tactics on the enemy side - params = AssignedTacticPlayControlParams() - gameplay_test_runner.yellow_full_system_proto_unix_io.send_proto( - AssignedTacticPlayControlParams, params - ) + def setup(): + # Setup Ball + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[], + yellow_robot_locations=[], + ball_location=ball_initial_position, + ball_velocity=ball_initial_velocity, + ), + ) + gameplay_test_runner.set_tactics() # expected ball position initial_v = ball_initial_velocity.length() @@ -103,6 +91,7 @@ def test_simulator_move_ball( ] gameplay_test_runner.run_test( + setup=setup, test_timeout_s=8, eventually_validation_sequence_set=eventually_validation_sequence_set, always_validation_sequence_set=always_validation_sequence_set, @@ -114,40 +103,17 @@ def test_ball_robot_collision(gameplay_test_runner): ball_initial_velocity = tbots_cpp.Vector(2.5, 0) robot_position = tbots_cpp.Point(2.5, 0) - # Setup Robot - gameplay_test_runner.simulator_proto_unix_io.send_proto( - WorldState, - create_world_state( - [], - blue_robot_locations=[robot_position], - ball_location=ball_initial_position, - ball_velocity=ball_initial_velocity, - ), - ) - - # Setup Ball - gameplay_test_runner.simulator_proto_unix_io.send_proto( - WorldState, - create_world_state( - [], - blue_robot_locations=[], - ball_location=ball_initial_position, - ball_velocity=ball_initial_velocity, - ), - ) - - # Setup Tactic - params = AssignedTacticPlayControlParams() - - gameplay_test_runner.blue_full_system_proto_unix_io.send_proto( - AssignedTacticPlayControlParams, params - ) - - # Setup no tactics on the enemy side - params = AssignedTacticPlayControlParams() - gameplay_test_runner.yellow_full_system_proto_unix_io.send_proto( - AssignedTacticPlayControlParams, params - ) + def setup(): + # Setup Robot + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[robot_position], + yellow_robot_locations=[], + ball_location=ball_initial_position, + ball_velocity=ball_initial_velocity, + ), + ) + gameplay_test_runner.set_tactics() # expected ball position initial_v = ball_initial_velocity.length() @@ -168,9 +134,6 @@ def test_ball_robot_collision(gameplay_test_runner): distance_from_robot = (ball_expected_position - robot_position).length() - # Always Validation - always_validation_sequence_set = [] - # Eventually Validation eventually_validation_sequence_set = [ [ @@ -181,8 +144,8 @@ def test_ball_robot_collision(gameplay_test_runner): ] gameplay_test_runner.run_test( + setup=setup, eventually_validation_sequence_set=eventually_validation_sequence_set, - always_validation_sequence_set=always_validation_sequence_set, ) diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 449a3a7c9f..33797ee282 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -133,31 +133,9 @@ def set_world_state(self, world_state: WorldState): """ raise NotImplementedError("abstract class method called set_world_state") - @abstractmethod - def _runner( - self, - always_validation_sequence_set, - eventually_validation_sequence_set, - test_timeout_s, - gc_cmd_with_delay, - ): - """Internal test loop; implemented by subclasses. - - See run_test() method for param docs. - """ - raise NotImplementedError("abstract class method called _runner") - - def _pre_run_setup(self, setup: (lambda: None)): - """Hook called before the test loop starts. Override in subclasses that - need to synchronize setup with external systems (e.g. simulator). - - :param setup: Function that sets up the world state - """ - setup() - def run_test( self, - setup=lambda: None, + setup: (lambda: None), always_validation_sequence_set=[], eventually_validation_sequence_set=[], test_timeout_s=3, @@ -166,6 +144,7 @@ def run_test( """Begins validating a test based on incoming world protos. Runs the test in a background thread if thunderscope is enabled. + :param setup: Function that sets up the world state :param always_validation_sequence_set: validation set that must always be true :param eventually_validation_sequence_set: validation set that must eventually be true :param test_timeout_s: how long the test will run @@ -195,6 +174,28 @@ def run_test( else: self._runner(*args) + @abstractmethod + def _runner( + self, + always_validation_sequence_set, + eventually_validation_sequence_set, + test_timeout_s, + gc_cmd_with_delay, + ): + """Internal test loop; implemented by subclasses. + + See run_test() method for param docs. + """ + raise NotImplementedError("abstract class method called _runner") + + def _pre_run_setup(self, setup: (lambda: None)): + """Hook called before the test loop starts. Override in subclasses that + need to synchronize setup with external systems (e.g. simulator). + + :param setup: Function that sets up the world state + """ + setup() + def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): """Stop running the test From 07c3c8f8d4a66867416b2605ebc57f629bdb8b29 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 12:24:47 -0700 Subject: [PATCH 32/45] Move tests away from gameplay_tests folder --- src/software/ai/hl/stp/tactic/kick/BUILD | 12 +++ .../hl/stp/tactic/kick/passing_field_test.py | 94 +++++++++++++++++++ src/software/ai/hl/stp/tactic/move/BUILD | 12 +++ .../tactic/move/move_tactic_field_test.py} | 0 .../ai/hl/stp/tactic/pivot_kick/BUILD | 13 +++ .../pivot_kick_tactic_field_test.py} | 0 src/software/gameplay_tests/BUILD | 50 ---------- src/software/simulation/BUILD | 14 +++ .../simulator_ball_test.py} | 0 9 files changed, 145 insertions(+), 50 deletions(-) create mode 100644 src/software/ai/hl/stp/tactic/kick/passing_field_test.py rename src/software/{gameplay_tests/movement_robot_field_test.py => ai/hl/stp/tactic/move/move_tactic_field_test.py} (100%) rename src/software/{gameplay_tests/pivot_kick_field_test.py => ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_field_test.py} (100%) rename src/software/{gameplay_tests/simulated_test_ball_model.py => simulation/simulator_ball_test.py} (100%) diff --git a/src/software/ai/hl/stp/tactic/kick/BUILD b/src/software/ai/hl/stp/tactic/kick/BUILD index 7d941081cd..a8e8feb39a 100644 --- a/src/software/ai/hl/stp/tactic/kick/BUILD +++ b/src/software/ai/hl/stp/tactic/kick/BUILD @@ -40,3 +40,15 @@ py_test( requirement("pytest"), ], ) + +py_test( + name = "passing_field_test", + srcs = [ + "passing_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) diff --git a/src/software/ai/hl/stp/tactic/kick/passing_field_test.py b/src/software/ai/hl/stp/tactic/kick/passing_field_test.py new file mode 100644 index 0000000000..37a5e59938 --- /dev/null +++ b/src/software/ai/hl/stp/tactic/kick/passing_field_test.py @@ -0,0 +1,94 @@ +import software.python_bindings as tbots_cpp + +from proto.import_all_protos import * +from proto.message_translation.tbots_protobuf import create_world_state +from software.gameplay_tests.util import pytest_main +from software.gameplay_tests.validation.friendly_receives_ball_slow import ( + FriendlyAlwaysReceivesBallSlow, +) + + +def test_passing(gameplay_test_runner): + passer_robot_id = 0 + receiver_robot_id = 1 + + should_receive_pass = True + passer_point = tbots_cpp.Point(0, 0) + receiver_point = tbots_cpp.Point(2, 0) + + def setup(): + gameplay_test_runner.set_world_state( + create_world_state( + blue_robot_locations=[tbots_cpp.Point(1, 1), receiver_point], + yellow_robot_locations=[], + ball_location=passer_point, + ball_velocity=tbots_cpp.Vector(0, 0), + ) + ) + + receive_speed_m_per_s = 2.0 + min_pass_speed_m_per_s = 1.0 + max_pass_speed_m_per_s = 4.0 + + pass_to_test = tbots_cpp.Pass.fromDestReceiveSpeed( + passer_point, + receiver_point, + receive_speed_m_per_s, + min_pass_speed_m_per_s, + max_pass_speed_m_per_s, + ) + + kick_vec = pass_to_test.receiverPoint() - pass_to_test.passerPoint() + + # Setup the passer's tactic + # We use KickTactic since AttackerTactic shoots towards the goal instead if open + # KickTactic just does the kick we want + blue_tactics = { + passer_robot_id: KickTactic( + kick_origin=Point( + x_meters=pass_to_test.passerPoint().x(), + y_meters=pass_to_test.passerPoint().y(), + ), + kick_direction=Angle(radians=kick_vec.orientation().toRadians()), + kick_speed_meters_per_second=pass_to_test.speed(), + ) + } + + # if we want a friendly robot to receive the pass + if should_receive_pass: + # arguments for a ReceiverTactic + receiver_args = { + "pass": Pass( + passer_point=tbots_cpp.createPointProto(pass_to_test.passerPoint()), + receiver_point=tbots_cpp.createPointProto( + pass_to_test.receiverPoint() + ), + pass_speed_m_per_s=pass_to_test.speed(), + ), + "disable_one_touch_shot": True, + } + + blue_tactics[receiver_robot_id] = ReceiverTactic(**receiver_args) + + gameplay_test_runner.set_tactics(blue_tactics=blue_tactics) + + # Validate that the ball is always received by the other robot + # slower than the max receive speed + # and also that the ball is not passed backwards over long distances + always_validation_sequence_set = [ + [ + FriendlyAlwaysReceivesBallSlow( + robot_id=receiver_robot_id, max_receive_speed=2.5 + ) + ] + ] + + gameplay_test_runner.run_test( + setup=setup, + always_validation_sequence_set=always_validation_sequence_set, + test_timeout_s=5, + ) + + +if __name__ == "__main__": + pytest_main(__file__) diff --git a/src/software/ai/hl/stp/tactic/move/BUILD b/src/software/ai/hl/stp/tactic/move/BUILD index ad65d4701e..0b62e74a93 100644 --- a/src/software/ai/hl/stp/tactic/move/BUILD +++ b/src/software/ai/hl/stp/tactic/move/BUILD @@ -39,3 +39,15 @@ py_test( requirement("pytest"), ], ) + +py_test( + name = "move_tactic_field_test", + srcs = [ + "move_tactic_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) diff --git a/src/software/gameplay_tests/movement_robot_field_test.py b/src/software/ai/hl/stp/tactic/move/move_tactic_field_test.py similarity index 100% rename from src/software/gameplay_tests/movement_robot_field_test.py rename to src/software/ai/hl/stp/tactic/move/move_tactic_field_test.py diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD index a248df127b..0b2ca129c2 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD +++ b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD @@ -42,3 +42,16 @@ py_test( requirement("pytest"), ], ) + +py_test( + name = "pivot_kick_tactic_field_test", + srcs = [ + "pivot_kick_tactic_field_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) + diff --git a/src/software/gameplay_tests/pivot_kick_field_test.py b/src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_field_test.py similarity index 100% rename from src/software/gameplay_tests/pivot_kick_field_test.py rename to src/software/ai/hl/stp/tactic/pivot_kick/pivot_kick_tactic_field_test.py diff --git a/src/software/gameplay_tests/BUILD b/src/software/gameplay_tests/BUILD index 701442ce70..e20aa01667 100644 --- a/src/software/gameplay_tests/BUILD +++ b/src/software/gameplay_tests/BUILD @@ -95,53 +95,3 @@ py_library( requirement("pytest"), ], ) - -py_test( - name = "simulated_test_ball_model", - srcs = [ - "simulated_test_ball_model.py", - ], - deps = [ - "//software:conftest", - "//software/gameplay_tests/validation:validations", - requirement("pytest"), - ], -) - -py_test( - name = "movement_robot_field_test", - srcs = [ - "movement_robot_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/gameplay_tests:tbots_test_runner", - "//software/gameplay_tests/validation:validations", - requirement("pytest"), - ], -) - -py_test( - name = "pivot_kick_field_test", - srcs = [ - "pivot_kick_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/gameplay_tests:tbots_test_runner", - "//software/gameplay_tests/validation:validations", - requirement("pytest"), - ], -) - -py_test( - name = "passing_field_test", - srcs = [ - "passing_field_test.py", - ], - deps = [ - "//software:conftest", - "//software/gameplay_tests/validation:validations", - requirement("pytest"), - ], -) diff --git a/src/software/simulation/BUILD b/src/software/simulation/BUILD index 8c2b7a4f92..c0f351547d 100644 --- a/src/software/simulation/BUILD +++ b/src/software/simulation/BUILD @@ -1,3 +1,5 @@ +load("@gameplay_tests_deps//:requirements.bzl", "requirement") + package(default_visibility = ["//visibility:public"]) cc_library( @@ -35,3 +37,15 @@ cc_test( "//software/world", ], ) + +py_test( + name = "simulator_ball_test", + srcs = [ + "simulator_ball_test.py", + ], + deps = [ + "//software:conftest", + "//software/gameplay_tests/validation:validations", + requirement("pytest"), + ], +) diff --git a/src/software/gameplay_tests/simulated_test_ball_model.py b/src/software/simulation/simulator_ball_test.py similarity index 100% rename from src/software/gameplay_tests/simulated_test_ball_model.py rename to src/software/simulation/simulator_ball_test.py From e53e2e6bedbf70152fab93546e078fa3802e03bc Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 12:28:37 -0700 Subject: [PATCH 33/45] Add simulator test to workflow --- .github/workflows/main.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0c7d271178..8efd799e94 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -151,16 +151,15 @@ jobs: - uses: ./.github/actions/environment-setup - name: Simulated Test Run - # TODO (#2908): reenable once field tests can be ran as simulated - # //software/gameplay_tests/... \ run: | cd src bazel test //software/gameplay_tests:requirements_test bazel test --copt=-O3 --flaky_test_attempts=3 --show_timestamps \ - --test_arg="--ci_mode" \ - -- //software:unix_full_system \ + --test_arg="--ci_mode" \ + -- //software:unix_full_system \ -//software/gameplay_tests:requirements_test \ - //software/ai/hl/... \ + //software/simulation/... \ + //software/ai/hl/... \ //software/ai/navigator/... - name: Upload simulated test proto logs From 0cc48823434d684ddc4563c71234dd9b56188080 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Thu, 18 Jun 2026 12:44:16 -0700 Subject: [PATCH 34/45] Formatting --- .../ai/hl/stp/play/ball_placement/ball_placement_play_test.py | 1 - src/software/ai/hl/stp/play/halt_play/halt_play_test.py | 4 +--- .../ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py | 2 -- .../stp/play/kickoff_friendly/kickoff_friendly_play_test.py | 2 -- .../ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py | 4 +--- src/software/ai/hl/stp/tactic/pivot_kick/BUILD | 1 - src/software/gameplay_tests/util.py | 2 -- src/software/gameplay_tests/validation/delay_validation.py | 1 - 8 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py index 54fbca86d7..44c7f51095 100644 --- a/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py +++ b/src/software/ai/hl/stp/play/ball_placement/ball_placement_play_test.py @@ -6,7 +6,6 @@ from proto.ssl_gc_common_pb2 import Team from proto.message_translation.tbots_protobuf import create_world_state from software.gameplay_tests.validation.ball_enters_region import ( - BallAlwaysStaysInRegion, BallEventuallyEntersRegion, ) from software.gameplay_tests.validation.robot_enters_region import ( diff --git a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py index c1099e1b2d..2ae6433d92 100644 --- a/src/software/ai/hl/stp/play/halt_play/halt_play_test.py +++ b/src/software/ai/hl/stp/play/halt_play/halt_play_test.py @@ -52,9 +52,7 @@ def setup(): gameplay_test_runner.run_test( setup=setup, always_validation_sequence_set=[[]], - eventually_validation_sequence_set=[ - [RobotSpeedEventuallyBelowThreshold(1e-3)] - ], + eventually_validation_sequence_set=[[RobotSpeedEventuallyBelowThreshold(1e-3)]], gc_cmd_with_delay=[ (3, Command.Type.HALT, Team.BLUE), (3, Command.Type.HALT, Team.YELLOW), diff --git a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py index 9f1d7da0bc..c357beb96e 100644 --- a/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_enemy/kickoff_enemy_play_test.py @@ -1,5 +1,3 @@ -import threading - import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName diff --git a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py index 988d76f870..e067c94bf2 100644 --- a/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py +++ b/src/software/ai/hl/stp/play/kickoff_friendly/kickoff_friendly_play_test.py @@ -1,5 +1,3 @@ -import threading - import software.python_bindings as tbots_cpp from proto.play_pb2 import PlayName diff --git a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py index 9153aa2de0..d3da86435d 100644 --- a/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py +++ b/src/software/ai/hl/stp/tactic/penalty_kick/penalty_kick_tactic_test.py @@ -49,9 +49,7 @@ @pytest.mark.skip( "Disabling this test because of poor dribbling controls, does not consistently score goal. TODO (#2232)" ) -def test_penalty_kick( - enemy_robot_location, enemy_robot_velocity, gameplay_test_runner -): +def test_penalty_kick(enemy_robot_location, enemy_robot_velocity, gameplay_test_runner): field = tbots_cpp.Field.createSSLDivisionBField() ball_initial_pos = field.friendlyPenaltyMark() diff --git a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD index 0b2ca129c2..5fb40b0997 100644 --- a/src/software/ai/hl/stp/tactic/pivot_kick/BUILD +++ b/src/software/ai/hl/stp/tactic/pivot_kick/BUILD @@ -54,4 +54,3 @@ py_test( requirement("pytest"), ], ) - diff --git a/src/software/gameplay_tests/util.py b/src/software/gameplay_tests/util.py index 89107abede..7603aacf7c 100644 --- a/src/software/gameplay_tests/util.py +++ b/src/software/gameplay_tests/util.py @@ -41,7 +41,6 @@ def get_pytest_path_name(): :return: The truncated base test name. """ - # Truncate the test name to 25 characters for UNIX path length limits return get_pytest_name().split("-")[0][:25] @@ -54,7 +53,6 @@ def load_command_line_arguments(allow_unrecognized: bool = False): :param allow_unrecognized: if true, does not raise an error for unrecognized arguments """ - RUNTIME_DIR = get_runtime_dir() parser = argparse.ArgumentParser( diff --git a/src/software/gameplay_tests/validation/delay_validation.py b/src/software/gameplay_tests/validation/delay_validation.py index 7e0164f233..4e14989fc9 100644 --- a/src/software/gameplay_tests/validation/delay_validation.py +++ b/src/software/gameplay_tests/validation/delay_validation.py @@ -10,7 +10,6 @@ class DelayValidation(Validation): def __init__(self, delay_s, validation): """A validation wrapper that adds a delay to given validation before being evaluated""" - # TODO (#3786): rewrite without DEFAULT_SIMULATOR_TICK_RATE_SECONDS_PER_TICK for field tests self.delay_s = delay_s self.ticks_so_far = 0 From f01552842a3c3d5549288dcc640420a55bf504c9 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sat, 20 Jun 2026 16:40:38 -0700 Subject: [PATCH 35/45] Wait for estop before setup in field test runner --- src/software/gameplay_tests/field_test_runner.py | 13 ++++++++++--- src/software/gameplay_tests/tbots_test_runner.py | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index b374fc2c36..179c7f4c05 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -47,7 +47,7 @@ def __init__( self.is_yellow_friendly = is_yellow_friendly self.robot_communication = robot_communication - # self._survey_field_robots() + self._survey_field_robots() @override def set_world_state(self, world_state: WorldState): @@ -57,6 +57,15 @@ def set_world_state(self, world_state: WorldState): "assuming robots are initialized according to the given parameters" ) + @override + def _pre_run_setup(self, setup: (lambda: None)): + """Wait for estop to be in play state before runing setup + + :param setup: Function that sets up the world state + """ + self._wait_for_estop_play() + setup() + @override def _runner( self, @@ -65,8 +74,6 @@ def _runner( test_timeout_s, gc_cmd_with_delay, ): - self._wait_for_estop_play() - time.sleep(LAUNCH_DELAY_S) time_elapsed_s = 0 diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 33797ee282..74353e5380 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -188,9 +188,9 @@ def _runner( """ raise NotImplementedError("abstract class method called _runner") + @abstractmethod def _pre_run_setup(self, setup: (lambda: None)): - """Hook called before the test loop starts. Override in subclasses that - need to synchronize setup with external systems (e.g. simulator). + """Hook called before the test loop starts :param setup: Function that sets up the world state """ From 85cf90d84dbd574fbd735a7c73d47a541ec03b05 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 14:15:57 -0700 Subject: [PATCH 36/45] Remove old passing field test --- .../gameplay_tests/passing_field_test.py | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 src/software/gameplay_tests/passing_field_test.py diff --git a/src/software/gameplay_tests/passing_field_test.py b/src/software/gameplay_tests/passing_field_test.py deleted file mode 100644 index 37a5e59938..0000000000 --- a/src/software/gameplay_tests/passing_field_test.py +++ /dev/null @@ -1,94 +0,0 @@ -import software.python_bindings as tbots_cpp - -from proto.import_all_protos import * -from proto.message_translation.tbots_protobuf import create_world_state -from software.gameplay_tests.util import pytest_main -from software.gameplay_tests.validation.friendly_receives_ball_slow import ( - FriendlyAlwaysReceivesBallSlow, -) - - -def test_passing(gameplay_test_runner): - passer_robot_id = 0 - receiver_robot_id = 1 - - should_receive_pass = True - passer_point = tbots_cpp.Point(0, 0) - receiver_point = tbots_cpp.Point(2, 0) - - def setup(): - gameplay_test_runner.set_world_state( - create_world_state( - blue_robot_locations=[tbots_cpp.Point(1, 1), receiver_point], - yellow_robot_locations=[], - ball_location=passer_point, - ball_velocity=tbots_cpp.Vector(0, 0), - ) - ) - - receive_speed_m_per_s = 2.0 - min_pass_speed_m_per_s = 1.0 - max_pass_speed_m_per_s = 4.0 - - pass_to_test = tbots_cpp.Pass.fromDestReceiveSpeed( - passer_point, - receiver_point, - receive_speed_m_per_s, - min_pass_speed_m_per_s, - max_pass_speed_m_per_s, - ) - - kick_vec = pass_to_test.receiverPoint() - pass_to_test.passerPoint() - - # Setup the passer's tactic - # We use KickTactic since AttackerTactic shoots towards the goal instead if open - # KickTactic just does the kick we want - blue_tactics = { - passer_robot_id: KickTactic( - kick_origin=Point( - x_meters=pass_to_test.passerPoint().x(), - y_meters=pass_to_test.passerPoint().y(), - ), - kick_direction=Angle(radians=kick_vec.orientation().toRadians()), - kick_speed_meters_per_second=pass_to_test.speed(), - ) - } - - # if we want a friendly robot to receive the pass - if should_receive_pass: - # arguments for a ReceiverTactic - receiver_args = { - "pass": Pass( - passer_point=tbots_cpp.createPointProto(pass_to_test.passerPoint()), - receiver_point=tbots_cpp.createPointProto( - pass_to_test.receiverPoint() - ), - pass_speed_m_per_s=pass_to_test.speed(), - ), - "disable_one_touch_shot": True, - } - - blue_tactics[receiver_robot_id] = ReceiverTactic(**receiver_args) - - gameplay_test_runner.set_tactics(blue_tactics=blue_tactics) - - # Validate that the ball is always received by the other robot - # slower than the max receive speed - # and also that the ball is not passed backwards over long distances - always_validation_sequence_set = [ - [ - FriendlyAlwaysReceivesBallSlow( - robot_id=receiver_robot_id, max_receive_speed=2.5 - ) - ] - ] - - gameplay_test_runner.run_test( - setup=setup, - always_validation_sequence_set=always_validation_sequence_set, - test_timeout_s=5, - ) - - -if __name__ == "__main__": - pytest_main(__file__) From f4b64d5d2cf623c4b67a4eb3a40ee50e3bb57455 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 21:23:57 +0000 Subject: [PATCH 37/45] [pre-commit.ci lite] apply automatic fixes --- src/software/gameplay_tests/field_test_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 179c7f4c05..c1dcd04171 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -59,7 +59,7 @@ def set_world_state(self, world_state: WorldState): @override def _pre_run_setup(self, setup: (lambda: None)): - """Wait for estop to be in play state before runing setup + """Wait for estop to be in play state before running setup :param setup: Function that sets up the world state """ From 2485b577b6a9e9e4dc85ad1687908aa10a3fb06f Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 14:26:33 -0700 Subject: [PATCH 38/45] Stop test when field test runner can't receive worlds --- .../gameplay_tests/field_test_runner.py | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index c1dcd04171..64818e3d61 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -90,18 +90,16 @@ def _runner( # remove command from the list gc_cmd_with_delay.remove((delay, cmd, team)) - while True: - try: - world = self.world_buffer.get( - block=True, timeout=WORLD_BUFFER_TIMEOUT - ) - break - except queue.Empty: - # If we timeout, that means full_system missed the last - # wrapper and robot status, lets resend it. - logger.warning( - f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." - ) + # Try to get the world; if can't receive for timeout, + # something went wrong with either FullSystem or SSL Vision + try: + world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) + break + except queue.Empty: + logger.warning( + f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." + ) + self._stopper() # Validate ( From e42ab95cb9797937d14ed0c05a7e9b80f2208a88 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 14:59:19 -0700 Subject: [PATCH 39/45] Minor fix --- src/software/gameplay_tests/field_test_runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 64818e3d61..55c863455b 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -94,12 +94,11 @@ def _runner( # something went wrong with either FullSystem or SSL Vision try: world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) - break except queue.Empty: logger.warning( f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." ) - self._stopper() + break # Validate ( From db8735df582ecdf639a004d28f2aae29259a7560 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 15:02:43 -0700 Subject: [PATCH 40/45] Remove duplicate proto unix io register observer, already in tbots test runner --- src/software/gameplay_tests/fixture.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index bd059e2291..2c6c06e00b 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -239,6 +239,4 @@ def field_test_runner(args): is_yellow_friendly=args.run_yellow, ) - friendly_proto_unix_io.register_observer(World, runner.world_buffer) - yield runner From d5036e80dc5b2495628730505e03641e1c1dd4ad Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 15:06:40 -0700 Subject: [PATCH 41/45] Check for ending early when always validations is [[]] --- src/software/gameplay_tests/field_test_runner.py | 2 +- src/software/gameplay_tests/simulated_test_runner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 55c863455b..2200ccc0e7 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -129,7 +129,7 @@ def _runner( ValidationProtoSet, always_validation_proto_set ) - if len(always_validation_sequence_set) != 0: + if any(len(seq) > 0 for seq in always_validation_sequence_set): # Check that all always validations are always valid validation.check_validation(always_validation_proto_set) else: diff --git a/src/software/gameplay_tests/simulated_test_runner.py b/src/software/gameplay_tests/simulated_test_runner.py index 26c851710a..a638ad547e 100644 --- a/src/software/gameplay_tests/simulated_test_runner.py +++ b/src/software/gameplay_tests/simulated_test_runner.py @@ -186,7 +186,7 @@ def _runner( ValidationProtoSet, always_validation_proto_set ) - if len(always_validation_sequence_set) != 0: + if any(len(seq) > 0 for seq in always_validation_sequence_set): # Check that all always validations are always valid validation.check_validation(always_validation_proto_set) else: From 73a81965cf9a5c46aed6ae31512947cb042bf4a2 Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 15:07:20 -0700 Subject: [PATCH 42/45] Make method abstract --- src/software/gameplay_tests/tbots_test_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/software/gameplay_tests/tbots_test_runner.py b/src/software/gameplay_tests/tbots_test_runner.py index 74353e5380..9e841b9173 100644 --- a/src/software/gameplay_tests/tbots_test_runner.py +++ b/src/software/gameplay_tests/tbots_test_runner.py @@ -194,7 +194,7 @@ def _pre_run_setup(self, setup: (lambda: None)): :param setup: Function that sets up the world state """ - setup() + raise NotImplementedError("abstract class method called _pre_run_setup") def _stopper(self, delay=PROCESS_BUFFER_DELAY_S): """Stop running the test From 5d8266196feab39118ab1b639798d3a1eb4e846e Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Sun, 21 Jun 2026 15:07:57 -0700 Subject: [PATCH 43/45] Delete dead code --- src/software/gameplay_tests/field_test_runner.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 2200ccc0e7..5511ef8ac3 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -154,11 +154,6 @@ def _wait_for_estop_play(self): logger.info("\x1b[33m" + "Waiting for Estop to be in PLAY state..." + "\x1b[0m") while not self.robot_communication.estop_is_playing: - # We must process events if Thunderscope is running to keep it responsive - # if self.thunderscope: - # from pyqtgraph.Qt import QtWidgets - # - # QtWidgets.QApplication.processEvents() time.sleep(0.1) logger.info( "\x1b[32m" + "Estop is in PLAY state. Proceeding with test." + "\x1b[0m" From fd213fee830f7b7a7937ed5d0fbeb0104ab216de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 22:15:49 +0000 Subject: [PATCH 44/45] [pre-commit.ci lite] apply automatic fixes --- src/software/gameplay_tests/fixture.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/software/gameplay_tests/fixture.py b/src/software/gameplay_tests/fixture.py index 2c6c06e00b..22d660ddd8 100644 --- a/src/software/gameplay_tests/fixture.py +++ b/src/software/gameplay_tests/fixture.py @@ -7,7 +7,6 @@ getRobotMulticastChannel, ) -from proto.import_all_protos import World from software.gameplay_tests.field_test_runner import FieldTestRunner from software.gameplay_tests.simulated_test_runner import SimulatedTestRunner from software.gameplay_tests.util import ( From f458160490fedccd5b80173235e8ba20f63a485b Mon Sep 17 00:00:00 2001 From: Eric Xiao Date: Mon, 22 Jun 2026 12:27:44 -0700 Subject: [PATCH 45/45] Raise exception to quit instead of breaking --- src/software/gameplay_tests/field_test_runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/software/gameplay_tests/field_test_runner.py b/src/software/gameplay_tests/field_test_runner.py index 5511ef8ac3..7bd542b75c 100644 --- a/src/software/gameplay_tests/field_test_runner.py +++ b/src/software/gameplay_tests/field_test_runner.py @@ -95,10 +95,9 @@ def _runner( try: world = self.world_buffer.get(block=True, timeout=WORLD_BUFFER_TIMEOUT) except queue.Empty: - logger.warning( + raise Exception( f"No World was received for {WORLD_BUFFER_TIMEOUT} seconds. Ending test early." ) - break # Validate (