Skip to content

Commit 075b207

Browse files
committed
tests: on_target: Add test for FOTA cancellation from cloud
Add test for FOTA cancellation from cloud. Reinstate the LTE connection drop test and add a custom shell command to disconnect and connect to LTE using zbus events/cmds to the network module. Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no>
1 parent 5e1dbd2 commit 075b207

File tree

4 files changed

+148
-26
lines changed

4 files changed

+148
-26
lines changed

app/src/modules/cloud/cloud_module.c

+37-8
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ static void shadow_get(bool delta_only)
479479
} else if (err == -ETIMEDOUT) {
480480
LOG_WRN("Request timed out, error: %d", err);
481481
return;
482+
} else if (err == -ENETUNREACH) {
483+
LOG_WRN("Network is unreachable, error: %d", err);
484+
return;
482485
} else if (err > 0) {
483486
LOG_WRN("Cloud error: %d", err);
484487
return;
@@ -535,7 +538,10 @@ static void state_connected_ready_run(void *o)
535538
msg.conn_eval_params.energy_estimate,
536539
NRF_CLOUD_NO_TIMESTAMP,
537540
confirmable);
538-
if (err) {
541+
if (err == -ENETUNREACH) {
542+
LOG_WRN("Network is unreachable, error: %d", err);
543+
return;
544+
} else if (err) {
539545
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
540546
SEND_FATAL_ERROR();
541547
return;
@@ -545,7 +551,10 @@ static void state_connected_ready_run(void *o)
545551
msg.conn_eval_params.rsrp,
546552
NRF_CLOUD_NO_TIMESTAMP,
547553
confirmable);
548-
if (err) {
554+
if (err == -ENETUNREACH) {
555+
LOG_WRN("Network is unreachable, error: %d", err);
556+
return;
557+
} else if (err) {
549558
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
550559
SEND_FATAL_ERROR();
551560
return;
@@ -567,9 +576,13 @@ static void state_connected_ready_run(void *o)
567576
msg.percentage,
568577
NRF_CLOUD_NO_TIMESTAMP,
569578
confirmable);
570-
if (err) {
579+
if (err == -ENETUNREACH) {
580+
LOG_WRN("Network is unreachable, error: %d", err);
581+
return;
582+
} else if (err) {
571583
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
572584
SEND_FATAL_ERROR();
585+
return;
573586
}
574587

575588
return;
@@ -586,27 +599,39 @@ static void state_connected_ready_run(void *o)
586599
msg.temperature,
587600
NRF_CLOUD_NO_TIMESTAMP,
588601
confirmable);
589-
if (err) {
602+
if (err == -ENETUNREACH) {
603+
LOG_WRN("Network is unreachable, error: %d", err);
604+
return;
605+
} else if (err) {
590606
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
591607
SEND_FATAL_ERROR();
608+
return;
592609
}
593610

594611
err = nrf_cloud_coap_sensor_send(NRF_CLOUD_JSON_APPID_VAL_AIR_PRESS,
595612
msg.pressure,
596613
NRF_CLOUD_NO_TIMESTAMP,
597614
confirmable);
598-
if (err) {
615+
if (err == -ENETUNREACH) {
616+
LOG_WRN("Network is unreachable, error: %d", err);
617+
return;
618+
} else if (err) {
599619
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
600620
SEND_FATAL_ERROR();
621+
return;
601622
}
602623

603624
err = nrf_cloud_coap_sensor_send(NRF_CLOUD_JSON_APPID_VAL_HUMID,
604625
msg.humidity,
605626
NRF_CLOUD_NO_TIMESTAMP,
606627
confirmable);
607-
if (err) {
628+
if (err == -ENETUNREACH) {
629+
LOG_WRN("Network is unreachable, error: %d", err);
630+
return;
631+
} else if (err) {
608632
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
609633
SEND_FATAL_ERROR();
634+
return;
610635
}
611636

612637
return;
@@ -618,9 +643,13 @@ static void state_connected_ready_run(void *o)
618643
const struct cloud_payload *payload = MSG_TO_PAYLOAD(state_object->msg_buf);
619644

620645
err = nrf_cloud_coap_json_message_send(payload->buffer, false, confirmable);
621-
if (err) {
622-
LOG_ERR("nrf_cloud_coap_json_message_send, error: %d", err);
646+
if (err == -ENETUNREACH) {
647+
LOG_WRN("Network is unreachable, error: %d", err);
648+
return;
649+
} else if (err) {
650+
LOG_ERR("nrf_cloud_coap_sensor_send, error: %d", err);
623651
SEND_FATAL_ERROR();
652+
return;
624653
}
625654
}
626655

app/src/modules/fota/fota.c

+3
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ static void state_polling_for_update_entry(void *o)
316316
SEND_FATAL_ERROR();
317317
}
318318
break;
319+
case -ENETUNREACH:
320+
LOG_WRN("Network is unreachable");
321+
break;
319322
case -ENOENT:
320323
LOG_DBG("FOTA job finished, status reported to nRF Cloud");
321324
break;

app/src/modules/shell/shell.c

+53-9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "message_channel.h"
2424
#include "button.h"
2525
#include "cloud_module.h"
26+
#include "network.h"
2627

2728
LOG_MODULE_REGISTER(shell, CONFIG_APP_SHELL_LOG_LEVEL);
2829

@@ -213,6 +214,44 @@ static int cmd_button_press(const struct shell *sh, size_t argc,
213214
return 0;
214215
}
215216

217+
static int cmd_connect(const struct shell *sh, size_t argc, char **argv)
218+
{
219+
ARG_UNUSED(argc);
220+
ARG_UNUSED(argv);
221+
222+
int err;
223+
const struct network_msg msg = {
224+
.type = NETWORK_CONNECT,
225+
};
226+
227+
err = zbus_chan_pub(&NETWORK_CHAN, &msg, K_SECONDS(1));
228+
if (err) {
229+
shell_print(sh, "zbus_chan_pub, error: %d", err);
230+
return 1;
231+
}
232+
233+
return 0;
234+
}
235+
236+
static int cmd_disconnect(const struct shell *sh, size_t argc, char **argv)
237+
{
238+
ARG_UNUSED(argc);
239+
ARG_UNUSED(argv);
240+
241+
int err;
242+
const struct network_msg msg = {
243+
.type = NETWORK_DISCONNECT,
244+
};
245+
246+
err = zbus_chan_pub(&NETWORK_CHAN, &msg, K_SECONDS(1));
247+
if (err) {
248+
shell_print(sh, "zbus_chan_pub, error: %d", err);
249+
return 1;
250+
}
251+
252+
return 0;
253+
}
254+
216255
static int cmd_publish_on_payload_chan(const struct shell *sh, size_t argc, char **argv)
217256
{
218257
int err;
@@ -323,16 +362,21 @@ static void shell_task(void)
323362
}
324363

325364
SHELL_STATIC_SUBCMD_SET_CREATE(sub_zbus_publish,
326-
SHELL_CMD(payload_chan, NULL, "Publish on payload channel", cmd_publish_on_payload_chan),
327-
SHELL_SUBCMD_SET_END
328-
);
365+
SHELL_CMD(payload_chan,
366+
NULL,
367+
"Publish on payload channel",
368+
cmd_publish_on_payload_chan),
369+
SHELL_SUBCMD_SET_END
370+
);
329371

330372
SHELL_STATIC_SUBCMD_SET_CREATE(sub_zbus,
331-
SHELL_CMD(ping, NULL, "Ping command.", cmd_zbus_ping),
332-
SHELL_CMD(button_press, NULL, "Button press command.", cmd_button_press),
333-
SHELL_CMD(publish, &sub_zbus_publish, "Publish on a zbus channel", NULL),
334-
SHELL_SUBCMD_SET_END
335-
);
373+
SHELL_CMD(ping, NULL, "Ping", cmd_zbus_ping),
374+
SHELL_CMD(button_press, NULL, "Button press", cmd_button_press),
375+
SHELL_CMD(publish, &sub_zbus_publish, "Publish a payload", NULL),
376+
SHELL_CMD(connect, NULL, "Connect to LTE", cmd_connect),
377+
SHELL_CMD(disconnect, NULL, "Disconnect from LTE", cmd_disconnect),
378+
SHELL_SUBCMD_SET_END
379+
);
336380

337381
SHELL_CMD_REGISTER(zbus, &sub_zbus, "Zbus shell", NULL);
338382

@@ -342,7 +386,7 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_uart,
342386
SHELL_CMD(pm_enable, NULL, "Enable UART power management", cmd_uart_pm_enable),
343387
SHELL_CMD(pm_disable, NULL, "Disable UART power management", cmd_uart_pm_disable),
344388
SHELL_SUBCMD_SET_END
345-
);
389+
);
346390

