nx_moonraker_telemetry_parse_test.nx source
↩ module page · 99 lines · 4794 B
1// nx_moonraker_telemetry_parse_test.nx -- parse canonical
2// Moonraker JSON responses into NxPrintTelemetry thermal fields.
3//
4// Closed-form invariants:
5// (a) Empty/null body -> BAD_INPUT
6// (b) JSON with no extruder/heater_bed -> NO_OBJECT
7// (c) Canonical extruder+heater_bed body -> OK, fields populated
8// (d) Missing target -> setpoint = -1 (skip axis), actual still set
9// (e) Negative temperature (e.g. ambient probe) handled
10// (f) Nested objects don't confuse the brace matcher
11// (g) Same-key inside different scope: "temperature" inside the
12// extruder scope picks the extruder one, not heater_bed's
13// (h) has_thermal flag set when any value found
14//
15// expect_exit: 0
16// license_tier: ORIGINAL
17
18import "nx_syscalls.nx"
19import "nx_print_telemetry.nx"
20import "nx_moonraker_telemetry_parse.nx"
21
22func cstr_len(s: *u8) -> i64 {
23 var n: i64 = 0
24 while s[n] != 0 { n = n + 1 }
25 return n
26}
27
28func main() -> i64 {
29 let ev: *NxPrintTelemetry = nx_print_telemetry_new()
30 if (ev as i64) == 0 { return 5 }
31
32 // ===== (a) Bad inputs =====
33 if nx_moonraker_parse_thermal(0 as *u8, 100, ev) != NX_MR_PARSE_BAD_INPUT { return 10 }
34 if nx_moonraker_parse_thermal("dummy", 5, 0 as *NxPrintTelemetry) != NX_MR_PARSE_BAD_INPUT { return 11 }
35 if nx_moonraker_parse_thermal("dummy", 0, ev) != NX_MR_PARSE_BAD_INPUT { return 12 }
36
37 // ===== (b) JSON without extruder/heater_bed -> NO_OBJECT =====
38 let body_a: *u8 = "{\"result\":{\"status\":{\"other\":{\"foo\":1}}}}"
39 if nx_moonraker_parse_thermal(body_a, cstr_len(body_a), ev) != NX_MR_PARSE_NO_OBJECT { return 20 }
40
41 // ===== (c) Canonical response =====
42 let ev2: *NxPrintTelemetry = nx_print_telemetry_new()
43 let body_c: *u8 =
44"{\"result\":{\"status\":{\"extruder\":{\"temperature\":209.8,\"target\":210.0,\"power\":0.45},\"heater_bed\":{\"temperature\":59.7,\"target\":60.0}},\"eventtime\":12345.6}}"
45 let vc: i64 = nx_moonraker_parse_thermal(body_c, cstr_len(body_c), ev2)
46 if vc != NX_MR_PARSE_OK { return 30 }
47 if ev2.hotend_actual_c != 209 { return 31 }
48 if ev2.hotend_setpoint_c != 210 { return 32 }
49 if ev2.bed_actual_c != 59 { return 33 }
50 if ev2.bed_setpoint_c != 60 { return 34 }
51 if ev2.has_thermal != 1 { return 35 }
52
53 // ===== (d) Missing target -> setpoint -1, actual still set =====
54 let ev3: *NxPrintTelemetry = nx_print_telemetry_new()
55 let body_d: *u8 =
56"{\"result\":{\"status\":{\"extruder\":{\"temperature\":25.0,\"power\":0.0},\"heater_bed\":{\"temperature\":24.0}}}}"
57 if nx_moonraker_parse_thermal(body_d, cstr_len(body_d), ev3) != NX_MR_PARSE_OK { return 40 }
58 if ev3.hotend_actual_c != 25 { return 41 }
59 if ev3.hotend_setpoint_c != -1 { return 42 }
60 if ev3.bed_actual_c != 24 { return 43 }
61 if ev3.bed_setpoint_c != -1 { return 44 }
62
63 // ===== (e) Negative temperature =====
64 let ev4: *NxPrintTelemetry = nx_print_telemetry_new()
65 let body_e: *u8 =
66"{\"result\":{\"status\":{\"extruder\":{\"temperature\":-12.5,\"target\":-10.0}}}}"
67 if nx_moonraker_parse_thermal(body_e, cstr_len(body_e), ev4) != NX_MR_PARSE_OK { return 50 }
68 if ev4.hotend_actual_c != -12 { return 51 }
69 if ev4.hotend_setpoint_c != -10 { return 52 }
70
71 // ===== (f) Nested object in extruder scope doesn't break matcher =====
72 let ev5: *NxPrintTelemetry = nx_print_telemetry_new()
73 let body_f: *u8 =
74"{\"result\":{\"status\":{\"extruder\":{\"temperature\":200.0,\"target\":210.0,\"smoothed\":{\"value\":199.8,\"target\":210.0}},\"heater_bed\":{\"temperature\":60.0,\"target\":65.0}}}}"
75 if nx_moonraker_parse_thermal(body_f, cstr_len(body_f), ev5) != NX_MR_PARSE_OK { return 60 }
76 if ev5.hotend_actual_c != 200 { return 61 }
77 if ev5.hotend_setpoint_c != 210 { return 62 }
78 if ev5.bed_actual_c != 60 { return 63 }
79 if ev5.bed_setpoint_c != 65 { return 64 }
80
81 // ===== (g) Scope isolation: heater_bed's temperature is not picked
82 // when extracting extruder's temperature =====
83 // Test by putting heater_bed BEFORE extruder in the response.
84 let ev6: *NxPrintTelemetry = nx_print_telemetry_new()
85 let body_g: *u8 =
86"{\"result\":{\"status\":{\"heater_bed\":{\"temperature\":50.0,\"target\":55.0},\"extruder\":{\"temperature\":190.0,\"target\":200.0}}}}"
87 if nx_moonraker_parse_thermal(body_g, cstr_len(body_g), ev6) != NX_MR_PARSE_OK { return 70 }
88 if ev6.hotend_actual_c != 190 { return 71 } // NOT 50
89 if ev6.bed_actual_c != 50 { return 72 }
90
91 // ===== (h) has_thermal flag set =====
92 if ev6.has_thermal != 1 { return 80 }
93
94 // Empty pass-through: ev with no parse run has has_thermal = 0
95 let ev7: *NxPrintTelemetry = nx_print_telemetry_new()
96 if ev7.has_thermal != 0 { return 81 }
97
98 return 0
99}