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,75 @@ 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+ start_pos = dut_cloud .uart .get_size (),
161+ )
109162 assert values , "Failed to get location data from device."
110163
111164 lat_str , lon_str , acc_str = values
@@ -117,6 +170,7 @@ def _verify_device_location_data(dut_cloud, expected_lat: float = 61.5, expected
117170
118171 logger .info ("Device location data verified." )
119172
173+
120174def _trigger_device_reprovisioning_with_new_credentials (dut_cloud , sec_tag : int ):
121175 """
122176 Initiates the reprovisioning process on the device by:
@@ -127,26 +181,25 @@ def _trigger_device_reprovisioning_with_new_credentials(dut_cloud, sec_tag: int)
127181 logger .info ("Starting reprovisioning process with new credentials..." )
128182
129183 # 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- }
184+ command_json = json .dumps (
185+ {
186+ "description" : "Reprovisioning with new cloud credentials" ,
187+ "request" : {"cloudAccessKeyGeneration" : {"secTag" : sec_tag }},
136188 }
137- } )
189+ )
138190
139191 logger .info (f"Adding reprovisioning command to nRF Cloud: { command_json } " )
140192
141193 dut_cloud .cloud .add_provisioning_command (
142- device_id = dut_cloud .device_id ,
143- command = command_json
194+ device_id = dut_cloud .device_id , command = command_json
144195 )
145196
146197 # Update device shadow to indicate a new provisioning command is available
147198 logger .info ("Updating device shadow to trigger reprovisioning command processing." )
148199
149- dut_cloud .cloud .patch_delete_command_entry_from_shadow (device_id = dut_cloud .device_id )
200+ dut_cloud .cloud .patch_delete_command_entry_from_shadow (
201+ device_id = dut_cloud .device_id
202+ )
150203 dut_cloud .cloud .patch_add_provisioning_command_to_shadow (
151204 device_id = dut_cloud .device_id ,
152205 command = 1 , # Command ID 1 signifies a provisioning request
@@ -157,6 +210,7 @@ def _trigger_device_reprovisioning_with_new_credentials(dut_cloud, sec_tag: int)
157210
158211 dut_cloud .uart .write ("att_button_press 1\r \n " )
159212
213+
160214def _trigger_device_reprovisioning_expecting_no_commands (dut_cloud ):
161215 """
162216 Initiates a reprovisioning check on the device when no new commands are expected from nRF Cloud.
@@ -167,7 +221,9 @@ def _trigger_device_reprovisioning_expecting_no_commands(dut_cloud):
167221 # Update device shadow to indicate a provisioning check (even if no commands are queued)
168222 logger .info ("Updating device shadow to trigger provisioning check." )
169223
170- dut_cloud .cloud .patch_delete_command_entry_from_shadow (device_id = dut_cloud .device_id )
224+ dut_cloud .cloud .patch_delete_command_entry_from_shadow (
225+ device_id = dut_cloud .device_id
226+ )
171227 dut_cloud .cloud .patch_add_provisioning_command_to_shadow (
172228 device_id = dut_cloud .device_id ,
173229 command = 1 , # Command ID 1 typically signifies a provisioning request
@@ -178,8 +234,10 @@ def _trigger_device_reprovisioning_expecting_no_commands(dut_cloud):
178234
179235 dut_cloud .uart .write ("att_button_press 1\r \n " )
180236
237+
181238# --- Test Phases ---
182239
240+
183241def _run_initial_provisioning (dut_cloud , hex_file ):
184242 logger .info ("--- Starting Phase 1: Initial Device Provisioning ---" )
185243
@@ -195,6 +253,7 @@ def _run_initial_provisioning(dut_cloud, hex_file):
195253
196254 logger .info ("--- Phase 1: Initial Device Provisioning Completed Successfully ---" )
197255
256+
198257def _run_reprovisioning (dut_cloud ):
199258 logger .info ("--- Starting Phase 2: Reprovisioning with New Credentials ---" )
200259
@@ -205,7 +264,10 @@ def _run_reprovisioning(dut_cloud):
205264 _wait_for_provisioning_completion_and_cloud_connection (dut_cloud , timeout = 300 )
206265 _verify_device_location_data (dut_cloud )
207266
208- logger .info ("--- Phase 2: Reprovisioning with New Credentials Completed Successfully ---" )
267+ logger .info (
268+ "--- Phase 2: Reprovisioning with New Credentials Completed Successfully ---"
269+ )
270+
209271
210272def _run_reprovisioning_expecting_no_commands (dut_cloud ):
211273 logger .info ("--- Starting Phase 3: Reprovisioning Expecting No Commands ---" )
@@ -214,14 +276,22 @@ def _run_reprovisioning_expecting_no_commands(dut_cloud):
214276
215277 _trigger_device_reprovisioning_expecting_no_commands (dut_cloud )
216278 # 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
279+ dut_cloud .uart .wait_for_str (
280+ [
281+ "cloud: No commands from the nRF Provisioning Service to process" ,
282+ "cloud: Connected to Cloud" ,
283+ ],
284+ timeout = 300 ,
285+ start_pos = dut_cloud .uart .get_size (),
286+ )
287+ logger .info (
288+ "--- Phase 3: Reprovisioning Expecting No Commands Completed Successfully ---"
289+ )
219290
220- logger .info ("--- Phase 3: Reprovisioning Expecting No Commands Completed Successfully ---" )
221291
222292# --- Main Test ---
223-
224- def test_device_provisioning (dut_cloud , hex_file ):
293+ @ pytest . mark . parametrize ( "_" , range ( 1 , 3 ))
294+ def test_device_provisioning (_ , dut_cloud , hex_file ):
225295 """
226296 Tests the full device provisioning and reprovisioning lifecycle:
227297 1. Initial provisioning: Flashes, gets attestation token, claims on nRF Cloud, connects.
@@ -231,12 +301,6 @@ def test_device_provisioning(dut_cloud, hex_file):
231301 connects to nRF Cloud Provisioning Service, but finds no new commands to process and establishes
232302 a connection without reprovisioning.
233303 """
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." )
304+ _run_initial_provisioning (dut_cloud , hex_file )
305+ _run_reprovisioning (dut_cloud )
306+ _run_reprovisioning_expecting_no_commands (dut_cloud )
0 commit comments