Skip to content

Commit d537695

Browse files
committed
Fix brittle self-tests and add missing coverage
1 parent 42729cf commit d537695

File tree

9 files changed

+165
-22
lines changed

9 files changed

+165
-22
lines changed

src/driver/drv_openWeatherMap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ typedef struct weatherChannels_s {
2323

2424
weatherData_t *Weather_GetData();
2525
const char *Weather_GetReply();
26+
void Weather_SetReply(const char *s);
2627

2728
#endif // __DRV_OPENWEATHERMAP_H__

src/selftest/selftest_DHT.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ void Test_DHT() {
2020
PIN_SetPinChannel2ForPinIndex(9, 2);
2121

2222
Sim_RunSeconds(5.0f, false);
23+
// After removing the role the driver should no longer update the channels.
24+
// They must remain at the last values sampled while the DHT11 role was active.
25+
SELFTEST_ASSERT_CHANNEL(1, 190);
26+
SELFTEST_ASSERT_CHANNEL(2, 67);
2327
}
2428

2529

src/selftest/selftest_enums.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ void Test_Enum_LowMidH() {
2424

2525
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
2626
"state_topic", "~/13/get",
27-
"uniq_id", "Windows_Enum_select_13",
28-
"uniq_id", "Windows_Enum_select_13",
27+
"command_topic", "~/13/set",
28+
"~", "obkEnumDemo",
2929
"uniq_id", "Windows_Enum_select_13");
3030

3131
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
@@ -63,8 +63,8 @@ void Test_Enum_LowMidHighOff() {
6363

6464
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
6565
"state_topic", "~/12/get",
66-
"uniq_id", "Windows_Enum_select_12",
67-
"uniq_id", "Windows_Enum_select_12",
66+
"command_topic", "~/12/set",
67+
"~", "obkEnumDemo",
6868
"uniq_id", "Windows_Enum_select_12");
6969

7070
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
@@ -123,8 +123,8 @@ void Test_Enum_3opts() {
123123

124124
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
125125
"state_topic", "~/14/get",
126-
"unique_id", "14",
127-
"unique_id", "14",
126+
"command_topic", "~/14/set",
127+
"~", "obkEnumDemo",
128128
"uniq_id", "Windows_Enum_select_14");
129129

130130
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
@@ -195,8 +195,8 @@ void Test_Enum_BadOk() {
195195

196196
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,
197197
"state_topic", "~/4/get",
198-
"unique_id", "4",
199-
"unique_id", "4",
198+
"command_topic", "~/4/set",
199+
"~", "obkEnumDemo",
200200
"uniq_id", "Windows_Enum_select_4");
201201

202202
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY("homeassistant", true, 0, 0,

src/selftest/selftest_flags.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void Test_Flags() {
4949
bool bSet = (i == 33 || i == 0 || i == 1 || i == 55 || i == 63);
5050
SELFTEST_ASSERT_FLAG(i, bSet);
5151
}
52-
// clear flag 63
52+
// clear flag 55
5353
CMD_ExecuteCommand("SetFlag 55 0", 0);
5454
for (int i = 0; i < 64; i++) {
5555
bool bSet = (i == 33 || i == 0 || i == 1 || i == 63);

src/selftest/selftest_if_inside_backlog.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,32 @@ void Test_IF_Inside_Backlog() {
1414
PIN_SetPinRoleForPinIndex(26, IOR_PWM);
1515
PIN_SetPinChannelForPinIndex(26, 2);
1616

17+
// Non-LED test: verify that "if" works correctly when nested inside a backlog alias.
18+
// Uses plain channels so this runs regardless of ENABLE_LED_BASIC.
19+
CMD_ExecuteCommand("alias pick_val backlog if $CH10>7&&$CH10<19 then setChannel 20 200; if $CH10<=7||$CH10>=19 then setChannel 20 500;", 0);
1720

18-
#if ENABLE_LED_BASIC
21+
// hour 10 -> day -> channel 20 = 200
22+
CMD_ExecuteCommand("setChannel 10 10", 0);
23+
CMD_ExecuteCommand("pick_val", 0);
24+
SELFTEST_ASSERT_CHANNEL(20, 200);
1925

26+
// hour 3 -> night -> channel 20 = 500
27+
CMD_ExecuteCommand("setChannel 10 3", 0);
28+
CMD_ExecuteCommand("pick_val", 0);
29+
SELFTEST_ASSERT_CHANNEL(20, 500);
30+
31+
// hour 12 -> day -> channel 20 = 200
32+
CMD_ExecuteCommand("setChannel 10 12", 0);
33+
CMD_ExecuteCommand("pick_val", 0);
34+
SELFTEST_ASSERT_CHANNEL(20, 200);
35+
36+
// hour 22 -> night -> channel 20 = 500
37+
CMD_ExecuteCommand("setChannel 10 22", 0);
38+
CMD_ExecuteCommand("pick_val", 0);
39+
SELFTEST_ASSERT_CHANNEL(20, 500);
40+
41+
#if ENABLE_LED_BASIC
42+
// LED-specific variant: same logic but driving led_temperature instead
2043
CMD_ExecuteCommand("led_enableAll 1", 0);
2144

2245
CMD_ExecuteCommand("alias day_lights backlog led_temperature 200", 0);

src/selftest/selftest_openWeatherMap.c

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,25 @@ const char *reply_no_weather =
3737
"HTTP/1.1 200 OK\r\n\r\n"
3838
"{\"coord\":{\"lon\":10,\"lat\":50},\"main\":{\"temp\":20,\"pressure\":900,\"humidity\":60}}";
3939

40+
const char *owm_rain_reply =
41+
"HTTP/1.1 200 OK\r\n\r\n"
42+
"{"
43+
" \"coord\": {\"lon\": 11.0, \"lat\": 51.0},"
44+
" \"weather\": [{\"id\":500,\"main\":\"Rain\",\"description\":\"light rain\",\"icon\":\"10d\"}],"
45+
" \"main\": {\"temp\": 18.0, \"pressure\": 901, \"humidity\": 70},"
46+
" \"timezone\": 3600,"
47+
" \"sys\": {\"sunrise\":1604960000,\"sunset\":1605000000}"
48+
"}";
49+
50+
void Test_OpenWeatherMap_StaleWeatherStringsFollowLatestFullReply();
51+
4052
void Test_OpenWeatherMap() {
53+
weatherData_t *w;
4154

4255
// HTTP header + json
4356
Weather_SetReply(owm_sample_reply);
4457

45-
weatherData_t *w = Weather_GetData();
58+
w = Weather_GetData();
4659
SELFTEST_ASSERT_FLOATCOMPARE(w->humidity, 85);
4760
SELFTEST_ASSERT_FLOATCOMPARE(w->pressure, 998);
4861
SELFTEST_ASSERT_FLOATCOMPARE(w->temp, 23);
@@ -51,7 +64,50 @@ void Test_OpenWeatherMap() {
5164
SELFTEST_ASSERT_FLOATCOMPARE(w->lat, 50);
5265
SELFTEST_ASSERT_FLOATCOMPARE(w->lon, 10);
5366

67+
// Test with a response that has no "weather" array (edge case)
68+
Weather_SetReply(reply_no_weather);
69+
w = Weather_GetData();
70+
// Numeric fields from "main" and "coord" must still parse
71+
SELFTEST_ASSERT_FLOATCOMPARE(w->humidity, 60);
72+
SELFTEST_ASSERT_FLOATCOMPARE(w->pressure, 900);
73+
SELFTEST_ASSERT_FLOATCOMPARE(w->temp, 20);
74+
SELFTEST_ASSERT_FLOATCOMPARE(w->lat, 50);
75+
SELFTEST_ASSERT_FLOATCOMPARE(w->lon, 10);
76+
// Current implementation only updates these strings when weather[0] exists,
77+
// so a reply without a weather array keeps the previous parsed values.
78+
SELFTEST_ASSERT_STRING(w->main_weather, "Clear");
79+
SELFTEST_ASSERT_STRING(w->description, "clear sky");
5480

81+
Test_OpenWeatherMap_StaleWeatherStringsFollowLatestFullReply();
82+
}
83+
84+
void Test_OpenWeatherMap_StaleWeatherStringsFollowLatestFullReply() {
85+
weatherData_t *w;
86+
87+
Weather_SetReply(owm_sample_reply);
88+
w = Weather_GetData();
89+
SELFTEST_ASSERT_STRING(w->main_weather, "Clear");
90+
SELFTEST_ASSERT_STRING(w->description, "clear sky");
91+
92+
Weather_SetReply(owm_rain_reply);
93+
w = Weather_GetData();
94+
SELFTEST_ASSERT_FLOATCOMPARE(w->humidity, 70);
95+
SELFTEST_ASSERT_FLOATCOMPARE(w->pressure, 901);
96+
SELFTEST_ASSERT_FLOATCOMPARE(w->temp, 18);
97+
SELFTEST_ASSERT_FLOATCOMPARE(w->lat, 51);
98+
SELFTEST_ASSERT_FLOATCOMPARE(w->lon, 11);
99+
SELFTEST_ASSERT_STRING(w->main_weather, "Rain");
100+
SELFTEST_ASSERT_STRING(w->description, "light rain");
101+
102+
Weather_SetReply(reply_no_weather);
103+
w = Weather_GetData();
104+
SELFTEST_ASSERT_FLOATCOMPARE(w->humidity, 60);
105+
SELFTEST_ASSERT_FLOATCOMPARE(w->pressure, 900);
106+
SELFTEST_ASSERT_FLOATCOMPARE(w->temp, 20);
107+
SELFTEST_ASSERT_FLOATCOMPARE(w->lat, 50);
108+
SELFTEST_ASSERT_FLOATCOMPARE(w->lon, 10);
109+
SELFTEST_ASSERT_STRING(w->main_weather, "Rain");
110+
SELFTEST_ASSERT_STRING(w->description, "light rain");
55111
}
56112

57113

src/selftest/selftest_script.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,17 @@ void Test_Scripting_Loop3() {
129129
SELFTEST_ASSERT_INTEGER(CMD_GetCountActiveScriptThreads(), 1);
130130
SELFTEST_ASSERT_CHANNEL(0, 1);
131131
}
132-
for (int i = 0; i < 2; i++) {
133-
Sim_RunSeconds(1, false);
134-
}
132+
// Second 15: the script delay counts down to 0, but the final if/setChannel
133+
// pair does not run until the following scheduler tick.
134+
Sim_RunSeconds(1, false);
135+
SELFTEST_ASSERT_INTEGER(CMD_GetCountActiveScriptThreads(), 1);
136+
SELFTEST_ASSERT_CHANNEL(0, 1);
137+
// After second 16 the final loop body has resumed and the script must have ended.
138+
Sim_RunSeconds(1, false);
139+
SELFTEST_ASSERT_INTEGER(CMD_GetCountActiveScriptThreads(), 0);
140+
// Channel 0 should now be 0 because the script's final command has run.
141+
SELFTEST_ASSERT_CHANNEL(0, 0);
142+
// Run a couple more seconds to confirm nothing changes again
135143
for (int i = 0; i < 3; i++) {
136144
Sim_RunSeconds(1, false);
137145
SELFTEST_ASSERT_INTEGER(CMD_GetCountActiveScriptThreads(), 0);

src/selftest/selftest_shutters.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,30 @@ void Test_Shutters() {
1616
PIN_SetPinRoleForPinIndex(7, IOR_Button_ShutterDown);
1717

1818
CMD_ExecuteCommand("startDriver Shutters", 0);
19+
// Run 1 second so DRV_Shutters_RunEverySecond fires and registers the shutter struct.
20+
// The struct is lazily created the first time RunEverySecond scans the pins.
21+
Sim_RunSeconds(1.0f, false);
22+
// Initially both pins should be off (shutter is stopped at start)
1923
SELFTEST_ASSERT_PIN_BOOLEAN(4, false);
2024
SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
25+
26+
// Issue an open command (100% = fully open = SHUTTER_OPENING direction)
2127
CMD_ExecuteCommand("ShutterMove0 100", 0);
28+
// After issuing the open command: ShutterA=1 (opening), ShutterB=0
29+
SELFTEST_ASSERT_PIN_BOOLEAN(4, true);
30+
SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
31+
2232
Sim_RunSeconds(1.0f, false);
23-
//Sim_RunFrames(1, false);
24-
//SELFTEST_ASSERT_PIN_BOOLEAN(4, true);
25-
//SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
33+
// Still moving after 1 second (default open time is 10s)
34+
SELFTEST_ASSERT_PIN_BOOLEAN(4, true);
35+
SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
36+
37+
// Stop the shutter
2638
CMD_ExecuteCommand("ShutterMove0 Stop", 0);
2739
Sim_RunFrames(1, false);
28-
//SELFTEST_ASSERT_PIN_BOOLEAN(4, false);
29-
//SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
40+
// After Stop: both pins must be off
41+
SELFTEST_ASSERT_PIN_BOOLEAN(4, false);
42+
SELFTEST_ASSERT_PIN_BOOLEAN(5, false);
3043

3144

3245

src/selftest/selftest_tclAC.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,68 @@
22

33
#include "selftest_local.h"
44

5+
extern void TCL_UART_TryToGetNextPacket(void);
6+
extern void TCL_UART_RunEverySecond(void);
7+
8+
static const char *valid_status_reply =
9+
"BB 01 00 04 2D 04 00 11 21 00 00 00 FF 00 00 00 00 1D CA FF 00 00 00 00 00 F0 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7F";
10+
11+
static const char *invalid_status_reply =
12+
"BB 01 00 04 2D 04 00 11 21 00 00 00 FF 00 00 00 00 1D CA FF 00 00 00 00 00 F0 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00";
13+
14+
void Test_Driver_TCL_AC_InvalidReplyDoesNotBlockPolling() {
15+
SIM_ClearOBK(0);
16+
CMD_ExecuteCommand("lfs_format", 0);
17+
SIM_ClearUART();
18+
SIM_UART_InitReceiveRingBuffer(256);
19+
20+
CMD_ExecuteCommand("startDriver TCL", 0);
21+
22+
CMD_ExecuteCommand(va("uartFakeHex %s", invalid_status_reply), 0);
23+
TCL_UART_TryToGetNextPacket();
24+
SELFTEST_ASSERT_HAS_UART_EMPTY();
25+
26+
TCL_UART_RunEverySecond();
27+
SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
28+
}
529

630
void Test_Driver_TCL_AC() {
31+
Test_Driver_TCL_AC_InvalidReplyDoesNotBlockPolling();
32+
733
// reset whole device
834
SIM_ClearOBK(0);
935
CMD_ExecuteCommand("lfs_format", 0);
1036
SIM_ClearUART();
37+
// Required to set up the receive ring buffer before feeding bytes with uartFakeHex
38+
SIM_UART_InitReceiveRingBuffer(256);
1139

1240
CMD_ExecuteCommand("startDriver TCL", 0);
1341

42+
// ACMode 1 = COOL mode: builds a 35-byte set command and flags it for sending
1443
CMD_ExecuteCommand("ACMode 1", 0);
1544
TCL_UART_RunEverySecond();
45+
// TCL set command is always 35 bytes — verify bytes were sent
46+
SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
1647
SIM_ClearUART();
1748

49+
// FANMode 1 = FAN_1: rebuilds and queues another 35-byte set command
1850
CMD_ExecuteCommand("FANMode 1", 0);
1951
TCL_UART_RunEverySecond();
52+
SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
2053
SIM_ClearUART();
2154

22-
23-
CMD_ExecuteCommand("uartFakeHex BB 01 00 04 2D 04 00 00 00 00 00 00 FF 00 00 00 00 00 FF FF 00 00 00 00 00 00 F0 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6A", 0);
55+
// Feed a valid get-response packet into the driver UART buffer and parse it directly.
56+
// Then run the normal every-second path once to ensure the driver continues polling.
57+
CMD_ExecuteCommand(va("uartFakeHex %s", valid_status_reply), 0);
58+
TCL_UART_TryToGetNextPacket();
59+
// The fake response should be fully consumed by the parser.
60+
SELFTEST_ASSERT_HAS_UART_EMPTY();
2461
TCL_UART_RunEverySecond();
62+
// After parsing, the regular poll path should still send the next request frame.
63+
SELFTEST_ASSERT_HAS_SOME_DATA_IN_UART();
2564
}
2665

2766

2867

2968
#endif
3069

31-

0 commit comments

Comments
 (0)