From d18804f906b770869c814c9f1746d1d20538e3c2 Mon Sep 17 00:00:00 2001 From: Jakub Andrysek Date: Wed, 10 Dec 2025 11:03:12 +0100 Subject: [PATCH 1/4] feat(sdcard): add validation diagrams and SD Card tests --- tests/validation/sdcard/diagram.esp32.json | 48 ++ tests/validation/sdcard/diagram.esp32c3.json | 34 ++ tests/validation/sdcard/diagram.esp32c6.json | 47 ++ tests/validation/sdcard/diagram.esp32s2.json | 41 ++ tests/validation/sdcard/diagram.esp32s3.json | 34 ++ tests/validation/sdcard/sdcard.ino | 594 +++++++++++++++++++ tests/validation/sdcard/test_sdcard.py | 2 + 7 files changed, 800 insertions(+) create mode 100644 tests/validation/sdcard/diagram.esp32.json create mode 100644 tests/validation/sdcard/diagram.esp32c3.json create mode 100644 tests/validation/sdcard/diagram.esp32c6.json create mode 100644 tests/validation/sdcard/diagram.esp32s2.json create mode 100644 tests/validation/sdcard/diagram.esp32s3.json create mode 100644 tests/validation/sdcard/sdcard.ino create mode 100644 tests/validation/sdcard/test_sdcard.py diff --git a/tests/validation/sdcard/diagram.esp32.json b/tests/validation/sdcard/diagram.esp32.json new file mode 100644 index 00000000000..1f22887b07c --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "author": "Jakub Andrysek", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-devkit-c-v4", + "id": "esp32", + "top": 27.66, + "left": -12.7, + "rotate": 270, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -74.23, + "left": 16.27, + "rotate": 90, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": 237.23, + "left": -2.27, + "rotate": 270, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:18", "green", [ "v67.2", "h-19.19" ] ], + [ "sd1:GND", "esp32:GND.1", "black", [ "v57.6", "h115.09", "v134.4", "h-96" ] ], + [ "sd1:DO", "esp32:19", "green", [ "v76.8", "h-48.11" ] ], + [ "sd1:DI", "esp32:23", "green", [ "v48", "h-67.11" ] ], + [ "sd1:CS", "esp32:5", "green", [ "v57.6", "h19.14" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v38.4", "h-124.94", "v144.15" ] ], + [ "sd2:SCK", "esp32:25", "green", [ "v0" ] ], + [ "sd2:DO", "esp32:26", "green", [ "v-38.4", "h28.91" ] ], + [ "sd2:DI", "esp32:27", "green", [ "v0" ] ], + [ "sd2:CS", "esp32:14", "green", [ "v0" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "v-28.8", "h-57.46" ] ], + [ "esp32:GND.1", "sd2:GND", "black", [ "v38.25", "h-57.6" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32c3.json b/tests/validation/sdcard/diagram.esp32c3.json new file mode 100644 index 00000000000..276cca7d845 --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32c3.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "author": "P-R-O-C-H-Y", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-c3-devkitm-1", + "id": "esp32", + "top": -57.6, + "left": -177.56, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": 18.77, + "left": 4.33, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:4", "green", [ "v0.01", "h-142.56" ] ], + [ "sd1:DO", "esp32:5", "green", [ "h-67.2", "v-28.91", "h-75.36" ] ], + [ "sd1:DI", "esp32:6", "green", [ "v0.09", "h-142.56" ] ], + [ "sd1:CS", "esp32:7", "green", [ "h-57.66", "v-9.3" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v11", "h-124.94", "v132.15" ] ], + [ "sd1:VCC", "esp32:3V3.2", "red", [ "h-28.8", "v-124.94", "h-172.8", "v57.6", "h18.22" ] ], + [ "sd1:GND", "esp32:GND.7", "black", [ "h0" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32c6.json b/tests/validation/sdcard/diagram.esp32c6.json new file mode 100644 index 00000000000..8586fbd9af7 --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32c6.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "author": "P-R-O-C-H-Y", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-c6-devkitc-1", + "id": "esp32", + "top": -52.31, + "left": -178.28, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -0.43, + "left": 61.93, + "rotate": 180, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": -86.83, + "left": 61.93, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:21", "green", [ "v0.01", "h-96", "v-28.8" ] ], + [ "sd1:DO", "esp32:20", "green", [ "v-0.11", "h-57.6", "v-9.6" ] ], + [ "sd1:DI", "esp32:19", "green", [ "v0.09", "h-86.4", "v38.4" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v-0.14", "h-48", "v-124.8", "h-192" ] ], + [ "esp32:18", "sd2:CS", "green", [ "h56.5", "v-144" ] ], + [ "esp32:19", "sd2:DI", "green", [ "h66.1", "v-124.8" ] ], + [ "esp32:21", "sd2:SCK", "green", [ "h75.7", "v-86.4" ] ], + [ "sd1:GND", "esp32:GND.2", "black", [ "h-19.2", "v-0.11" ] ], + [ "esp32:GND.2", "sd2:GND", "black", [ "h133.3", "v-153.6" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "h-48", "v-38.54", "h-192" ] ], + [ "esp32:20", "sd2:DO", "green", [ "h94.9", "v-76.8", "h-9.6" ] ], + [ "sd1:CS", "esp32:9", "green", [ "h-105.6", "v-0.06" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32s2.json b/tests/validation/sdcard/diagram.esp32s2.json new file mode 100644 index 00000000000..3a27e834fb0 --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32s2.json @@ -0,0 +1,41 @@ +{ + "version": 1, + "author": "P-R-O-C-H-Y", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-s2-devkitm-1", + "id": "esp32", + "top": -61.91, + "left": -177.83, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": 9.17, + "left": 23.53, + "rotate": 180, + "attrs": {} + }, + { "type": "wokwi-microsd-card", "id": "sd2", "top": -57.37, "left": -345.53, "attrs": {} } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:GND", "esp32:GND.1", "black", [ "v-0.11", "h-96", "v115.2", "h-105.33" ] ], + [ "sd1:DI", "esp32:23", "green", [ "v14", "h6" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v-0.14", "h-38.4", "v-105.6", "h-162.93" ] ], + [ "sd1:CS", "esp32:34", "green", [ "h-86.4", "v-0.06" ] ], + [ "sd1:DI", "esp32:35", "green", [ "h-76.8", "v0.09" ] ], + [ "sd1:DO", "esp32:37", "green", [ "h0" ] ], + [ "sd1:SCK", "esp32:36", "green", [ "h-48", "v28.81" ] ], + [ "esp32:1", "sd2:SCK", "green", [ "h0" ] ], + [ "esp32:2", "sd2:DO", "green", [ "h-48", "v-19.2" ] ], + [ "esp32:3", "sd2:DI", "green", [ "h0" ] ], + [ "esp32:8", "sd2:CS", "green", [ "h-19.2", "v-38.46" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "h28.8", "v-57.46", "h67.2" ] ], + [ "sd2:GND", "esp32:GND.1", "black", [ "h38.4", "v0.11" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32s3.json b/tests/validation/sdcard/diagram.esp32s3.json new file mode 100644 index 00000000000..14acb50500e --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32s3.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": 0, "left": 0, "attrs": {} }, + { "type": "wokwi-microsd-card", "id": "sd1", "top": 144.23, "left": -201.53, "attrs": {} }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": 18.77, + "left": 186.73, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp:TX", "$serialMonitor:RX", "", [] ], + [ "esp:RX", "$serialMonitor:TX", "", [] ], + [ "sd1:CS", "esp:10", "green", [ "h57.6", "v-38.16" ] ], + [ "sd1:DI", "esp:11", "green", [ "h48", "v-19.11" ] ], + [ "sd1:SCK", "esp:12", "green", [ "h38.4", "v9.77" ] ], + [ "sd1:GND", "esp:GND.1", "black", [ "h9.6", "v57.71" ] ], + [ "sd1:DO", "esp:13", "green", [ "h67.2", "v38.69" ] ], + [ "sd1:VCC", "esp:3V3.2", "red", [ "h28.8", "v-143.86" ] ], + [ "sd2:GND", "esp:GND.3", "black", [ "h-19.2", "v86.47" ] ], + [ "sd2:VCC", "esp:3V3.2", "red", [ "h-19.2", "v-48.14", "h0", "v-86.4", "h-172.8", "v57.6" ] ], + [ "sd2:SCK", "esp:1", "green", [ "h-57.6", "v-38.39" ] ], + [ "sd2:DO", "esp:2", "green", [ "h-48", "v-9.71" ] ], + [ "sd2:DI", "esp:3", "green", [ "h-67.2", "v-47.91", "h-144", "v153.78" ] ], + [ "sd2:CS", "esp:8", "green", [ "h-57.6", "v-48.06", "h-144", "v153.78" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/sdcard.ino b/tests/validation/sdcard/sdcard.ino new file mode 100644 index 00000000000..a449148f67b --- /dev/null +++ b/tests/validation/sdcard/sdcard.ino @@ -0,0 +1,594 @@ +#include "FS.h" +#include "SD.h" +#include "SPI.h" +#include // Needed for custom SD instance +#include +#include +#include +#include +#include + +#if defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) +#define SPI_COUNT_MAX 2 +#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) +#define SPI_COUNT_MAX 1 +#else +#define SPI_COUNT_MAX 4 // ESP32 +#endif + +#define MAX_FILES 5 + +class SPITestConfig { +public: + std::string name; + const char *mountpoint; + uint8_t spi_num; + int8_t sck; + int8_t miso; + int8_t mosi; + int8_t ss; + std::unique_ptr spi; + std::unique_ptr sd; + bool mounted = false; + + SPITestConfig(std::string name, const char *mountpoint, uint8_t spi_num, int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + : name(name), mountpoint(mountpoint), spi_num(spi_num), sck(sck), miso(miso), mosi(mosi), ss(ss) { + Serial.printf("Creating SPITestConfig [%s] on SPI bus %d: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", name.c_str(), spi_num, sck, miso, mosi, ss); + } + + void begin(uint8_t max_files = MAX_FILES, bool format_if_empty = false) { + spi = std::make_unique(spi_num); + sd = std::make_unique(FSImplPtr(new VFSImpl())); + + TEST_ASSERT_TRUE_MESSAGE(spi->begin(sck, miso, mosi, ss), "Failed to begin SPI"); + TEST_ASSERT_TRUE_MESSAGE(sd->begin(ss, *spi, 4000000, mountpoint, max_files, format_if_empty), "Failed to mount SD card"); + mounted = true; + } + + void end() { + sd->end(); + mounted = false; + spi->end(); + spi.reset(); + sd.reset(); + Serial.printf("SPI and SD card for [%s] cleaned up\n", name.c_str()); + } +}; + +std::vector> spiTestConfigs; +using SpiTestFunction = std::function; + +void run_init_continuous(SpiTestFunction test_function, uint8_t max_files, bool format_if_empty = false) { + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + Serial.printf("Running test with SPI configuration: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", config.sck, config.miso, config.mosi, config.ss); + config.begin(max_files, format_if_empty); + test_function(config); + config.end(); + } +} + +void run_first_init(SpiTestFunction test_function, uint8_t max_files, bool format_if_empty = false) { + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.begin(max_files, format_if_empty); + } + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + test_function(config); + } + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.end(); + } +} + +void test_nonexistent_spi_interface(void) { + // Attempt to create a SPIClass with an invalid SPI number + SPIClass spiNotExist(SPI_COUNT_MAX); + TEST_ASSERT_FALSE_MESSAGE(spiNotExist.begin(), "SPIClass should not be initialized with an invalid SPI number"); + spiNotExist.end(); +} + +void run_multiple_ways(SpiTestFunction test_function, uint8_t max_files = MAX_FILES, bool format_if_empty = false) { + run_first_init(test_function, max_files, format_if_empty); + run_init_continuous(test_function, max_files, format_if_empty); + test_nonexistent_spi_interface(); +} + +void test_sd_basic(void) { + Serial.println("Running test_sd_basic"); + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.begin(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/"), "Root directory should exist"); + config.end(); + } +} + +void test_sd_dir(void) { + Serial.println("Running test_sd_dir"); + run_multiple_ways([](SPITestConfig &config) { + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir"), "Failed to create directory /testdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/testdir"), "Directory /testdir should exist after creation"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir"), "Failed to remove directory /testdir"); + TEST_ASSERT_FALSE_MESSAGE(config.sd->exists("/testdir"), "Directory /testdir should not exist after removal"); + }); +} + +void test_sd_file_operations(void) { + Serial.println("Running test_sd_file_operations"); + run_multiple_ways([](SPITestConfig &config) { + // Write a test file + File testFile = config.sd->open("/testfile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(testFile, "Failed to open file for writing"); + TEST_ASSERT_TRUE_MESSAGE(testFile.print("Hello, SD Card!"), "Failed to write to file"); + testFile.close(); + + // Read the test file + testFile = config.sd->open("/testfile.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(testFile, "Failed to open file for reading"); + String content = testFile.readString(); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Hello, SD Card!", content.c_str(), "File content does not match expected value"); + testFile.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testfile.txt"), "Failed to remove test file"); + }); +} + +void test_sd_open_limit(void) { + Serial.println("Running test_sd_open_limit"); + + run_init_continuous( + [](SPITestConfig &config) { + Serial.printf("Testing file open limit with SPI configuration: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", config.sck, config.miso, config.mosi, config.ss); + + // Open multiple files to test the limit + File file1 = config.sd->open("/file1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to open file1 for writing"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("File 1 content"), "Failed to write to file1"); + + File file2 = config.sd->open("/file2.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to open file2 for writing"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("File 2 content"), "Failed to write to file2"); + + // Attempt to open a third file, which should fail due to the limit + File file3 = config.sd->open("/file3.txt", FILE_WRITE); + TEST_ASSERT_FALSE_MESSAGE(file3, "Third file should not be opened due to max_files=2 limit"); + + // Clean up files + file1.close(); + file2.close(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file1.txt"), "Failed to remove file1"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file2.txt"), "Failed to remove file2"); + if (file3) { + file3.close(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file3.txt"), "Failed to remove file3"); + } + }, + 2 // max_files set to 2 for this test + ); +} + +void test_sd_directory_listing(void) { + Serial.println("Running test_sd_directory_listing"); + + run_multiple_ways([](SPITestConfig &config) { + // Create test directory structure + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir"), "Failed to create /testdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir/subdir"), "Failed to create /testdir/subdir"); + + // Create test files + File file1 = config.sd->open("/testdir/file1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to create file1.txt"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("Content of file 1"), "Failed to write to file1.txt"); + file1.close(); + + File file2 = config.sd->open("/testdir/file2.dat", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to create file2.dat"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("Content of file 2"), "Failed to write to file2.dat"); + file2.close(); + + File subfile = config.sd->open("/testdir/subdir/subfile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(subfile, "Failed to create subfile.txt"); + TEST_ASSERT_TRUE_MESSAGE(subfile.print("Content of subfile"), "Failed to write to subfile.txt"); + subfile.close(); + + // Test directory listing + File dir = config.sd->open("/testdir"); + TEST_ASSERT_TRUE_MESSAGE(dir, "Failed to open /testdir for listing"); + TEST_ASSERT_TRUE_MESSAGE(dir.isDirectory(), "/testdir should be a directory"); + + int fileCount = 0; + int dirCount = 0; + File entry = dir.openNextFile(); + while (entry) { + if (entry.isDirectory()) { + dirCount++; + Serial.printf("Found directory: %s\n", entry.name()); + } else { + fileCount++; + Serial.printf("Found file: %s (size: %d bytes)\n", entry.name(), entry.size()); + } + entry.close(); + entry = dir.openNextFile(); + } + dir.close(); + + TEST_ASSERT_EQUAL_MESSAGE(2, fileCount, "Should find 2 files in /testdir"); + TEST_ASSERT_EQUAL_MESSAGE(1, dirCount, "Should find 1 subdirectory in /testdir"); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/subdir/subfile.txt"), "Failed to remove subfile.txt"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir/subdir"), "Failed to remove subdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/file1.txt"), "Failed to remove file1.txt"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/file2.dat"), "Failed to remove file2.dat"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir"), "Failed to remove testdir"); + }); +} + +void test_sd_file_size_operations(void) { + Serial.println("Running test_sd_file_size_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *testData = "This is a test file with specific content for size testing."; + size_t expectedSize = strlen(testData); + + // Create file with known content + File file = config.sd->open("/sizefile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create sizefile.txt"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.print(testData), "Failed to write expected amount of data"); + file.close(); + + // Test file size + file = config.sd->open("/sizefile.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open sizefile.txt for reading"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.size(), "File size doesn't match expected size"); + + // Test available() method + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.available(), "Available bytes don't match file size"); + + // Test position tracking + TEST_ASSERT_EQUAL_MESSAGE(0, file.position(), "Initial position should be 0"); + + char buffer[20]; + file.readBytes(buffer, 10); + TEST_ASSERT_EQUAL_MESSAGE(10, file.position(), "Position should be 10 after reading 10 bytes"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize - 10, file.available(), "Available should decrease after reading"); + + // Test seek + TEST_ASSERT_TRUE_MESSAGE(file.seek(0), "Failed to seek to beginning"); + TEST_ASSERT_EQUAL_MESSAGE(0, file.position(), "Position should be 0 after seek"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.available(), "Available should be full size after seek to start"); + + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/sizefile.txt"), "Failed to remove sizefile.txt"); + }); +} + +void test_sd_nested_directories(void) { + Serial.println("Running test_sd_nested_directories"); + + run_multiple_ways([](SPITestConfig &config) { + // Create nested directory structure + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1"), "Failed to create /level1"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1/level2"), "Failed to create /level1/level2"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1/level2/level3"), "Failed to create /level1/level2/level3"); + + // Create files at different levels + File file1 = config.sd->open("/level1/file_level1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to create file at level1"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("Level 1 content"), "Failed to write to level1 file"); + file1.close(); + + File file2 = config.sd->open("/level1/level2/file_level2.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to create file at level2"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("Level 2 content"), "Failed to write to level2 file"); + file2.close(); + + File file3 = config.sd->open("/level1/level2/level3/file_level3.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file3, "Failed to create file at level3"); + TEST_ASSERT_TRUE_MESSAGE(file3.print("Level 3 content"), "Failed to write to level3 file"); + file3.close(); + + // Verify files exist at all levels + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/file_level1.txt"), "Level 1 file should exist"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/level2/file_level2.txt"), "Level 2 file should exist"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/level2/level3/file_level3.txt"), "Level 3 file should exist"); + + // Test reading from nested files + File readFile = config.sd->open("/level1/level2/level3/file_level3.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(readFile, "Failed to open level3 file for reading"); + String content = readFile.readString(); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Level 3 content", content.c_str(), "Level 3 file content mismatch"); + readFile.close(); + + // Clean up from deepest to shallowest + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/level2/level3/file_level3.txt"), "Failed to remove level3 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1/level2/level3"), "Failed to remove level3 dir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/level2/file_level2.txt"), "Failed to remove level2 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1/level2"), "Failed to remove level2 dir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/file_level1.txt"), "Failed to remove level1 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1"), "Failed to remove level1 dir"); + }); +} + +void test_sd_file_count_in_directoryOLD(void) { + Serial.println("Running test_sd_file_count_in_directory"); + + run_multiple_ways([](SPITestConfig &config) { + // Create test directory + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/counttest"), "Failed to create /counttest"); + + // Function to count files in directory + auto countFilesInDir = [&config](const char *dirPath) -> int { + File dir = config.sd->open(dirPath); + if (!dir || !dir.isDirectory()) { + return -1; + } + + int count = 0; + File entry = dir.openNextFile(); + while (entry) { + if (!entry.isDirectory()) { + count++; + } + entry.close(); + entry = dir.openNextFile(); + } + dir.close(); + return count; + }; + + // Initially should be empty + TEST_ASSERT_EQUAL_MESSAGE(0, countFilesInDir("/counttest"), "Directory should initially be empty"); + + // Create files one by one and verify count + for (int i = 1; i <= 5; i++) { + char filename[50]; + snprintf(filename, sizeof(filename), "/counttest/file_%d.txt", i); + + File file = config.sd->open(filename, FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create numbered file"); + file.printf("Content of file %d", i); + file.close(); + + TEST_ASSERT_EQUAL_MESSAGE(i, countFilesInDir("/counttest"), "File count mismatch after creating file"); + } + + // Create a subdirectory (should not affect file count) + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/counttest/subdir"), "Failed to create subdirectory"); + TEST_ASSERT_EQUAL_MESSAGE(5, countFilesInDir("/counttest"), "File count should not change with subdirectory"); + + // Remove files one by one and verify count + for (int i = 5; i >= 1; i--) { + char filename[50]; + snprintf(filename, sizeof(filename), "/counttest/file_%d.txt", i); + + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove numbered file"); + TEST_ASSERT_EQUAL_MESSAGE(i - 1, countFilesInDir("/counttest"), "File count mismatch after removing file"); + } + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/counttest/subdir"), "Failed to remove subdirectory"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/counttest"), "Failed to remove test directory"); + }); +} + +void test_sd_file_count_in_directory(void) { + Serial.println("Running test_sd_file_count_in_directory"); + run_multiple_ways([](SPITestConfig &config) { + const char *fileBasePath = "/dir/a/b"; + const int numFiles = 5; + { + // create nested directories + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/dir"), "mkdir /dir failed"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/dir/a"), "mkdir /dir/a failed"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir(fileBasePath), "mkdir /dir/a/b failed"); + } + + { + for (int i = 0; i < numFiles; ++i) { + String path = String(fileBasePath) + String("/file") + String(i) + String(".txt"); + File f = config.sd->open(path.c_str(), FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(f, ("open " + path + " failed").c_str()); + f.print("data:" + String(i)); + f.close(); + } + } + + { + File d = config.sd->open(fileBasePath); + TEST_ASSERT_TRUE_MESSAGE(d && d.isDirectory(), "open(/dir/a/b) not a directory"); + + auto getExpectedFile = [fileBasePath](int i) -> std::pair { + return {String(fileBasePath) + "/file" + String(i) + ".txt", "data:" + String(i)}; + }; + + bool found[numFiles] = {false}; + int count = 0; + + while (true) { + File e = d.openNextFile(); + if (!e) { + break; + } + + String path = e.path(); + String content = e.readString(); + bool matched = false; + + for (int i = 0; i < numFiles; ++i) { + if (!found[i]) { + auto [expectedPath, expectedContent] = getExpectedFile(i); + if (path == expectedPath) { + TEST_ASSERT_EQUAL_STRING_MESSAGE(expectedContent.c_str(), content.c_str(), "File content mismatch"); + found[i] = true; + matched = true; + break; + } + } + } + + TEST_ASSERT_TRUE_MESSAGE(matched, ("Unexpected file found: " + path).c_str()); + count++; + e.close(); + } + + d.close(); + TEST_ASSERT_EQUAL_INT_MESSAGE(numFiles, count, "File count mismatch in directory listing"); + + for (int i = 0; i < numFiles; ++i) { + auto [expectedPath, _] = getExpectedFile(i); + TEST_ASSERT_TRUE_MESSAGE(found[i], ("Expected file not found: " + expectedPath).c_str()); + } + } + }); +} + +void test_sd_file_append_operations(void) { + Serial.println("Running test_sd_file_append_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *filename = "/appendtest.txt"; + + // Create initial file + File file = config.sd->open(filename, FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create file for append test"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 1\n"), "Failed to write initial line"); + file.close(); + + // Append to file + file = config.sd->open(filename, FILE_APPEND); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open file for append"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 2\n"), "Failed to append second line"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 3\n"), "Failed to append third line"); + file.close(); + + // Verify file contents + file = config.sd->open(filename, FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open file for reading"); + + String line1 = file.readStringUntil('\n'); + String line2 = file.readStringUntil('\n'); + String line3 = file.readStringUntil('\n'); + + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 1", line1.c_str(), "First line mismatch"); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 2", line2.c_str(), "Second line mismatch"); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 3", line3.c_str(), "Third line mismatch"); + + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove append test file"); + }); +} + +void test_sd_large_file_operations(void) { + Serial.println("Running test_sd_large_file_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *filename = "/largefile.bin"; + const size_t chunkSize = 512; + const size_t numChunks = 10; // 5KB total + const size_t totalSize = chunkSize * numChunks; + + // Create a buffer with known pattern + uint8_t writeBuffer[chunkSize]; + for (size_t i = 0; i < chunkSize; i++) { + writeBuffer[i] = (uint8_t)(i % 256); + } + + // Write large file in chunks + File file = config.sd->open(filename, FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create large file"); + + for (size_t chunk = 0; chunk < numChunks; chunk++) { + size_t written = file.write(writeBuffer, chunkSize); + TEST_ASSERT_EQUAL_MESSAGE(chunkSize, written, "Failed to write complete chunk"); + } + file.close(); + + // Verify file size + file = config.sd->open(filename, FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open large file for reading"); + TEST_ASSERT_EQUAL_MESSAGE(totalSize, file.size(), "Large file size mismatch"); + + // Read and verify chunks + uint8_t readBuffer[chunkSize]; + for (size_t chunk = 0; chunk < numChunks; chunk++) { + size_t bytesRead = file.readBytes((char *)readBuffer, chunkSize); + TEST_ASSERT_EQUAL_MESSAGE(chunkSize, bytesRead, "Failed to read complete chunk"); + + // Verify chunk content + for (size_t i = 0; i < chunkSize; i++) { + if (readBuffer[i] != writeBuffer[i]) { + char errorMsg[100]; + snprintf(errorMsg, sizeof(errorMsg), "Data mismatch at chunk %zu, byte %zu: expected %d, got %d", chunk, i, writeBuffer[i], readBuffer[i]); + TEST_FAIL_MESSAGE(errorMsg); + } + } + } + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove large test file"); + }); +} + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(100); // Wait for Serial to be ready + } + Serial.println("SPI test START"); + +// pins for SD1 +#define SD1_SCK SCK +#define SD1_MISO MISO +#define SD1_MOSI MOSI +#define SD1_SS SS + +#if defined(CONFIG_IDF_TARGET_ESP32) +// pins for SD2 - ESP32 +#define SD2_SCK 25 +#define SD2_MISO 26 +#define SD2_MOSI 27 +#define SD2_SS 14 + +// ESP32 uses FSPI for the flash memory (for tests we use VSPI) +#undef FSPI +#define FSPI VSPI +#else +#define SD2_SCK 1 +#define SD2_MISO 2 +#define SD2_MOSI 3 +#define SD2_SS 8 +#endif + + spiTestConfigs.push_back(std::make_unique("FSPI", "/sd1", FSPI, SD1_SCK, SD1_MISO, SD1_MOSI, SD1_SS)); +#if SPI_COUNT_MAX >= 2 + spiTestConfigs.push_back(std::make_unique("HSPI", "/sd2", HSPI, SD2_SCK, SD2_MISO, SD2_MOSI, SD2_SS)); +#endif + + UNITY_BEGIN(); + RUN_TEST(test_sd_basic); + RUN_TEST(test_sd_dir); + RUN_TEST(test_sd_file_operations); + RUN_TEST(test_sd_open_limit); + RUN_TEST(test_sd_directory_listing); + RUN_TEST(test_sd_file_size_operations); + RUN_TEST(test_sd_nested_directories); + RUN_TEST(test_sd_file_count_in_directory); + RUN_TEST(test_sd_file_append_operations); + RUN_TEST(test_sd_large_file_operations); + + UNITY_END(); + + Serial.println("SPI test END"); +} + +void loop() {} diff --git a/tests/validation/sdcard/test_sdcard.py b/tests/validation/sdcard/test_sdcard.py new file mode 100644 index 00000000000..2dd071b31f0 --- /dev/null +++ b/tests/validation/sdcard/test_sdcard.py @@ -0,0 +1,2 @@ +def test_spi(dut): + dut.expect_unity_test_output(timeout=280) From d1efa2a384a160535f95cdb832e78c522ef3e735 Mon Sep 17 00:00:00 2001 From: Jakub Andrysek Date: Wed, 10 Dec 2025 13:59:18 +0100 Subject: [PATCH 2/4] fix(sdcard): correct author name in diagram files and remove obsolete test function --- tests/validation/sdcard/diagram.esp32.json | 2 +- tests/validation/sdcard/diagram.esp32c3.json | 1 - tests/validation/sdcard/diagram.esp32s2.json | 1 - tests/validation/sdcard/sdcard.ino | 83 ++++---------------- 4 files changed, 17 insertions(+), 70 deletions(-) diff --git a/tests/validation/sdcard/diagram.esp32.json b/tests/validation/sdcard/diagram.esp32.json index 1f22887b07c..c1a084b98ab 100644 --- a/tests/validation/sdcard/diagram.esp32.json +++ b/tests/validation/sdcard/diagram.esp32.json @@ -1,6 +1,6 @@ { "version": 1, - "author": "Jakub Andrysek", + "author": "Jakub Andrýsek", "editor": "wokwi", "parts": [ { diff --git a/tests/validation/sdcard/diagram.esp32c3.json b/tests/validation/sdcard/diagram.esp32c3.json index 276cca7d845..97ca46b5fd2 100644 --- a/tests/validation/sdcard/diagram.esp32c3.json +++ b/tests/validation/sdcard/diagram.esp32c3.json @@ -27,7 +27,6 @@ [ "sd1:DI", "esp32:6", "green", [ "v0.09", "h-142.56" ] ], [ "sd1:CS", "esp32:7", "green", [ "h-57.66", "v-9.3" ] ], [ "sd1:VCC", "esp32:3V3", "red", [ "v11", "h-124.94", "v132.15" ] ], - [ "sd1:VCC", "esp32:3V3.2", "red", [ "h-28.8", "v-124.94", "h-172.8", "v57.6", "h18.22" ] ], [ "sd1:GND", "esp32:GND.7", "black", [ "h0" ] ] ], "dependencies": {} diff --git a/tests/validation/sdcard/diagram.esp32s2.json b/tests/validation/sdcard/diagram.esp32s2.json index 3a27e834fb0..b397c5e5d67 100644 --- a/tests/validation/sdcard/diagram.esp32s2.json +++ b/tests/validation/sdcard/diagram.esp32s2.json @@ -24,7 +24,6 @@ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "sd1:GND", "esp32:GND.1", "black", [ "v-0.11", "h-96", "v115.2", "h-105.33" ] ], - [ "sd1:DI", "esp32:23", "green", [ "v14", "h6" ] ], [ "sd1:VCC", "esp32:3V3", "red", [ "v-0.14", "h-38.4", "v-105.6", "h-162.93" ] ], [ "sd1:CS", "esp32:34", "green", [ "h-86.4", "v-0.06" ] ], [ "sd1:DI", "esp32:35", "green", [ "h-76.8", "v0.09" ] ], diff --git a/tests/validation/sdcard/sdcard.ino b/tests/validation/sdcard/sdcard.ino index a449148f67b..65cdf13d892 100644 --- a/tests/validation/sdcard/sdcard.ino +++ b/tests/validation/sdcard/sdcard.ino @@ -93,7 +93,6 @@ void test_nonexistent_spi_interface(void) { void run_multiple_ways(SpiTestFunction test_function, uint8_t max_files = MAX_FILES, bool format_if_empty = false) { run_first_init(test_function, max_files, format_if_empty); run_init_continuous(test_function, max_files, format_if_empty); - test_nonexistent_spi_interface(); } void test_sd_basic(void) { @@ -316,73 +315,16 @@ void test_sd_nested_directories(void) { }); } -void test_sd_file_count_in_directoryOLD(void) { - Serial.println("Running test_sd_file_count_in_directory"); - - run_multiple_ways([](SPITestConfig &config) { - // Create test directory - TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/counttest"), "Failed to create /counttest"); - - // Function to count files in directory - auto countFilesInDir = [&config](const char *dirPath) -> int { - File dir = config.sd->open(dirPath); - if (!dir || !dir.isDirectory()) { - return -1; - } - - int count = 0; - File entry = dir.openNextFile(); - while (entry) { - if (!entry.isDirectory()) { - count++; - } - entry.close(); - entry = dir.openNextFile(); - } - dir.close(); - return count; - }; - - // Initially should be empty - TEST_ASSERT_EQUAL_MESSAGE(0, countFilesInDir("/counttest"), "Directory should initially be empty"); - - // Create files one by one and verify count - for (int i = 1; i <= 5; i++) { - char filename[50]; - snprintf(filename, sizeof(filename), "/counttest/file_%d.txt", i); - - File file = config.sd->open(filename, FILE_WRITE); - TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create numbered file"); - file.printf("Content of file %d", i); - file.close(); - - TEST_ASSERT_EQUAL_MESSAGE(i, countFilesInDir("/counttest"), "File count mismatch after creating file"); - } - - // Create a subdirectory (should not affect file count) - TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/counttest/subdir"), "Failed to create subdirectory"); - TEST_ASSERT_EQUAL_MESSAGE(5, countFilesInDir("/counttest"), "File count should not change with subdirectory"); - - // Remove files one by one and verify count - for (int i = 5; i >= 1; i--) { - char filename[50]; - snprintf(filename, sizeof(filename), "/counttest/file_%d.txt", i); - - TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove numbered file"); - TEST_ASSERT_EQUAL_MESSAGE(i - 1, countFilesInDir("/counttest"), "File count mismatch after removing file"); - } - - // Clean up - TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/counttest/subdir"), "Failed to remove subdirectory"); - TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/counttest"), "Failed to remove test directory"); - }); -} - void test_sd_file_count_in_directory(void) { Serial.println("Running test_sd_file_count_in_directory"); run_multiple_ways([](SPITestConfig &config) { const char *fileBasePath = "/dir/a/b"; const int numFiles = 5; + + auto getExpectedFile = [fileBasePath](int i) -> std::pair { + return {String(fileBasePath) + "/file" + String(i) + ".txt", "data:" + String(i)}; + }; + { // create nested directories TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/dir"), "mkdir /dir failed"); @@ -404,10 +346,6 @@ void test_sd_file_count_in_directory(void) { File d = config.sd->open(fileBasePath); TEST_ASSERT_TRUE_MESSAGE(d && d.isDirectory(), "open(/dir/a/b) not a directory"); - auto getExpectedFile = [fileBasePath](int i) -> std::pair { - return {String(fileBasePath) + "/file" + String(i) + ".txt", "data:" + String(i)}; - }; - bool found[numFiles] = {false}; int count = 0; @@ -446,6 +384,16 @@ void test_sd_file_count_in_directory(void) { TEST_ASSERT_TRUE_MESSAGE(found[i], ("Expected file not found: " + expectedPath).c_str()); } } + + // Cleanup: remove files and directories in reverse order (deepest first) + for (int i = 0; i < numFiles; ++i) { + auto [filePath, _] = getExpectedFile(i); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filePath.c_str()), ("Failed to remove file: " + filePath).c_str()); + } + // Remove directories + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir/a/b"), "Failed to remove directory: /dir/a/b"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir/a"), "Failed to remove directory: /dir/a"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir"), "Failed to remove directory: /dir"); }); } @@ -575,6 +523,7 @@ void setup() { #endif UNITY_BEGIN(); + RUN_TEST(test_nonexistent_spi_interface); RUN_TEST(test_sd_basic); RUN_TEST(test_sd_dir); RUN_TEST(test_sd_file_operations); From 7c51e79d2090a36359f1ec79a39ee03db76b2481 Mon Sep 17 00:00:00 2001 From: Jakub Andrysek Date: Wed, 10 Dec 2025 15:14:34 +0100 Subject: [PATCH 3/4] fix(sdcard): update author name in diagram files --- tests/validation/sdcard/diagram.esp32c3.json | 2 +- tests/validation/sdcard/diagram.esp32c6.json | 2 +- tests/validation/sdcard/diagram.esp32s2.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/validation/sdcard/diagram.esp32c3.json b/tests/validation/sdcard/diagram.esp32c3.json index 97ca46b5fd2..da9227af98c 100644 --- a/tests/validation/sdcard/diagram.esp32c3.json +++ b/tests/validation/sdcard/diagram.esp32c3.json @@ -1,6 +1,6 @@ { "version": 1, - "author": "P-R-O-C-H-Y", + "author": "Jakub Andrýsek", "editor": "wokwi", "parts": [ { diff --git a/tests/validation/sdcard/diagram.esp32c6.json b/tests/validation/sdcard/diagram.esp32c6.json index 8586fbd9af7..9c846ea918f 100644 --- a/tests/validation/sdcard/diagram.esp32c6.json +++ b/tests/validation/sdcard/diagram.esp32c6.json @@ -1,6 +1,6 @@ { "version": 1, - "author": "P-R-O-C-H-Y", + "author": "Jakub Andrýsek", "editor": "wokwi", "parts": [ { diff --git a/tests/validation/sdcard/diagram.esp32s2.json b/tests/validation/sdcard/diagram.esp32s2.json index b397c5e5d67..f7f03bfbd2a 100644 --- a/tests/validation/sdcard/diagram.esp32s2.json +++ b/tests/validation/sdcard/diagram.esp32s2.json @@ -1,6 +1,6 @@ { "version": 1, - "author": "P-R-O-C-H-Y", + "author": "Jakub Andrýsek", "editor": "wokwi", "parts": [ { From fcae8c833d9def2bb6621e543e635ccf201ee491 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:37:12 -0300 Subject: [PATCH 4/4] Apply suggestions from code review --- tests/validation/sdcard/test_sdcard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/validation/sdcard/test_sdcard.py b/tests/validation/sdcard/test_sdcard.py index 2dd071b31f0..dbd060fa5e9 100644 --- a/tests/validation/sdcard/test_sdcard.py +++ b/tests/validation/sdcard/test_sdcard.py @@ -1,2 +1,2 @@ -def test_spi(dut): +def test_sdcard(dut): dut.expect_unity_test_output(timeout=280)