End-to-end test harness for the SpaceOS pipeline
1(*---------------------------------------------------------------------------
2 Copyright (c) 2025 Thomas Gazagnaire. All rights reserved.
3 SPDX-License-Identifier: ISC
4 ---------------------------------------------------------------------------*)
5
6(** Unit tests for {!E2e} — pure logic only, no VMs. *)
7
8(* ---------------------------------------------------------------------------
9 Helpers
10 --------------------------------------------------------------------------- *)
11
12let result_testable =
13 Alcotest.testable E2e.pp_result (fun a b ->
14 match (a, b) with
15 | E2e.Pass a, E2e.Pass b -> String.equal a b
16 | Fail a, Fail b -> String.equal a b
17 | _ -> false)
18
19let pass msg = E2e.Pass msg
20let fail msg = E2e.Fail msg
21let contains pat s = Re.execp (Re.Pcre.re pat |> Re.compile) s
22
23(* ---------------------------------------------------------------------------
24 Tests
25 --------------------------------------------------------------------------- *)
26
27let test_default_config () =
28 let c = E2e.default_config in
29 Alcotest.(check int) "min_frames" 10 c.min_frames;
30 Alcotest.(check (float 0.1)) "timeout" 60. c.timeout;
31 Alcotest.(check (option string)) "build_file" None c.build_file;
32 Alcotest.(check (option string)) "run_file" None c.run_file;
33 Alcotest.(check (option string)) "kernel" None c.kernel;
34 Alcotest.(check (option string)) "output" None c.output
35
36let test_is_pass () =
37 Alcotest.(check bool) "pass is pass" true (E2e.is_pass (pass "ok"));
38 Alcotest.(check bool) "fail is not pass" false (E2e.is_pass (fail "bad"))
39
40let test_exit_code_all_pass () =
41 let results = [ pass "a"; pass "b"; pass "c" ] in
42 Alcotest.(check int) "all pass" 0 (E2e.exit_code results)
43
44let test_exit_code_one_fail () =
45 let results = [ pass "a"; fail "b"; pass "c" ] in
46 Alcotest.(check int) "one fail" 1 (E2e.exit_code results)
47
48let test_exit_code_empty () = Alcotest.(check int) "empty" 0 (E2e.exit_code [])
49
50let test_check_frames_pass () =
51 let r = E2e.check_frames ~received:15 ~target:10 in
52 Alcotest.(check bool) "enough frames" true (E2e.is_pass r)
53
54let test_check_frames_exact () =
55 let r = E2e.check_frames ~received:10 ~target:10 in
56 Alcotest.(check bool) "exact frames" true (E2e.is_pass r)
57
58let test_check_frames_fail () =
59 let r = E2e.check_frames ~received:3 ~target:10 in
60 Alcotest.(check bool) "not enough frames" false (E2e.is_pass r)
61
62let test_check_frames_zero () =
63 let r = E2e.check_frames ~received:0 ~target:10 in
64 Alcotest.(check bool) "zero frames" false (E2e.is_pass r)
65
66let test_check_exit_code_clean () =
67 let r = E2e.check_exit_code 0 in
68 Alcotest.(check result_testable)
69 "clean"
70 (pass "clean shutdown (exit code 0)")
71 r
72
73let test_check_exit_code_crash () =
74 let r = E2e.check_exit_code 1 in
75 Alcotest.(check bool) "crash" false (E2e.is_pass r)
76
77let test_check_exit_code_signal () =
78 let r = E2e.check_exit_code 137 in
79 Alcotest.(check bool) "signal" false (E2e.is_pass r)
80
81let test_pp_result_pass () =
82 let s = Fmt.str "%a" E2e.pp_result (pass "ok") in
83 Alcotest.(check bool) "has PASS" true (contains "PASS" s)
84
85let test_pp_result_fail () =
86 let s = Fmt.str "%a" E2e.pp_result (fail "bad") in
87 Alcotest.(check bool) "has FAIL" true (contains "FAIL" s)
88
89let test_report_all_pass () =
90 (* report prints to stdout but returns exit code *)
91 let code = E2e.report [ pass "a"; pass "b" ] in
92 Alcotest.(check int) "exit 0" 0 code
93
94let test_report_with_failure () =
95 let code = E2e.report [ pass "a"; fail "b" ] in
96 Alcotest.(check int) "exit 1" 1 code
97
98let test_report_empty () =
99 let code = E2e.report [] in
100 Alcotest.(check int) "exit 0" 0 code
101
102(* ---------------------------------------------------------------------------
103 Suite
104 --------------------------------------------------------------------------- *)
105
106let suite =
107 ( "e2e",
108 [
109 Alcotest.test_case "config defaults" `Quick test_default_config;
110 Alcotest.test_case "is_pass" `Quick test_is_pass;
111 Alcotest.test_case "exit_code all pass" `Quick test_exit_code_all_pass;
112 Alcotest.test_case "exit_code one fail" `Quick test_exit_code_one_fail;
113 Alcotest.test_case "exit_code empty" `Quick test_exit_code_empty;
114 Alcotest.test_case "check_frames enough" `Quick test_check_frames_pass;
115 Alcotest.test_case "check_frames exact" `Quick test_check_frames_exact;
116 Alcotest.test_case "check_frames not enough" `Quick test_check_frames_fail;
117 Alcotest.test_case "check_frames zero" `Quick test_check_frames_zero;
118 Alcotest.test_case "check_exit clean" `Quick test_check_exit_code_clean;
119 Alcotest.test_case "check_exit crash" `Quick test_check_exit_code_crash;
120 Alcotest.test_case "check_exit signal" `Quick test_check_exit_code_signal;
121 Alcotest.test_case "pp_result pass" `Quick test_pp_result_pass;
122 Alcotest.test_case "pp_result fail" `Quick test_pp_result_fail;
123 Alcotest.test_case "report all pass" `Quick test_report_all_pass;
124 Alcotest.test_case "report with failure" `Quick test_report_with_failure;
125 Alcotest.test_case "report empty" `Quick test_report_empty;
126 ] )