347391
SHELL_CMD_REGISTER(uart, &sub_uart, "UART shell", NULL);
348392

tests/on_target/tests/test_functional/test_fota.py

+55-9
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
APP_FOTA_TIMEOUT = 60 * 10
3131
FULL_MFW_FOTA_TIMEOUT = 60 * 30
3232

33-
3433
def await_nrfcloud(func, expected, field, timeout):
3534
start = time.time()
3635
logger.info(f"Awaiting {field} == {expected} in nrfcloud shadow...")
@@ -65,26 +64,61 @@ def run_fota_resumption(t91x_fota, fota_type):
6564

6665
# LTE disconnect
6766
t91x_fota.uart.flush()
68-
t91x_fota.uart.write("lte offline\r\n")
67+
t91x_fota.uart.write("zbus disconnect\r\n")
6968
t91x_fota.uart.wait_for_str(patterns_lte_offline, timeout=20)
7069

7170
# LTE reconnect
7271
t91x_fota.uart.flush()
73-
t91x_fota.uart.write("lte normal\r\n")
72+
t91x_fota.uart.write("zbus connect\r\n")
7473
t91x_fota.uart.wait_for_str(patterns_lte_normal, timeout=120)
7574

7675
t91x_fota.uart.wait_for_str("fota_download: Refuse fragment, restart with offset")
7776
t91x_fota.uart.wait_for_str("fota_download: Downloading from offset:")
7877

