11import os
22import sys
33import json
4- import requests .exceptions # Used for handling HTTP errors from nRF Cloud API
4+ import time
5+ import pytest
6+ import requests .exceptions # Used for handling HTTP errors from nRF Cloud API
57
68# Ensure the utils directory is in the Python path
79sys .path .append (os .getcwd ())
1618
1719# --- Helper Functions ---
1820
21+
1922def _perform_initial_device_setup_and_factory_reset (dut_cloud , hex_file : str ):
2023 logger .info (f"Flashing device with { hex_file } and performing factory reset." )
2124
@@ -24,40 +27,57 @@ def _perform_initial_device_setup_and_factory_reset(dut_cloud, hex_file: str):
2427 dut_cloud .uart .flush ()
2528 reset_device ()
2629
30+
2731def _wait_for_lte_connection (dut_cloud , timeout : int = 240 ):
2832 logger .info ("Waiting for device to connect to LTE network..." )
2933
3034 log_pattern_network_connected = "network: Network connectivity established"
31- dut_cloud .uart .wait_for_str (log_pattern_network_connected , timeout = timeout )
35+ dut_cloud .uart .wait_for_str (
36+ log_pattern_network_connected ,
37+ timeout = timeout ,
38+ start_pos = dut_cloud .uart .get_size (),
39+ )
3240
3341 logger .info ("Device connected to LTE network." )
3442
43+
3544def _disconnect_network_and_clear_modem_credentials (dut_cloud , sec_tag : int ):
3645 logger .info ("Disconnecting network and clearing modem credentials..." )
3746
3847 log_pattern_network_disconnected = "network: Network connectivity lost"
3948 dut_cloud .uart .write ("att_network disconnect\r \n " )
40- dut_cloud .uart .wait_for_str (log_pattern_network_disconnected , timeout = 20 )
49+ dut_cloud .uart .wait_for_str (
50+ log_pattern_network_disconnected ,
51+ timeout = 20 ,
52+ start_pos = dut_cloud .uart .get_size (),
53+ )
4154
4255 # Clear any existing credentials for the given security tag
4356 dut_cloud .uart .write (f"at AT%CMNG=3,{ sec_tag } ,0\r \n " )
57+ time .sleep (1 )
4458 dut_cloud .uart .write (f"at AT%CMNG=3,{ sec_tag } ,1\r \n " )
59+ time .sleep (1 )
4560 dut_cloud .uart .write (f"at AT%CMNG=3,{ sec_tag } ,2\r \n " )
61+ time .sleep (1 )
4662
4763 logger .info ("Modem credentials cleared." )
4864
65+
4966def _get_attestation_token_from_device (dut_cloud ) -> str :
5067 logger .info ("Getting attestation token from device..." )
5168
5269 dut_cloud .uart .at_cmd_write ("at AT%ATTESTTOKEN\r \n " )
53- token_match = dut_cloud .uart .wait_for_str_re (r'%ATTESTTOKEN: "([^"]+)"' , timeout = 20 )
70+ token_match = dut_cloud .uart .wait_for_str_re (
71+ r'%ATTESTTOKEN: "([^"]+)"' , timeout = 20
72+ )
5473
5574 assert token_match , "No attestation token found"
5675 attestation_token = token_match [0 ]
5776 logger .info (f"Attestation Token: { attestation_token } " )
5877
5978 return attestation_token
6079
80+
6181def _unclaim_device_from_nrf_cloud_if_exists (dut_cloud ):
6282 """
6383 Attempts to unclaim the device from nRF Cloud.
@@ -70,42 +90,74 @@ def _unclaim_device_from_nrf_cloud_if_exists(dut_cloud):
7090 logger .info (f"Unclaim device status_code: { status_code } " )
7191 except requests .exceptions .HTTPError as e :
7292 if e .response .status_code == 404 :
73- logger .info (f"Device { dut_cloud .device_id } not found or already unclaimed (404), proceeding." )
93+ logger .info (
94+ f"Device { dut_cloud .device_id } not found or already unclaimed (404), proceeding."
95+ )
7496 else :
7597 logger .error (f"Error unclaiming device: { e } " )
76- raise # Re-raise other HTTP errors
98+ raise # Re-raise other HTTP errors
99+
77100
78101def _connect_to_network_and_wait_for_claiming_prompt (dut_cloud ):
79102 logger .info ("Connecting to network and waiting for device to request claiming..." )
80103
81104 log_pattern_network_connected = "network: Network connectivity established"
82- log_pattern_need_claiming = "Claim the device using the device's attestation token on nrfcloud.com"
105+ log_pattern_need_claiming = (
106+ "Claim the device using the device's attestation token on nrfcloud.com"
107+ )
83108
84109 dut_cloud .uart .write ("att_network connect\r \n " )
85- dut_cloud .uart .wait_for_str (log_pattern_network_connected , timeout = 240 )
86- dut_cloud .uart .wait_for_str (log_pattern_need_claiming , timeout = 240 )
110+ dut_cloud .uart .wait_for_str (
111+ log_pattern_network_connected , timeout = 240 , start_pos = dut_cloud .uart .get_size ()
112+ )
113+ dut_cloud .uart .wait_for_str (
114+ log_pattern_need_claiming , timeout = 240 , start_pos = dut_cloud .uart .get_size ()
115+ )
87116
88117 logger .info ("Device is ready to be claimed." )
89118
119+
90120def _claim_device_on_nrf_cloud (dut_cloud , attestation_token : str ):
91121 logger .info ("Claiming device on nRF Cloud..." )
92122
93123 dut_cloud .cloud .claim_device (attestation_token = attestation_token )
94124
95125 logger .info ("Device claimed successfully." )
96126
97- def _wait_for_provisioning_completion_and_cloud_connection (dut_cloud , timeout : int = 240 ):
98- logger .info ("Waiting for provisioning to complete and device to connect to nRF Cloud..." )
99127
100- dut_cloud .uart .wait_for_str ("cloud: Provisioning finished" , timeout = timeout )
101- dut_cloud .uart .wait_for_str ("cloud: Connected to Cloud" , timeout = timeout )
128+ def _wait_for_provisioning_completion_and_cloud_connection (
129+ dut_cloud , timeout : int = 240
130+ ):
131+ logger .info (
132+ "Waiting for provisioning to complete and device to connect to nRF Cloud..."
133+ )
134+
135+ dut_cloud .uart .wait_for_str (
136+ [
137+ "cloud: nrf_provisioning_callback: Provisioning finished" ,
138+ "cloud: Connected to Cloud" ,
139+ ],
140+ timeout = timeout ,
141+ start_pos = dut_cloud .uart .get_size (),
142+ )
102143
103144 logger .info ("Device provisioned and connected to nRF Cloud." )
104145
105- def _verify_device_location_data (dut_cloud , expected_lat : float = 61.5 , expected_lon : float = 10.5 , lat_tolerance : float = 2.0 , lon_tolerance : float = 1.0 , timeout : int = 300 ):
146+
147+ def _verify_device_location_data (
148+ dut_cloud ,
149+ expected_lat : float = 61.5 ,
150+ expected_lon : float = 10.5 ,
151+ lat_tolerance : float = 2.0 ,
152+ lon_tolerance : float = 1.0 ,
153+ timeout : int = 300 ,
154+ ):
106155 logger .info ("Verifying device location data..." )
107156
108- values = dut_cloud .uart .wait_for_str_re (r'location_event_handler: Got location: lat: ([\d.-]+), lon: ([\d.-]+), acc: ([\d.-]+), method:' , timeout = timeout )
157+ values = dut_cloud .uart .wait_for_str_re (
158+ r"location_event_handler: Got location: lat: ([\d.-]+), lon: ([\d.-]+), acc: ([\d.-]+),method:" ,
159+ timeout = timeout
160+ )
109161 assert values , "Failed to get location data from device."
110162
111163 lat_str , lon_str , acc_str = values
@@ -117,6 +169,7 @@ def _verify_device_location_data(dut_cloud, expected_lat: float = 61.5, expected
117169
118170 logger .info ("Device location data verified." )
119171
172+
120173def _trigger_device_reprovisioning_with_new_credentials (dut_cloud , sec_tag : int ):
121174 """
122175 Initiates the reprovisioning process on the device by:
@@ -127,26 +180,25 @@ def _trigger_device_reprovisioning_with_new_credentials(dut_cloud, sec_tag: int)
127180 logger .info ("Starting reprovisioning process with new credentials..." )
128181
129182 # Prepare the reprovisioning command for nRF Cloud
130- command_json = json .dumps ({
131- "description" : "Reprovisioning with new cloud credentials" ,
132- "request" : {
133- "cloudAccessKeyGeneration" : {
134- "secTag" : sec_tag
135- }
183+ command_json = json .dumps (
184+ {
185+ "description" : "Reprovisioning with new cloud credentials" ,
186+ "request" : {"cloudAccessKeyGeneration" : {"secTag" : sec_tag }},
136187 }
137- } )
188+ )
138189
139190 logger .info (f"Adding reprovisioning command to nRF Cloud: { command_json } " )
140191
141192 dut_cloud .cloud .add_provisioning_command (
142- device_id = dut_cloud .device_id ,
143- command = command_json
193+ device_id = dut_cloud .device_id , command = command_json
144194 )
145195
146196 # Update device shadow to indicate a new provisioning command is available
147197 logger .info ("Updating device shadow to trigger reprovisioning command processing." )
148198
149- dut_cloud .cloud .patch_delete_command_entry_from_shadow (device_id = dut_cloud .device_id )
199+ dut_cloud .cloud .patch_delete_command_entry_from_shadow (
200+ device_id = dut_cloud .device_id
201+ )
150202 dut_cloud .cloud .patch_add_provisioning_command_to_shadow (
151203 device_id = dut_cloud .device_id ,
152204 command = 1 , # Command ID 1 signifies a provisioning request
@@ -157,6 +209,7 @@ def _trigger_device_reprovisioning_with_new_credentials(dut_cloud, sec_tag: int)
157209
158210 dut_cloud .uart .write ("att_button_press 1\r \n " )
159211
212+
160213def _trigger_device_reprovisioning_expecting_no_commands (dut_cloud ):
161214 """
162215 Initiates a reprovisioning check on the device when no new commands are expected from nRF Cloud.
@@ -167,7 +220,9 @@ def _trigger_device_reprovisioning_expecting_no_commands(dut_cloud):
167220 # Update device shadow to indicate a provisioning check (even if no commands are queued)
168221 logger .info ("Updating device shadow to trigger provisioning check." )
169222
170- dut_cloud .cloud .patch_delete_command_entry_from_shadow (device_id = dut_cloud .device_id )
223+ dut_cloud .cloud .patch_delete_command_entry_from_shadow (
224+ device_id = dut_cloud .device_id
225+ )
171226 dut_cloud .cloud .patch_add_provisioning_command_to_shadow (
172227 device_id = dut_cloud .device_id ,
173228 command = 1 , # Command ID 1 typically signifies a provisioning request
@@ -178,8 +233,10 @@ def _trigger_device_reprovisioning_expecting_no_commands(dut_cloud):
178233
179234 dut_cloud .uart .write ("att_button_press 1\r \n " )
180235
236+
181237# --- Test Phases ---
182238
239+
183240def _run_initial_provisioning (dut_cloud , hex_file ):
184241 logger .info ("--- Starting Phase 1: Initial Device Provisioning ---" )
185242
@@ -195,6 +252,7 @@ def _run_initial_provisioning(dut_cloud, hex_file):
195252
196253 logger .info ("--- Phase 1: Initial Device Provisioning Completed Successfully ---" )
197254
255+
198256def _run_reprovisioning (dut_cloud ):
199257 logger .info ("--- Starting Phase 2: Reprovisioning with New Credentials ---" )
200258
@@ -205,7 +263,10 @@ def _run_reprovisioning(dut_cloud):
205263 _wait_for_provisioning_completion_and_cloud_connection (dut_cloud , timeout = 300 )
206264 _verify_device_location_data (dut_cloud )
207265
208- logger .info ("--- Phase 2: Reprovisioning with New Credentials Completed Successfully ---" )
266+ logger .info (
267+ "--- Phase 2: Reprovisioning with New Credentials Completed Successfully ---"
268+ )
269+
209270
210271def _run_reprovisioning_expecting_no_commands (dut_cloud ):
211272 logger .info ("--- Starting Phase 3: Reprovisioning Expecting No Commands ---" )
@@ -214,14 +275,22 @@ def _run_reprovisioning_expecting_no_commands(dut_cloud):
214275
215276 _trigger_device_reprovisioning_expecting_no_commands (dut_cloud )
216277 # Verify the device correctly handles the absence of new provisioning commands
217- dut_cloud .uart .wait_for_str ("cloud: No commands from the nRF Provisioning Service to process" , timeout = 300 )
218- dut_cloud .uart .wait_for_str ("cloud: Connected to Cloud" , timeout = 240 ) # Ensure it reconnects
278+ dut_cloud .uart .wait_for_str (
279+ [
280+ "cloud: No commands from the nRF Provisioning Service to process" ,
281+ "cloud: Connected to Cloud" ,
282+ ],
283+ timeout = 300 ,
284+ start_pos = dut_cloud .uart .get_size (),
285+ )
286+ logger .info (
287+ "--- Phase 3: Reprovisioning Expecting No Commands Completed Successfully ---"
288+ )
219289
220- logger .info ("--- Phase 3: Reprovisioning Expecting No Commands Completed Successfully ---" )
221290
222291# --- Main Test ---
223-
224- def test_device_provisioning (dut_cloud , hex_file ):
292+ @ pytest . mark . parametrize ( "_" , range ( 1 , 3 ))
293+ def test_device_provisioning (_ , dut_cloud , hex_file ):
225294 """
226295 Tests the full device provisioning and reprovisioning lifecycle:
227296 1. Initial provisioning: Flashes, gets attestation token, claims on nRF Cloud, connects.
@@ -231,12 +300,6 @@ def test_device_provisioning(dut_cloud, hex_file):
231300 connects to nRF Cloud Provisioning Service, but finds no new commands to process and establishes
232301 a connection without reprovisioning.
233302 """
234-
235- for i in range (1 , 21 ):
236- logger .info (f"--- Starting Test Iteration: { i } /20 ---" )
237- _run_initial_provisioning (dut_cloud , hex_file )
238- _run_reprovisioning (dut_cloud )
239- _run_reprovisioning_expecting_no_commands (dut_cloud )
240- logger .info (f"--- Completed Test Iteration: { i } /20 ---" )
241-
242- logger .info ("Device provisioning and reprovisioning test completed successfully." )
303+ _run_initial_provisioning (dut_cloud , hex_file )
304+ _run_reprovisioning (dut_cloud )
305+ _run_reprovisioning_expecting_no_commands (dut_cloud )
0 commit comments