///- --package .
world_executor (test)
test world_executor: prelude
///- --test static --symbol
:< testing
:< sm'_operators
:== executor_programs =
{
world : world_port::world_programs
database : string
database_args : list string
client : string
client_args : list string
}
:== executor_files = { run_log : string; block_log : string; experience_prefix : string }
:== executor_limits = { run_ms : i32; start_ms : i32 }
:== executor =
{
programs : executor_programs
files : executor_files
limits : executor_limits
character : character::character
}
:== command = arena_host::host_command
:== event = arena_host::host_event
:== maybe_world = option world_port::world
:== stepped = maybe_world * list event
:= trimmed_first_line (text : string) : string = sm'::trim (sm'::first_line text)
:= rest_from (at : i32) (text : string) : string = sm'::substring at (sm'::length text - at) text
:= line_after (prefix : string) (text : string) : string =
:= at : i32 = sm'::index_of prefix text
? at < 0i32 ?> "" |: trimmed_first_line (rest_from (at + sm'::length prefix) text)
:~ ^ awaited_start (w : world_port::world) (left_ms : i32) : event =
?= world_port::world_event_within w 50i32 ?|
| Some e => e
| None => ? left_ms <= 0i32 ?> arena_host::WorldCrashed |: awaited_start w (left_ms - 50i32)
:= obeyed (x : executor) (current : maybe_world) (c : command) : maybe_world =
world_port::world_obeyed x::programs::world current c
:= started_world (x : executor) (current : maybe_world) (c : command) : stepped =
:= next = obeyed x current c
?= next ?|
| Some w => next, [ awaited_start w x::limits::start_ms ]
| None => next, [ arena_host::WorldCrashed ]
:= reset_events (x : executor) : list event =
:= args = listm::rev (character::reset_sql x::character :: listm::rev x::programs::database_args)
? process::capture_status_within x::programs::database args 30000i32 = 0i32 ?> [] |: [ arena_host::WorldCrashed ]
:= logged_experience (x : executor) : string =
line_after x::files::experience_prefix (file_system::read_all_text x::files::run_log)
:= run_ended (x : executor) (code : i32) : list event =
? code = -1i32 ?> [ arena_host::RunTimedOut ]
|? code = 0i32 ?> [ arena_host::RunEnded (block_plan::outcome_of_experience (logged_experience x)) ]
|: [ arena_host::RunEnded (:< block_plan ;; Crashed) ]
:= run_events (x : executor) : list event =
file_system::write_all_text x::files::run_log ""
run_ended x (process::capture_status_within x::programs::client x::programs::client_args x::limits::run_ms)
:= recorded (x : executor) (fate : block_plan::run_fate) : list event =
file_system::append_all_text x::files::block_log (arena_host::fate_text fate ++# "\n")
[]
:= run_command_events (x : executor) (c : command) : list event =
:< arena_host
?= c ?|
| ResetCharacter => reset_events x
| StartRun _ => run_events x
| Record fate => recorded x fate
| _ => []
:= executed (x : executor) (current : maybe_world) (c : command) : stepped =
:< arena_host
?= c ?|
| StartWorld | RestartWorld => started_world x current c
| ReleaseWindow => obeyed x current c, []
| _ => current, run_command_events x c
:= prepended (events : list event) (produced : list event) : list event =
listm::fold (\ acc e => e :: acc) events produced
:= executed_all (x : executor) (current : maybe_world) (commands : list command) : stepped =
:= step (now, events) c =
:= next, produced = executed x now c
next, prepended events produced
listm::fold step (current, []) commands
:== drive = { host : arena_host::host; current : maybe_world; log : string }
:= enqueued (rest : list event) (produced : list event) : list event =
listm::fold (\ queue y => y :: queue) (listm::rev produced) (listm::rev rest)
:= advanced (x : executor) (d : drive) (e : event) : drive * list event =
:= next, commands = arena_host::step d::host e
:= after, out = executed_all x d::current commands
{ host = next; current = after; log = arena_host::logged d::log commands }, out
:= finished (d : drive) : string =
world_port::stopped_if_running d::current
d::log
:~ ^ driven (x : executor) (d : drive) (pending : list event) : string =
?= pending ?|
| [] => finished d
| e :: rest =>
:= moved, produced = advanced x d e
driven x moved (enqueued rest produced)
:= fake_database_args () : list string = [ "-c"; "import sys; sys.exit(0)"; "--" ]
:= fake_world () : world_port::world_programs = world_port::fake_server ">> server online!"
:= fake_programs (run_script : string) : executor_programs =
{
world = fake_world ()
database = "python"
database_args = fake_database_args ()
client = "python"
client_args = [ "-c"; run_script ]
}
:= fake_log (tag : string) (kind : string) : string =
file_system::get_temp_path () ++# "/tibia-world-executor-" ++# tag ++# "-" ++# kind ++# ".log"
:= fake_files (tag : string) : executor_files =
{ run_log = fake_log tag "run"; block_log = fake_log tag "block"; experience_prefix = "experience " }
:= fake_character () : character::character = { name = "Knight"; level = 8; axe_skill = 10; x = 100; y = 100 }
:= fake_executor (tag : string) (run_script : string) : executor =
{
programs = fake_programs run_script
files = fake_files tag
limits = { run_ms = 3000i32; start_ms = 20000i32 }
character = fake_character ()
}
:= block_run (first : i32) (x : executor) : string =
:= h = arena_host::opened (block_plan::stages_of first 1)
driven x { host = h; current = None; log = "" } [ arena_host::WindowGranted ]
:= counted_run_script () : string = "open(r'" ++# fake_log "counted" "run" ++# "', 'w').write('experience 120\\n')"
:= counted_block (first : i32) : string * string =
:= x = fake_executor "counted" (counted_run_script ())
file_system::write_all_text x::files::block_log ""
:= trace = block_run first x
trace, file_system::read_all_text x::files::block_log
:= three_counted_trace () : string =
"start-world | reset, run 0.0 | counted, reset, run 0.1 | counted, reset, run 1.0 | counted, release"
:= sleeping_executor () : executor =
:= x = fake_executor "sleeping" "import time; time.sleep(30)"
{ x ?| limits = { (x::limits) ?| run_ms = 500i32 } }
:= stop_rerun_trace () : string =
"start-world | reset, run 0.0 | stop, rerun, reset, run 0.0 | stop, missing, reset, run 1.0 | "
^^ "stop, rerun, reset, run 1.0 | stop, missing, release"
a_block_runs_through_real_programs_and_matches_the_simulated_trace (test)
test a_block_runs_through_real_programs_and_matches_the_simulated_trace: passed
///- --test --symbol
///> gleam
:= ~first = 2i32
counted_block first |> _assert_eq (three_counted_trace (), "counted\ncounted\ncounted\n")
__assert_eq { expected = (start-world | reset, run 0.0 | counted, reset, run 0.1 | counted, reset, run 1.0 | counted, release, counted
counted
counted
) }
a_run_past_its_deadline_is_stopped_and_rerun (test)
test a_run_past_its_deadline_is_stopped_and_rerun: passed
///- --test --symbol
///> gleam
:= ~first = 1i32
block_run first (sleeping_executor ()) |> _assert_eq (stop_rerun_trace ())
__assert_eq { expected = start-world | reset, run 0.0 | stop, rerun, reset, run 0.0 | stop, missing, reset, run 1.0 | stop, rerun, reset, run 1.0 | stop, missing, release }