diff --git a/planetwars-matchrunner/bots/crash_bot.py b/planetwars-matchrunner/bots/crash_bot.py new file mode 100644 index 0000000..2a505b2 --- /dev/null +++ b/planetwars-matchrunner/bots/crash_bot.py @@ -0,0 +1 @@ +raise RuntimeError("This bot does not run!") \ No newline at end of file diff --git a/planetwars-matchrunner/src/docker_runner.rs b/planetwars-matchrunner/src/docker_runner.rs index 939d734..a4415ca 100644 --- a/planetwars-matchrunner/src/docker_runner.rs +++ b/planetwars-matchrunner/src/docker_runner.rs @@ -207,8 +207,9 @@ impl DockerBotRunner { let result = timeout(request.timeout, resp_fut).await; let request_response = match result { Ok(Ok(response)) => Ok(response.to_vec()), - // this one happens when a bot output stream ends, map this to Timeout for now - Ok(Err(_read_error)) => Err(RequestError::Timeout), + // Read failed. + // TODO: better logging for errors + Ok(Err(_read_error)) => Err(RequestError::BotTerminated), Err(_elapsed) => Err(RequestError::Timeout), }; let request_id = (self.player_id, request.request_id); diff --git a/planetwars-matchrunner/src/match_context.rs b/planetwars-matchrunner/src/match_context.rs index bdc87a3..6e6a088 100644 --- a/planetwars-matchrunner/src/match_context.rs +++ b/planetwars-matchrunner/src/match_context.rs @@ -163,6 +163,7 @@ impl Future for Request { #[derive(Debug, Clone, PartialEq, Eq)] pub enum RequestError { Timeout, + BotTerminated, } pub type RequestResult = Result; diff --git a/planetwars-matchrunner/tests/test_matchrunner.rs b/planetwars-matchrunner/tests/test_matchrunner.rs index 131a7b8..caa78c0 100644 --- a/planetwars-matchrunner/tests/test_matchrunner.rs +++ b/planetwars-matchrunner/tests/test_matchrunner.rs @@ -56,23 +56,20 @@ async fn match_does_run() { tokio::time::sleep(Duration::from_secs(1)).await } -async fn match_ctx_from_bot_spec(bot_spec: B) -> MatchCtx { - let event_bus = Arc::new(Mutex::new(EventBus::new())); - let (logger, _rx) = mpsc::unbounded_channel(); - - let player_handle = bot_spec.run_bot(1, event_bus.clone(), logger.clone()).await; - let mut players = HashMap::new(); - players.insert(1, player_handle); - MatchCtx::new(event_bus, players, logger) -} - /// creates a simple match ctx which only holds a single bot async fn with_bot_match_ctx(bot_spec: B, func: F) where F: FnOnce(&mut MatchCtx) -> Pin>>, B: BotSpec, { - let mut ctx = match_ctx_from_bot_spec(bot_spec).await; + let event_bus = Arc::new(Mutex::new(EventBus::new())); + let (logger, _rx) = mpsc::unbounded_channel(); + + let player_handle = bot_spec.run_bot(1, event_bus.clone(), logger.clone()).await; + let mut players = HashMap::new(); + players.insert(1, player_handle); + let mut ctx = MatchCtx::new(event_bus, players, logger); + func(&mut ctx).await; ctx.shutdown().await; } @@ -108,3 +105,19 @@ async fn docker_runner_timeout() { }) .await; } + +#[tokio::test] +async fn docker_runner_crash() { + let bot_spec = simple_python_docker_bot_spec("./bots", "crash_bot.py"); + with_bot_match_ctx(bot_spec, |ctx| { + async move { + let resp = ctx + .request(1, b"sup".to_vec(), Duration::from_millis(200)) + .await; + + assert_eq!(resp, Err(RequestError::BotTerminated)); + } + .boxed() + }) + .await; +}