Skip to content
31 changes: 31 additions & 0 deletions src/cli/cli_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,21 @@ class DebugBinary(str, Enum):
EnableThunderscopeOption = Annotated[bool, Option("-t", "--enable_thunderscope")]
StopAIOnStartOption = Annotated[bool, Option("-s", "--stop_ai_on_start")]

TestModeOption = Annotated[
bool,
Option(
"--test_mode",
help="Launch Thunderscope with a widget to select and run gameplay tests",
),
]
RunFieldTestOption = Annotated[
bool,
Option(
"--run_field_test",
help="In test mode, run field tests instead of simulated tests",
),
]

JobsOption = Annotated[str, Option("-j", "--jobs")]
RunsOption = Annotated[
int,
Expand Down Expand Up @@ -166,6 +181,22 @@ class DebugBinary(str, Enum):
title="Diagnostics",
description="Run Thunderscope in diagnostics mode against real robots",
),
questionary.Choice(
title="Tests",
description="Launch with a widget to select and run gameplay tests",
),
]

# Thunderscope test mode "Test type?" menu.
TEST_TYPE_CHOICES = [
questionary.Choice(
title="Simulated tests",
description="Run gameplay tests against the simulator",
),
questionary.Choice(
title="Field tests",
description="Run gameplay tests against real robots (requires an interface)",
),
]

# Thunderscope simulator "Options:" checkbox.
Expand Down
14 changes: 4 additions & 10 deletions src/software/er_force_simulator_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,6 @@ int main(int argc, char** argv)
ThreadedProtoUnixSender<TbotsProto::WorldStateReceivedTrigger>(
runtime_dir + WORLD_STATE_RECEIVED_TRIGGER_PATH);

bool has_sent_world_state_trigger = false;

// Inputs
// World State Input: Configures the ERForceSimulator
auto world_state_input = ThreadedProtoUnixListener<TbotsProto::WorldState>(
Expand All @@ -144,14 +142,10 @@ int main(int argc, char** argv)
std::scoped_lock lock(simulator_mutex);
er_force_sim->setWorldState(input);

if (!has_sent_world_state_trigger)
{
auto world_state_received_trigger_msg =
*createWorldStateReceivedTrigger();
world_state_received_trigger.sendProto(
world_state_received_trigger_msg);
has_sent_world_state_trigger = true;
}
auto world_state_received_trigger_msg =
*createWorldStateReceivedTrigger();
world_state_received_trigger.sendProto(
world_state_received_trigger_msg);
});

// World Input: Buffer vision until we have primitives to tick
Expand Down
18 changes: 18 additions & 0 deletions src/software/gameplay_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,21 @@ py_library(
requirement("pytest"),
],
)

py_library(
name = "test_mode",
srcs = [
"test_mode.py",
],
deps = [
":fixture",
":util",
"//proto:import_all_protos",
"//software/logger:py_logger",
"//software/thunderscope",
"//software/thunderscope:config",
"//software/thunderscope:test_runner_widget",
"//software/thunderscope:thunderscope_types",
requirement("pytest"),
],
)
6 changes: 5 additions & 1 deletion src/software/gameplay_tests/field_test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(
gamecontroller,
robot_communication,
is_yellow_friendly=False,
owns_thunderscope=True,
):
"""Initialize the FieldTestRunner

Expand All @@ -35,6 +36,8 @@ def __init__(
:param gamecontroller: The gamecontroller context managed instance
:param robot_communication: The robot communication instance
:param: is_yellow_friendly: if yellow is the friendly team
:param owns_thunderscope: Whether this runner controls the Thunderscope
lifecycle (False when binding to an open Thunderscope in test mode)
"""
super(FieldTestRunner, self).__init__(
test_name,
Expand All @@ -43,6 +46,7 @@ def __init__(
yellow_full_system_proto_unix_io,
gamecontroller,
is_yellow_friendly,
owns_thunderscope=owns_thunderscope,
)
self.is_yellow_friendly = is_yellow_friendly
self.robot_communication = robot_communication
Expand Down Expand Up @@ -78,7 +82,7 @@ def _runner(

time_elapsed_s = 0

while time_elapsed_s < test_timeout_s:
while time_elapsed_s < test_timeout_s and not self._is_cancelled():
processing_start_time = time.time()

# Check for new GC commands at this time step
Expand Down
Loading
Loading