Skip to content

Commit df98d91

Browse files
committed
Update and test light_ll ota_management.rs
1 parent 7b94896 commit df98d91

File tree

3 files changed

+314
-2
lines changed

3 files changed

+314
-2
lines changed

rust/src/sdk/ble_app/light_ll/ota_management.rs

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,315 @@ pub fn rf_ota_save_data(data: &[u8]) -> OtaState
139139
return OtaState::Error;
140140
}
141141
}
142+
143+
#[cfg(test)]
144+
mod tests {
145+
use super::*;
146+
use mry::Any;
147+
148+
// Import mock functions from their original modules
149+
use crate::sdk::drivers::flash::{mock_flash_write_page, mock_flash_read_page};
150+
151+
/// Helper function to reset global state for test isolation
152+
fn reset_ota_state() {
153+
OTA_UPDATE_CURRENT_FLASH_ADDRESS.set(0);
154+
}
155+
156+
// ================================================================================
157+
// Tests for rf_ota_save_data function
158+
// ================================================================================
159+
160+
/// Tests rf_ota_save_data with basic functionality.
161+
///
162+
/// Verifies that the function correctly calls flash write and read operations
163+
/// and handles the address calculation correctly.
164+
#[test]
165+
#[mry::lock(flash_write_page, flash_read_page)]
166+
fn test_rf_ota_save_data_basic_functionality() {
167+
// Setup mocks for basic flash operations
168+
mock_flash_write_page(Any, Any, Any).returns(());
169+
mock_flash_read_page(Any, Any, Any).returns(());
170+
171+
reset_ota_state();
172+
173+
let test_data = [0x01, 0x02, 0x03, 0x04];
174+
let initial_address = OTA_UPDATE_CURRENT_FLASH_ADDRESS.get();
175+
176+
// Call the function under test
177+
let result = rf_ota_save_data(&test_data);
178+
179+
// Verify flash operations were called with correct parameters
180+
let expected_addr = initial_address + FLASH_ADR_LIGHT_NEW_FW;
181+
mock_flash_write_page(expected_addr, test_data.len() as u32, test_data.as_ptr()).assert_called(1);
182+
mock_flash_read_page(expected_addr, test_data.len() as u32, Any).assert_called(1);
183+
184+
// Function should return some valid OtaState (since mocks don't modify buffers)
185+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
186+
"Function should return a valid OtaState");
187+
}
188+
189+
/// Tests rf_ota_save_data with empty data.
190+
///
191+
/// Verifies that the function handles empty data gracefully.
192+
#[test]
193+
#[mry::lock(flash_write_page, flash_read_page)]
194+
fn test_rf_ota_save_data_empty_data() {
195+
// Setup mocks for empty data operations
196+
mock_flash_write_page(Any, Any, Any).returns(());
197+
mock_flash_read_page(Any, Any, Any).returns(());
198+
199+
reset_ota_state();
200+
201+
let empty_data: &[u8] = &[];
202+
let initial_address = OTA_UPDATE_CURRENT_FLASH_ADDRESS.get();
203+
204+
let result = rf_ota_save_data(empty_data);
205+
206+
// Verify operations were called with zero length
207+
let expected_addr = initial_address + FLASH_ADR_LIGHT_NEW_FW;
208+
mock_flash_write_page(expected_addr, 0, empty_data.as_ptr()).assert_called(1);
209+
mock_flash_read_page(expected_addr, 0, Any).assert_called(1);
210+
211+
// Empty data should succeed since verification will compare empty slices
212+
assert_eq!(result, OtaState::Continue, "Should return Continue for empty data");
213+
214+
// Flash address should advance by the length of data (which is 0 for empty data)
215+
assert_eq!(
216+
OTA_UPDATE_CURRENT_FLASH_ADDRESS.get(),
217+
initial_address + empty_data.len() as u32,
218+
"Flash address should advance by data length (0 for empty data)"
219+
);
220+
}
221+
222+
/// Tests rf_ota_save_data with single byte.
223+
///
224+
/// Verifies that the function correctly handles minimal data sizes.
225+
#[test]
226+
#[mry::lock(flash_write_page, flash_read_page)]
227+
fn test_rf_ota_save_data_single_byte() {
228+
// Setup mocks for single byte operation
229+
mock_flash_write_page(Any, Any, Any).returns(());
230+
mock_flash_read_page(Any, Any, Any).returns(());
231+
232+
reset_ota_state();
233+
234+
let single_byte = [0x42];
235+
let initial_address = OTA_UPDATE_CURRENT_FLASH_ADDRESS.get();
236+
237+
let result = rf_ota_save_data(&single_byte);
238+
239+
// Verify operations called with length 1
240+
let expected_addr = initial_address + FLASH_ADR_LIGHT_NEW_FW;
241+
mock_flash_write_page(expected_addr, 1, single_byte.as_ptr()).assert_called(1);
242+
mock_flash_read_page(expected_addr, 1, Any).assert_called(1);
243+
244+
// Function should execute successfully
245+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
246+
"Function should return a valid OtaState for single byte");
247+
}
248+
249+
/// Tests rf_ota_save_data with maximum typical packet size.
250+
///
251+
/// Verifies that the function handles larger data packets correctly.
252+
#[test]
253+
#[mry::lock(flash_write_page, flash_read_page)]
254+
fn test_rf_ota_save_data_max_packet_size() {
255+
// Setup mocks for 16-byte packet (typical maximum)
256+
mock_flash_write_page(Any, Any, Any).returns(());
257+
mock_flash_read_page(Any, Any, Any).returns(());
258+
259+
reset_ota_state();
260+
261+
// Create 16-byte test packet with sequential pattern
262+
let max_packet: Vec<u8> = (0..16).map(|i| (i & 0xFF) as u8).collect();
263+
let initial_address = OTA_UPDATE_CURRENT_FLASH_ADDRESS.get();
264+
265+
let result = rf_ota_save_data(&max_packet);
266+
267+
// Verify operations called with correct length
268+
let expected_addr = initial_address + FLASH_ADR_LIGHT_NEW_FW;
269+
mock_flash_write_page(expected_addr, max_packet.len() as u32, max_packet.as_ptr()).assert_called(1);
270+
mock_flash_read_page(expected_addr, max_packet.len() as u32, Any).assert_called(1);
271+
272+
// Function should execute successfully
273+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
274+
"Function should return a valid OtaState for max packet size");
275+
}
276+
277+
/// Tests rf_ota_save_data with different flash addresses.
278+
///
279+
/// Verifies that the function correctly calculates flash addresses
280+
/// based on the current offset plus the base address.
281+
#[test]
282+
#[mry::lock(flash_write_page, flash_read_page)]
283+
fn test_rf_ota_save_data_address_calculation() {
284+
// Setup mocks
285+
mock_flash_write_page(Any, Any, Any).returns(());
286+
mock_flash_read_page(Any, Any, Any).returns(());
287+
288+
reset_ota_state();
289+
290+
// Set a specific offset for testing address calculation
291+
let test_offset = 0x1000;
292+
OTA_UPDATE_CURRENT_FLASH_ADDRESS.set(test_offset);
293+
294+
let test_data = [0xAA, 0xBB];
295+
296+
let result = rf_ota_save_data(&test_data);
297+
298+
// Verify address calculation: offset + base address
299+
let expected_flash_addr = test_offset + FLASH_ADR_LIGHT_NEW_FW;
300+
mock_flash_write_page(expected_flash_addr, test_data.len() as u32, test_data.as_ptr()).assert_called(1);
301+
mock_flash_read_page(expected_flash_addr, test_data.len() as u32, Any).assert_called(1);
302+
303+
// Function should execute successfully
304+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
305+
"Function should return a valid OtaState with custom offset");
306+
}
307+
308+
/// Tests rf_ota_save_data operation sequence.
309+
///
310+
/// Verifies that flash write is called before flash read, ensuring
311+
/// the write-verify cycle is performed in the correct order.
312+
#[test]
313+
#[mry::lock(flash_write_page, flash_read_page)]
314+
fn test_rf_ota_save_data_operation_sequence() {
315+
use core::sync::atomic::{AtomicU32, Ordering};
316+
317+
// Track operation sequence using atomic counters
318+
static WRITE_CALL_COUNT: AtomicU32 = AtomicU32::new(0);
319+
static READ_CALL_COUNT: AtomicU32 = AtomicU32::new(0);
320+
321+
// Reset counters
322+
WRITE_CALL_COUNT.store(0, Ordering::Relaxed);
323+
READ_CALL_COUNT.store(0, Ordering::Relaxed);
324+
325+
// Setup mocks to track call order
326+
mock_flash_write_page(Any, Any, Any).returns_with(|_addr, _len, _buf| {
327+
let count = WRITE_CALL_COUNT.fetch_add(1, Ordering::Relaxed);
328+
// Verify write is called before any reads
329+
assert_eq!(READ_CALL_COUNT.load(Ordering::Relaxed), count,
330+
"Write {} should occur before read {}", count, count);
331+
});
332+
333+
mock_flash_read_page(Any, Any, Any).returns_with(|_addr, _len, _buf| {
334+
let count = READ_CALL_COUNT.fetch_add(1, Ordering::Relaxed);
335+
// Verify write was called first
336+
assert_eq!(WRITE_CALL_COUNT.load(Ordering::Relaxed), count + 1,
337+
"Read {} should occur after write {}", count, count);
338+
});
339+
340+
reset_ota_state();
341+
342+
let test_data = [0x77, 0x77, 0x77];
343+
let result = rf_ota_save_data(&test_data);
344+
345+
// Verify both operations were called exactly once in correct order
346+
assert_eq!(WRITE_CALL_COUNT.load(Ordering::Relaxed), 1, "Write should be called exactly once");
347+
assert_eq!(READ_CALL_COUNT.load(Ordering::Relaxed), 1, "Read should be called exactly once");
348+
349+
// Function should execute successfully
350+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
351+
"Function should return a valid OtaState");
352+
}
353+
354+
/// Tests rf_ota_save_data with multiple sequential calls.
355+
///
356+
/// Verifies that multiple consecutive packets are handled correctly
357+
/// and that the flash address advances properly.
358+
#[test]
359+
#[mry::lock(flash_write_page, flash_read_page)]
360+
fn test_rf_ota_save_data_sequential_packets() {
361+
// Setup mocks that will return Error (since we can't easily mock buffer verification)
362+
mock_flash_write_page(Any, Any, Any).returns(());
363+
mock_flash_read_page(Any, Any, Any).returns(());
364+
365+
reset_ota_state();
366+
367+
let packets = [
368+
vec![0x00, 0x01, 0x02], // First packet (3 bytes)
369+
vec![0x03, 0x04], // Second packet (2 bytes)
370+
vec![0x05], // Third packet (1 byte)
371+
];
372+
373+
let initial_address = OTA_UPDATE_CURRENT_FLASH_ADDRESS.get();
374+
375+
// Process each packet sequentially
376+
for (i, packet) in packets.iter().enumerate() {
377+
let result = rf_ota_save_data(packet);
378+
379+
// Function should execute without crashing
380+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
381+
"Packet {} should return valid OtaState", i);
382+
}
383+
384+
// Verify correct number of operations (one write and one read per packet)
385+
mock_flash_write_page(Any, Any, Any).assert_called(packets.len());
386+
mock_flash_read_page(Any, Any, Any).assert_called(packets.len());
387+
}
388+
389+
/// Tests rf_ota_save_data with various data patterns.
390+
///
391+
/// Verifies that the function correctly handles various data patterns
392+
/// that might cause issues (all zeros, all ones, alternating patterns).
393+
#[test]
394+
#[mry::lock(flash_write_page, flash_read_page)]
395+
fn test_rf_ota_save_data_edge_case_patterns() {
396+
// Setup mocks for pattern testing
397+
mock_flash_write_page(Any, Any, Any).returns(());
398+
mock_flash_read_page(Any, Any, Any).returns(());
399+
400+
reset_ota_state();
401+
402+
let test_patterns = [
403+
vec![0x00, 0x00, 0x00, 0x00], // All zeros
404+
vec![0xFF, 0xFF, 0xFF, 0xFF], // All ones
405+
vec![0xAA, 0x55, 0xAA, 0x55], // Alternating pattern
406+
vec![0x00, 0xFF, 0x00, 0xFF], // Alternating extreme values
407+
];
408+
409+
for (i, pattern) in test_patterns.iter().enumerate() {
410+
let result = rf_ota_save_data(pattern);
411+
412+
// Each pattern should be processed without crashing
413+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
414+
"Pattern {} should return valid OtaState", i);
415+
}
416+
417+
// Verify all patterns were processed
418+
mock_flash_write_page(Any, Any, Any).assert_called(test_patterns.len());
419+
mock_flash_read_page(Any, Any, Any).assert_called(test_patterns.len());
420+
}
421+
422+
/// Tests rf_ota_save_data buffer boundary conditions.
423+
///
424+
/// Verifies that the temporary buffer (16 bytes) is used correctly
425+
/// and doesn't cause issues with different data sizes.
426+
#[test]
427+
#[mry::lock(flash_write_page, flash_read_page)]
428+
fn test_rf_ota_save_data_buffer_boundaries() {
429+
// Setup mocks to test the 16-byte temporary buffer handling
430+
mock_flash_write_page(Any, Any, Any).returns(());
431+
mock_flash_read_page(Any, Any, Any).returns(());
432+
433+
reset_ota_state();
434+
435+
// Test sizes around the 16-byte buffer boundary
436+
let test_sizes = [1, 8, 15, 16]; // Various sizes up to buffer limit
437+
438+
for &size in &test_sizes {
439+
// Create test data
440+
let test_data: Vec<u8> = (0..size).map(|i| (0x80 | i) as u8).collect();
441+
442+
let result = rf_ota_save_data(&test_data);
443+
444+
// Verify function executes for all buffer sizes
445+
assert!(matches!(result, OtaState::Continue | OtaState::Error),
446+
"Size {} should return valid OtaState", size);
447+
}
448+
449+
// Verify all sizes were processed
450+
mock_flash_write_page(Any, Any, Any).assert_called(test_sizes.len());
451+
mock_flash_read_page(Any, Any, Any).assert_called(test_sizes.len());
452+
}
453+
}

rust/src/version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pub static BUILD_VERSION: u32 = 3279;
1+
pub static BUILD_VERSION: u32 = 3280;

sdk/version.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
.equ BUILD_VERSION,3279
1+
.equ BUILD_VERSION,3280
22
.equ XTAL_16MHZ,0

0 commit comments

Comments
 (0)