78+
def run_fota_reschedule(t91x_fota, fota_type):
79+
t91x_fota.uart.wait_for_str(f"5%", timeout=APP_FOTA_TIMEOUT)
80+
logger.debug(f"Cancelling FOTA, type: {fota_type}")
81+
82+
t91x_fota.fota.cancel_fota_job(t91x_fota.data['job_id'])
83+
84+
await_nrfcloud(
85+
functools.partial(t91x_fota.fota.get_fota_status, t91x_fota.data['job_id']),
86+
"CANCELLED",
87+
"FOTA status",
88+
APP_FOTA_TIMEOUT
89+
)
90+
91+
patterns_fota_cancel = ["Firmware download canceled", "state_waiting_for_poll_request_entry"]
92+
93+
t91x_fota.uart.wait_for_str(patterns_fota_cancel, timeout=180)
94+
95+
t91x_fota.data['job_id'] = t91x_fota.fota.create_fota_job(t91x_fota.device_id, t91x_fota.data['bundle_id'])
96+
97+
logger.info(f"Rescheduled FOTA Job (ID: {t91x_fota.data['job_id']})")
98+
99+
# Sleep a bit and trigger fota poll
100+
for i in range(3):
101+
try:
102+
time.sleep(30)
103+
t91x_fota.uart.write("zbus button_press\r\n")
104+
t91x_fota.uart.wait_for_str("nrf_cloud_fota_poll: Starting FOTA download")
105+
break
106+
except AssertionError:
107+
continue
108+
else:
109+
raise AssertionError(f"Fota update not available after {i} attempts")
110+
79111
@pytest.fixture
80-
def run_fota_fixture(t91x_fota, hex_file):
81-
def _run_fota(bundle_id="", fota_type="app", fotatimeout=APP_FOTA_TIMEOUT, new_version=TEST_APP_VERSION):
112+
def run_fota_fixture(t91x_fota, hex_file, reschedule=False):
113+
def _run_fota(bundle_id="", fota_type="app", fotatimeout=APP_FOTA_TIMEOUT, new_version=TEST_APP_VERSION, reschedule=False):
82114
flash_device(os.path.abspath(hex_file))
83115
t91x_fota.uart.xfactoryreset()
84116
t91x_fota.uart.flush()
85117
reset_device()
86118
t91x_fota.uart.wait_for_str("Connected to Cloud")
87119

120+
time.sleep(60)
121+
88122
if fota_type == "app":
89123
bundle_id = t91x_fota.fota.upload_firmware(
90124
"nightly_test_app",
@@ -105,7 +139,7 @@ def _run_fota(bundle_id="", fota_type="app", fotatimeout=APP_FOTA_TIMEOUT, new_v
105139
# Sleep a bit and trigger fota poll
106140
for i in range(3):
107141
try:
108-
time.sleep(30)
142+
time.sleep(10)
109143
t91x_fota.uart.write("zbus button_press\r\n")
110144
t91x_fota.uart.wait_for_str("nrf_cloud_fota_poll: Starting FOTA download")
111145
break
@@ -114,15 +148,26 @@ def _run_fota(bundle_id="", fota_type="app", fotatimeout=APP_FOTA_TIMEOUT, new_v
114148
else:
115149
raise AssertionError(f"Fota update not available after {i} attempts")
116150

117-
# if fota_type == "app":
118-
# run_fota_resumption(t91x_fota, "app")
151+
if reschedule:
152+
run_fota_reschedule(t91x_fota, fota_type)
153+
154+
if fota_type == "app":
155+
run_fota_resumption(t91x_fota, "app")
156+
157+
await_nrfcloud(
158+
functools.partial(t91x_fota.fota.get_fota_status, t91x_fota.data['job_id']),
159+
"IN_PROGRESS",
160+
"FOTA status",
161+
fotatimeout
162+
)
119163

120164
await_nrfcloud(
121165
functools.partial(t91x_fota.fota.get_fota_status, t91x_fota.data['job_id']),
122166
"COMPLETED",
123167
"FOTA status",
124168
fotatimeout
125169
)
170+
126171
try:
127172
if fota_type == "app":
128173
await_nrfcloud(
@@ -173,5 +218,6 @@ def test_full_mfw_fota(run_fota_fixture):
173218
bundle_id=FULL_MFW_BUNDLEID,
174219
fota_type="full",
175220
new_version=MFW_202_VERSION,
176-
fotatimeout=FULL_MFW_FOTA_TIMEOUT
221+
fotatimeout=FULL_MFW_FOTA_TIMEOUT,
222+
reschedule=True
177223
)

0 commit comments

Comments
 (0)