Browse Source

test: move spi related unit-test into pytest app

wanlei 3 năm trước cách đây
mục cha
commit
f31ecbb42b
38 tập tin đã thay đổi với 503 bổ sung80 xóa
  1. 8 0
      .gitlab/ci/target-test.yml
  2. 1 1
      components/driver/test/CMakeLists.txt
  3. 1 3
      components/driver/test/test_sdio.c
  4. 10 0
      components/driver/test_apps/spi/master/CMakeLists.txt
  5. 2 0
      components/driver/test_apps/spi/master/README.md
  6. 16 0
      components/driver/test_apps/spi/master/main/CMakeLists.txt
  7. 49 0
      components/driver/test_apps/spi/master/main/test_app_main.c
  8. 6 7
      components/driver/test_apps/spi/master/main/test_spi_bus_lock.c
  9. 8 23
      components/driver/test_apps/spi/master/main/test_spi_master.c
  10. 2 10
      components/driver/test_apps/spi/master/main/test_spi_sio.c
  11. 8 0
      components/driver/test_apps/spi/master/partition_table_esp32_flash.csv
  12. 34 0
      components/driver/test_apps/spi/master/pytest_spi_master.py
  13. 2 0
      components/driver/test_apps/spi/master/sdkconfig.defaults
  14. 8 0
      components/driver/test_apps/spi/master/sdkconfig.defaults.esp32
  15. 2 0
      components/driver/test_apps/spi/master/sdkconfig.defaults.esp32s2
  16. 2 0
      components/driver/test_apps/spi/master/sdkconfig.defaults.esp32s3
  17. 10 0
      components/driver/test_apps/spi/param/CMakeLists.txt
  18. 2 0
      components/driver/test_apps/spi/param/README.md
  19. 14 0
      components/driver/test_apps/spi/param/main/CMakeLists.txt
  20. 48 0
      components/driver/test_apps/spi/param/main/test_app_main.c
  21. 30 22
      components/driver/test_apps/spi/param/main/test_spi_param.c
  22. 24 0
      components/driver/test_apps/spi/param/pytest_spi_param.py
  23. 2 0
      components/driver/test_apps/spi/param/sdkconfig.defaults
  24. 10 0
      components/driver/test_apps/spi/slave/CMakeLists.txt
  25. 2 0
      components/driver/test_apps/spi/slave/README.md
  26. 14 0
      components/driver/test_apps/spi/slave/main/CMakeLists.txt
  27. 48 0
      components/driver/test_apps/spi/slave/main/test_app_main.c
  28. 3 2
      components/driver/test_apps/spi/slave/main/test_spi_slave.c
  29. 24 0
      components/driver/test_apps/spi/slave/pytest_spi_slave.py
  30. 2 0
      components/driver/test_apps/spi/slave/sdkconfig.defaults
  31. 10 0
      components/driver/test_apps/spi/slave_hd/CMakeLists.txt
  32. 2 0
      components/driver/test_apps/spi/slave_hd/README.md
  33. 14 0
      components/driver/test_apps/spi/slave_hd/main/CMakeLists.txt
  34. 48 0
      components/driver/test_apps/spi/slave_hd/main/test_app_main.c
  35. 10 11
      components/driver/test_apps/spi/slave_hd/main/test_spi_slave_hd.c
  36. 24 0
      components/driver/test_apps/spi/slave_hd/pytest_spi_slave_hd.py
  37. 2 0
      components/driver/test_apps/spi/slave_hd/sdkconfig.defaults
  38. 1 1
      tools/ci/idf_unity_tester.py

+ 8 - 0
.gitlab/ci/target-test.yml

@@ -452,6 +452,14 @@ component_ut_pytest_esp32c2_adc:
     - build_pytest_components_esp32c2
   tags: [ esp32c2, adc ]
 
+component_ut_pytest_esp32c2_generic_multi_device:
+  extends:
+    - .pytest_components_dir_template
+    - .rules:test:component_ut-esp32c2
+  needs:
+    - build_pytest_components_esp32c2
+  tags: [ esp32c2, generic_multi_device, xtal_40mhz ]
+
 component_ut_pytest_esp32c2_xtal_26mhz:
   extends:
     - .pytest_components_dir_template

+ 1 - 1
components/driver/test/CMakeLists.txt

@@ -1,7 +1,7 @@
 idf_component_register(
     SRC_DIRS .
     PRIV_INCLUDE_DIRS include
-    PRIV_REQUIRES cmock test_utils test_driver_utils driver nvs_flash spi_flash esp_timer esp_adc esp_event esp_wifi
+    PRIV_REQUIRES cmock test_utils test_driver_utils driver nvs_flash esp_timer esp_adc esp_event esp_wifi
 )
 target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
 

+ 1 - 3
components/driver/test/test_sdio.c

@@ -6,7 +6,7 @@
 
 #include "unity.h"
 #include "test_utils.h"
-#include "param_test.h"
+#include "test_spi_utils.h"
 #include "esp_log.h"
 #include "esp_timer.h"
 #include "soc/soc_caps.h"
@@ -115,8 +115,6 @@ const sdio_test_config_t* default_config = &test_cfg_array[0];
 
 #define TEST_SIZE   (sizeof(test_cfg_array)/sizeof(sdio_test_config_t))
 
-static const char MASTER_TAG[] = "master";
-static const char SLAVE_TAG[] = "slave";
 
 /*******************************************************************************
  * Master

+ 10 - 0
components/driver/test_apps/spi/master/CMakeLists.txt

@@ -0,0 +1,10 @@
+# This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS
+    "$ENV{IDF_PATH}/tools/unit-test-app/components"
+    "$ENV{IDF_PATH}/components/driver/test_apps/components"
+)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(spi_master_test)

+ 2 - 0
components/driver/test_apps/spi/master/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |

+ 16 - 0
components/driver/test_apps/spi/master/main/CMakeLists.txt

@@ -0,0 +1,16 @@
+
+set(srcs
+    "test_app_main.c"
+    "test_spi_master.c"
+    "test_spi_sio.c"
+    "test_spi_bus_lock.c"
+)
+
+
+# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
+# the component can be registered as WHOLE_ARCHIVE
+idf_component_register(
+    SRCS ${srcs}
+    PRIV_REQUIRES test_utils driver test_driver_utils spi_flash
+    WHOLE_ARCHIVE
+)

+ 49 - 0
components/driver/test_apps/spi/master/main/test_app_main.c

@@ -0,0 +1,49 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "unity.h"
+#include "unity_test_utils.h"
+#include "esp_heap_caps.h"
+
+// iterator to load partition tables in `test spi bus lock, with flash` will lead memory not free
+#define TEST_MEMORY_LEAK_THRESHOLD (250)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+    esp_reent_cleanup();    //clean up some of the newlib's lazy allocations
+    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    printf("\n");
+    unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+    unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+    //  ____  ____ ___   __  __           _
+    // / ___||  _ \_ _| |  \/  | __ _ ___| |_ ___ _ __
+    // \___ \| |_) | |  | |\/| |/ _` / __| __/ _ \ '__|
+    //  ___) |  __/| |  | |  | | (_| \__ \ ||  __/ |
+    // |____/|_|  |___| |_|  |_|\__,_|___/\__\___|_|
+
+    printf("\n");
+    printf("     ____  ____ ___   __  __           _            \n");
+    printf("    / ___||  _ \\_ _| |  \\/  | __ _ ___| |_ ___ _ __ \n");
+    printf("    \\___ \\| |_) | |  | |\\/| |/ _` / __| __/ _ \\ '__|\n");
+    printf("     ___) |  __/| |  | |  | | (_| \\__ \\ ||  __/ |   \n");
+    printf("    |____/|_|  |___| |_|  |_|\\__,_|___/\\__\\___|_|   \n");
+
+    unity_run_menu();
+}

+ 6 - 7
components/driver/test/test_spi_bus_lock.c → components/driver/test_apps/spi/master/main/test_spi_bus_lock.c

@@ -8,9 +8,9 @@
 #include "driver/spi_master.h"
 #include "driver/gpio.h"
 #include "esp_flash_spi_init.h"
+#include "test_utils.h"
+#include "test_spi_utils.h"
 #include "spi_flash_mmap.h"
-
-#include "test/test_common_spi.h"
 #include "unity.h"
 
 
@@ -283,14 +283,13 @@ static void test_bus_lock(bool test_flash)
     TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST) );
 }
 
-#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32C3, ESP32S3, ESP32C2)
-//no runners
-//IDF-5049
-TEST_CASE("spi bus lock, with flash","[spi][test_env=UT_T1_ESP_FLASH]")
+#if CONFIG_IDF_TARGET_ESP32
+// no need this case in other target, only esp32 need buslock to split MSPI and GPSPI2 action
+TEST_CASE("spi bus lock, with flash","[spi][test_env=external_flash]")
 {
     test_bus_lock(true);
 }
-#endif //!TEMPORARY_DISABLED_FOR_TARGETS(...)
+#endif //CONFIG_IDF_TARGET_ESP32
 
 
 TEST_CASE("spi bus lock","[spi]")

+ 8 - 23
components/driver/test/test_spi_master.c → components/driver/test_apps/spi/master/main/test_spi_master.c

@@ -7,21 +7,13 @@
  Tests for the spi_master device driver
 */
 
-#include <esp_types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <malloc.h>
-#include <string.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/semphr.h"
-#include "freertos/queue.h"
-#include "unity.h"
+
 #include "sdkconfig.h"
 #include "driver/spi_master.h"
 #include "driver/spi_slave.h"
 #include "driver/gpio.h"
 #include "soc/gpio_periph.h"
+#include "soc/spi_periph.h"
 #include "soc/soc_memory_layout.h"
 #include "esp_private/cache_utils.h"
 #include "esp_private/spi_common_internal.h"
@@ -29,7 +21,7 @@
 #include "esp_heap_caps.h"
 #include "esp_log.h"
 #include "test_utils.h"
-#include "test/test_common_spi.h"
+#include "test_spi_utils.h"
 
 
 #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6)
@@ -913,7 +905,7 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first)
             addr_got = addr_got >> (64 - addr_bits);
         }
 
-        ESP_LOGI(SLAVE_TAG, "cmd_got: %04X, addr_got: %08X%08X", cmd_got, (uint32_t)(addr_got >> 32), (uint32_t)addr_got);
+        ESP_LOGI(SLAVE_TAG, "cmd_got: %" PRIX16 ", addr_got: %" PRIX32 "%" PRIX32, cmd_got, (uint32_t)(addr_got >> 32), (uint32_t)addr_got);
 
         TEST_ASSERT_EQUAL_HEX16(cmd_expected, cmd_got);
         if (addr_bits > 0) {
@@ -932,7 +924,7 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first)
 TEST_CASE("SPI master variable cmd & addr test", "[spi]")
 {
     spi_slave_task_context_t slave_context = {};
-    esp_err_t err = init_slave_context( &slave_context );
+    esp_err_t err = init_slave_context( &slave_context, TEST_SLAVE_HOST );
     TEST_ASSERT( err == ESP_OK );
     TaskHandle_t handle_slave;
     xTaskCreate( spitest_slave_task, "spi_slave", 4096, &slave_context, 0, &handle_slave);
@@ -1301,7 +1293,7 @@ static void fd_slave(void)
     TEST_ASSERT(spi_slave_free(SPI2_HOST) == ESP_OK);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI Master: FD, DMA, Master Single Direction Test", "[spi_ms][test_env=Example_SPI_Multi_device]", fd_master, fd_slave);
+TEST_CASE_MULTIPLE_DEVICES("SPI Master: FD, DMA, Master Single Direction Test", "[spi_ms][test_env=generic_multi_device]", fd_master, fd_slave);
 #endif  //#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32)    //TODO: IDF-3494
 #endif  //#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C2)    //TODO: IDF-3494
 
@@ -1416,7 +1408,6 @@ TEST_CASE("spi_speed", "[spi]")
 
     //release the bus
     spi_device_release_bus(spi);
-
     master_free_device_bus(spi);
 
     speed_setup(&spi, !use_dma);
@@ -1437,6 +1428,7 @@ TEST_CASE("spi_speed", "[spi]")
     //acquire the bus to send polling transactions faster
     ret = spi_device_acquire_bus(spi, portMAX_DELAY);
     TEST_ESP_OK(ret);
+
     //record flight time by polling, without DMA
     t_flight_num = 0;
     for (int i = 0; i < TEST_TIMES; i++) {
@@ -1541,13 +1533,6 @@ void test_add_device_slave(void)
         .spics_io_num = CS_REAL_DEV,
         .queue_size = 3,
     };
-#if CONFIG_IDF_TARGET_ESP32
-    //now esp32 runners use SPI3 pin group to test gpio matrix together on CI.
-    bus_cfg.miso_io_num = SPI3_IOMUX_PIN_NUM_MISO;
-    bus_cfg.mosi_io_num = SPI3_IOMUX_PIN_NUM_MOSI;
-    bus_cfg.sclk_io_num = SPI3_IOMUX_PIN_NUM_CLK;
-    slvcfg.spics_io_num = SPI3_IOMUX_PIN_NUM_CS;
-#endif
     TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &bus_cfg, &slvcfg, SPI_DMA_CH_AUTO));
 
     spi_slave_transaction_t slave_trans = {};
@@ -1573,5 +1558,5 @@ void test_add_device_slave(void)
     spi_bus_free(TEST_SPI_HOST);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI_Master:Test multiple devices", "[spi_ms][test_env=Example_SPI_Multi_device]", test_add_device_master, test_add_device_slave);
+TEST_CASE_MULTIPLE_DEVICES("SPI_Master:Test multiple devices", "[spi_ms][test_env=generic_multi_device]", test_add_device_master, test_add_device_slave);
 #endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6)

+ 2 - 10
components/driver/test/test_spi_sio.c → components/driver/test_apps/spi/master/main/test_spi_sio.c

@@ -24,7 +24,7 @@
 #include "esp_log.h"
 #include "soc/spi_periph.h"
 #include "test_utils.h"
-#include "test/test_common_spi.h"
+#include "test_spi_utils.h"
 #include "soc/gpio_periph.h"
 
 #include "hal/spi_ll.h"
@@ -269,14 +269,6 @@ void test_sio_slave_emulate(bool sio_master_in)
 
     spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
     spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
-#if CONFIG_IDF_TARGET_ESP32
-    // esp32 use different pin for slave in current runner
-    bus_cfg.mosi_io_num = spi_periph_signal[TEST_SLAVE_HOST].spid_iomux_pin;
-    bus_cfg.miso_io_num = spi_periph_signal[TEST_SLAVE_HOST].spiq_iomux_pin;
-    bus_cfg.sclk_io_num = spi_periph_signal[TEST_SLAVE_HOST].spiclk_iomux_pin;
-
-    slv_cfg.spics_io_num = spi_periph_signal[TEST_SLAVE_HOST].spics0_iomux_pin;
-#endif
     TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_CH_AUTO));
     printf("CS:CLK:MO:MI: %d\t%d\t%d\t%d\n", slv_cfg.spics_io_num, bus_cfg.sclk_io_num, bus_cfg.mosi_io_num, bus_cfg.miso_io_num);
 
@@ -334,5 +326,5 @@ void test_slave_run(void)
     test_sio_slave_emulate(true);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI_Master:Test_SIO_Mode_Multi_Board", "[spi_ms][test_env=Example_SPI_Multi_device]", test_master_run, test_slave_run);
+TEST_CASE_MULTIPLE_DEVICES("SPI_Master:Test_SIO_Mode_Multi_Board", "[spi_ms][test_env=generic_multi_device]", test_master_run, test_slave_run);
 #endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6)

+ 8 - 0
components/driver/test_apps/spi/master/partition_table_esp32_flash.csv

@@ -0,0 +1,8 @@
+# Special partition table for unit test app
+#
+# Name,     Type, SubType, Offset,   Size, Flags
+# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
+factory,    0,    0,        0x20000, 0x260000
+
+# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
+flash_test, data, fat,      ,        528K

+ 34 - 0
components/driver/test_apps/spi/master/pytest_spi_master.py

@@ -0,0 +1,34 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+import pytest
+
+
+# If `test_env` is define, should not run on generic runner
+@pytest.mark.supported_targets
+@pytest.mark.generic
+def test_master_single_dev(case_tester) -> None:       # type: ignore
+    for case in case_tester.test_menu:
+        if 'test_env' in case.attributes:
+            continue
+        case_tester.run_normal_case(case=case, reset=True)
+
+
+# Job for test_env `external_flash` just for esp32 only
+@pytest.mark.esp32
+@pytest.mark.flash_mutli
+def test_master_esp_flash(case_tester) -> None:        # type: ignore
+    for case in case_tester.test_menu:
+        # test case `spi_bus_lock_with_flash` use difference test env
+        if case.attributes.get('test_env') == 'external_flash':
+            case_tester.run_normal_case(case=case, reset=True)
+
+
+# if `test_env` not defined, will run on `generic_multi_device` by default
+@pytest.mark.supported_targets
+@pytest.mark.generic_multi_device
+@pytest.mark.parametrize('count', [2,], indirect=True)
+def test_master_multi_dev(case_tester) -> None:        # type: ignore
+    for case in case_tester.test_menu:
+        if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
+            case_tester.run_multi_dev_case(case=case, reset=True)

+ 2 - 0
components/driver/test_apps/spi/master/sdkconfig.defaults

@@ -0,0 +1,2 @@
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESP_TASK_WDT=n

+ 8 - 0
components/driver/test_apps/spi/master/sdkconfig.defaults.esp32

@@ -0,0 +1,8 @@
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
+CONFIG_XTAL_FREQ_AUTO=y
+CONFIG_SPI_FLASH_SHARE_SPI1_BUS=y
+CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
+CONFIG_PARTITION_TABLE_CUSTOM=y
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_esp32_flash.csv"
+CONFIG_PARTITION_TABLE_FILENAME="partition_table_esp32_flash.csv"
+CONFIG_PARTITION_TABLE_OFFSET=0x8000

+ 2 - 0
components/driver/test_apps/spi/master/sdkconfig.defaults.esp32s2

@@ -0,0 +1,2 @@
+# test_case `spi_speed` need freq 240M
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y

+ 2 - 0
components/driver/test_apps/spi/master/sdkconfig.defaults.esp32s3

@@ -0,0 +1,2 @@
+# test_case `spi_speed` need freq 240M
+CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y

+ 10 - 0
components/driver/test_apps/spi/param/CMakeLists.txt

@@ -0,0 +1,10 @@
+# This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS
+    "$ENV{IDF_PATH}/tools/unit-test-app/components"
+    "$ENV{IDF_PATH}/components/driver/test_apps/components"
+)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(spi_param_test)

+ 2 - 0
components/driver/test_apps/spi/param/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |

+ 14 - 0
components/driver/test_apps/spi/param/main/CMakeLists.txt

@@ -0,0 +1,14 @@
+
+set(srcs
+    "test_app_main.c"
+    "test_spi_param.c"
+)
+
+
+# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
+# the component can be registered as WHOLE_ARCHIVE
+idf_component_register(
+    SRCS ${srcs}
+    PRIV_REQUIRES test_utils driver test_driver_utils
+    WHOLE_ARCHIVE
+)

+ 48 - 0
components/driver/test_apps/spi/param/main/test_app_main.c

@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "unity.h"
+#include "unity_test_utils.h"
+#include "esp_heap_caps.h"
+
+#define TEST_MEMORY_LEAK_THRESHOLD (150)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+    esp_reent_cleanup();    //clean up some of the newlib's lazy allocations
+    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    printf("\n");
+    unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+    unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+    //  ____  ____ ___   ____
+    // / ___||  _ \_ _| |  _ \ __ _ _ __ __ _ _ __ ___
+    // \___ \| |_) | |  | |_) / _` | '__/ _` | '_ ` _ `.
+    //  ___) |  __/| |  |  __/ (_| | | | (_| | | | | | |
+    // |____/|_|  |___| |_|   \__,_|_|  \__,_|_| |_| |_|
+
+    printf("\n");
+    printf("     ____  ____ ___   ____                           \n");
+    printf("    / ___||  _ \\_ _| |  _ \\ __ _ _ __ __ _ _ __ ___  \n");
+    printf("    \\___ \\| |_) | |  | |_) / _` | '__/ _` | '_ ` _ \\ \n");
+    printf("     ___) |  __/| |  |  __/ (_| | | | (_| | | | | | |\n");
+    printf("    |____/|_|  |___| |_|   \\__,_|_|  \\__,_|_| |_| |_|\n");
+
+    unity_run_menu();
+}

+ 30 - 22
components/driver/test/test_spi_param.c → components/driver/test_apps/spi/param/main/test_spi_param.c

@@ -7,7 +7,8 @@
 #include "esp_attr.h"
 #include "soc/spi_periph.h"
 #include "sdkconfig.h"
-#include "test/test_common_spi.h"
+#include "test_utils.h"
+#include "test_spi_utils.h"
 #include "driver/spi_master.h"
 #include "driver/spi_slave.h"
 
@@ -44,7 +45,7 @@ static void local_test_init(void** arg)
     spitest_context_t* context = (spitest_context_t*)*arg;
     TEST_ASSERT(context!=NULL);
     context->slave_context = (spi_slave_task_context_t){};
-    esp_err_t err = init_slave_context( &context->slave_context);
+    esp_err_t err = init_slave_context( &context->slave_context, TEST_SLAVE_HOST);
     TEST_ASSERT(err == ESP_OK);
 
     xTaskCreate(spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave);
@@ -211,7 +212,7 @@ static void local_test_loop(const void* arg1, void* arg2)
             }
 
             if (failed) {
-                ESP_LOGI(SLAVE_TAG, "slave_recv_len: %d", rcv_len);
+                ESP_LOGI(SLAVE_TAG, "slave_recv_len: %" PRIu32, rcv_len);
                 spitest_master_print_data(t, len);
 
                 ESP_LOG_BUFFER_HEX("slave tx", slave_trans.tx_buffer, len);
@@ -659,9 +660,12 @@ static const ptest_func_t slave_test_func = {
     .def_param = spitest_def_param,
 };
 
+//temporarily close mass data print to avoid pytest run too busy to timeout
+#define TEST_LOG_DBUG   false
+
 #define TEST_SPI_MASTER_SLAVE(name, param_group, extra_tag) \
     PARAM_GROUP_DECLARE(name, param_group) \
-    TEST_MASTER_SLAVE(name, param_group, "[spi_ms][test_env=Example_SPI_Multi_device][timeout=120]"extra_tag, &master_test_func, &slave_test_func)
+    TEST_MASTER_SLAVE(name, param_group, "[spi_ms][test_env=generic_multi_device][timeout=120]"extra_tag, &master_test_func, &slave_test_func)
 
 /************ Master Code ***********************************************/
 static void test_master_init(void** arg)
@@ -671,8 +675,9 @@ static void test_master_init(void** arg)
     spitest_context_t* context = *arg;
     TEST_ASSERT(context!=NULL);
     context->slave_context = (spi_slave_task_context_t){};
-    esp_err_t err = init_slave_context(&context->slave_context);
+    esp_err_t err = init_slave_context(&context->slave_context, TEST_SPI_HOST);
     TEST_ASSERT(err == ESP_OK);
+    unity_send_signal("Master ready");
 }
 
 static void test_master_deinit(void* arg)
@@ -685,13 +690,10 @@ static void test_master_start(spi_device_handle_t *spi, int freq, const spitest_
 {
     //master config
     spi_bus_config_t buspset=SPI_BUS_TEST_DEFAULT_CONFIG();
-    buspset.miso_io_num = MASTER_IOMUX_PIN_MISO;
-    buspset.mosi_io_num = MASTER_IOMUX_PIN_MOSI;
-    buspset.sclk_io_num = MASTER_IOMUX_PIN_SCLK;
     //this does nothing, but avoid the driver from using native pins
     if (!pset->master_iomux) buspset.quadhd_io_num = UNCONNECTED_PIN;
     spi_device_interface_config_t devpset=SPI_DEVICE_TEST_DEFAULT_CONFIG();
-    devpset.spics_io_num = MASTER_IOMUX_PIN_CS;
+    devpset.spics_io_num = SPI2_IOMUX_PIN_NUM_CS;
     devpset.mode = pset->mode;
     const int cs_pretrans_max = 15;
     if (pset->dup==HALF_DUPLEX_MISO) {
@@ -757,7 +759,7 @@ static void test_master_loop(const void *arg1, void* arg2)
         ESP_LOGI(MASTER_TAG, "==============> %dk", freq/1000);
         test_master_start(&spi, freq, test_cfg, context);
 
-        unity_wait_for_signal("slave ready");
+        unity_wait_for_signal("Slave ready");
 
         for( int j= 0; j < test_cfg->test_size; j ++ ) {
             //wait for both master and slave end
@@ -768,11 +770,15 @@ static void test_master_loop(const void *arg1, void* arg2)
             spi_transaction_t *t = &context->master_trans[j];
             TEST_ESP_OK (spi_device_transmit(spi, t) );
             int len = get_trans_len(test_cfg->dup, t);
-            spitest_master_print_data(t, len);
+            if(TEST_LOG_DBUG){
+                spitest_master_print_data(t, len);
+            }
 
             size_t rcv_len;
             slave_rxdata_t *rcv_data = xRingbufferReceive( context->slave_context.data_received, &rcv_len, portMAX_DELAY );
-            spitest_slave_print_data(rcv_data, false);
+            if(TEST_LOG_DBUG){
+                spitest_slave_print_data(rcv_data, false);
+            }
 
             //check result
             bool check_master_data = (test_cfg->dup != HALF_DUPLEX_MOSI &&
@@ -800,9 +806,10 @@ static void test_slave_init(void** arg)
     spitest_context_t* context = (spitest_context_t*)*arg;
     TEST_ASSERT(context!=NULL);
     context->slave_context = (spi_slave_task_context_t){};
-    esp_err_t err = init_slave_context( &context->slave_context );
+    esp_err_t err = init_slave_context( &context->slave_context, TEST_SPI_HOST);
     TEST_ASSERT( err == ESP_OK );
 
+    unity_wait_for_signal("Master ready");
     xTaskCreate( spitest_slave_task, "spi_slave", 4096, &context->slave_context, 0, &context->handle_slave);
 }
 
@@ -819,19 +826,16 @@ static void timing_slave_start(int speed, const spitest_param_set_t* pset, spite
 {
     //slave config
     spi_bus_config_t slv_buscfg=SPI_BUS_TEST_DEFAULT_CONFIG();
-    slv_buscfg.miso_io_num = SLAVE_IOMUX_PIN_MISO;
-    slv_buscfg.mosi_io_num = SLAVE_IOMUX_PIN_MOSI;
-    slv_buscfg.sclk_io_num = SLAVE_IOMUX_PIN_SCLK;
     //this does nothing, but avoid the driver from using native pins
     if (!pset->slave_iomux) slv_buscfg.quadhd_io_num = UNCONNECTED_PIN;
     spi_slave_interface_config_t slvcfg=SPI_SLAVE_TEST_DEFAULT_CONFIG();
-    slvcfg.spics_io_num = SLAVE_IOMUX_PIN_CS;
+    slvcfg.spics_io_num = SPI2_IOMUX_PIN_NUM_CS;
     slvcfg.mode = pset->mode;
     //Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
     slave_pull_up(&slv_buscfg, slvcfg.spics_io_num);
 
     int slave_dma_chan = (pset->slave_dma_chan == 0) ? 0 : SPI_DMA_CH_AUTO;
-    TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &slv_buscfg, &slvcfg, slave_dma_chan));
+    TEST_ESP_OK(spi_slave_initialize(TEST_SPI_HOST, &slv_buscfg, &slvcfg, slave_dma_chan));
 
     //prepare data for the master
     for (int i = 0; i < pset->test_size; i++) {
@@ -867,7 +871,7 @@ static void test_slave_loop(const void *arg1, void* arg2)
         }
 
         vTaskDelay(50/portTICK_PERIOD_MS);
-        unity_send_signal("slave ready");
+        unity_send_signal("Slave ready");
 
         for( int i= 0; i < pset->test_size; i ++ ) {
             //wait for both master and slave end
@@ -877,11 +881,15 @@ static void test_slave_loop(const void *arg1, void* arg2)
 
             spi_transaction_t *t = &context->master_trans[i];
             int len = get_trans_len(pset->dup, t);
-            spitest_master_print_data(t, FULL_DUPLEX);
+            if(TEST_LOG_DBUG){
+                spitest_master_print_data(t, FULL_DUPLEX);
+            }
 
             size_t rcv_len;
             slave_rxdata_t *rcv_data = xRingbufferReceive( context->slave_context.data_received, &rcv_len, portMAX_DELAY );
-            spitest_slave_print_data(rcv_data, true);
+            if(TEST_LOG_DBUG){
+                spitest_slave_print_data(rcv_data, true);
+            }
 
             //check result
             const bool check_master_data = false;
@@ -891,7 +899,7 @@ static void test_slave_loop(const void *arg1, void* arg2)
             //clean
             vRingbufferReturnItem( context->slave_context.data_received, rcv_data );
         }
-        TEST_ASSERT(spi_slave_free(TEST_SLAVE_HOST) == ESP_OK);
+        TEST_ASSERT(spi_slave_free(TEST_SPI_HOST) == ESP_OK);
     }
 }
 

+ 24 - 0
components/driver/test_apps/spi/param/pytest_spi_param.py

@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+import pytest
+
+
+# If `test_env` is define, should not run on generic runner
+@pytest.mark.supported_targets
+@pytest.mark.generic
+def test_param_single_dev(case_tester) -> None:       # type: ignore
+    for case in case_tester.test_menu:
+        if 'test_env' in case.attributes:
+            continue
+        case_tester.run_normal_case(case=case, reset=True)
+
+
+# if `test_env` not defined, will run on `generic_multi_device` by default
+@pytest.mark.supported_targets
+@pytest.mark.generic_multi_device
+@pytest.mark.parametrize('count', [2,], indirect=True)
+def test_param_multi_dev(case_tester) -> None:        # type: ignore
+    for case in case_tester.test_menu:
+        if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
+            case_tester.run_multi_dev_case(case=case, reset=True)

+ 2 - 0
components/driver/test_apps/spi/param/sdkconfig.defaults

@@ -0,0 +1,2 @@
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESP_TASK_WDT=n

+ 10 - 0
components/driver/test_apps/spi/slave/CMakeLists.txt

@@ -0,0 +1,10 @@
+# This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS
+    "$ENV{IDF_PATH}/tools/unit-test-app/components"
+    "$ENV{IDF_PATH}/components/driver/test_apps/components"
+)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(spi_slave_test)

+ 2 - 0
components/driver/test_apps/spi/slave/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |

+ 14 - 0
components/driver/test_apps/spi/slave/main/CMakeLists.txt

@@ -0,0 +1,14 @@
+
+set(srcs
+    "test_app_main.c"
+    "test_spi_slave.c"
+)
+
+
+# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
+# the component can be registered as WHOLE_ARCHIVE
+idf_component_register(
+    SRCS ${srcs}
+    PRIV_REQUIRES test_utils driver test_driver_utils
+    WHOLE_ARCHIVE
+)

+ 48 - 0
components/driver/test_apps/spi/slave/main/test_app_main.c

@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "unity.h"
+#include "unity_test_utils.h"
+#include "esp_heap_caps.h"
+
+#define TEST_MEMORY_LEAK_THRESHOLD (100)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+    esp_reent_cleanup();    //clean up some of the newlib's lazy allocations
+    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    printf("\n");
+    unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+    unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+    //  ____  ____ ___   ____  _
+    // / ___||  _ \_ _| / ___|| | __ ___   _____
+    // \___ \| |_) | |  \___ \| |/ _` \ \ / / _ `
+    //  ___) |  __/| |   ___) | | (_| |\ V /  __/
+    // |____/|_|  |___| |____/|_|\__,_| \_/ \___|
+
+    printf("\n");
+    printf("     ____  ____ ___   ____  _                 \n");
+    printf("    / ___||  _ \\_ _| / ___|| | __ ___   _____ \n");
+    printf("    \\___ \\| |_) | |  \\___ \\| |/ _` \\ \\ / / _ \\\n");
+    printf("     ___) |  __/| |   ___) | | (_| |\\ V /  __/\n");
+    printf("    |____/|_|  |___| |____/|_|\\__,_| \\_/ \\___|\n");
+
+    unity_run_menu();
+}

+ 3 - 2
components/driver/test/test_spi_slave.c → components/driver/test_apps/spi/slave/main/test_spi_slave.c

@@ -10,7 +10,8 @@
 #include <string.h>
 #include "sdkconfig.h"
 #include "unity.h"
-#include "test/test_common_spi.h"
+#include "test_utils.h"
+#include "test_spi_utils.h"
 #include "driver/spi_master.h"
 #include "driver/spi_slave.h"
 #include "driver/gpio.h"
@@ -382,7 +383,7 @@ static void unaligned_test_slave(void)
     TEST_ASSERT(spi_slave_free(TEST_SPI_HOST) == ESP_OK);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI_Slave_Unaligned_Test", "[spi_ms][test_env=Example_SPI_Multi_device][timeout=120]", unaligned_test_master, unaligned_test_slave);
+TEST_CASE_MULTIPLE_DEVICES("SPI_Slave_Unaligned_Test", "[spi_ms][test_env=generic_multi_device][timeout=120]", unaligned_test_master, unaligned_test_slave);
 
 #endif  //#if (TEST_SPI_PERIPH_NUM == 1)
 #endif //!TEMPORARY_DISABLED_FOR_TARGETS(...)

+ 24 - 0
components/driver/test_apps/spi/slave/pytest_spi_slave.py

@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+import pytest
+
+
+# If `test_env` is define, should not run on generic runner
+@pytest.mark.supported_targets
+@pytest.mark.generic
+def test_slave_single_dev(case_tester) -> None:       # type: ignore
+    for case in case_tester.test_menu:
+        if 'test_env' in case.attributes:
+            continue
+        case_tester.run_normal_case(case=case, reset=True)
+
+
+# if `test_env` not defined, will run on `generic_multi_device` by default
+@pytest.mark.supported_targets
+@pytest.mark.generic_multi_device
+@pytest.mark.parametrize('count', [2,], indirect=True)
+def test_slave_multi_dev(case_tester) -> None:        # type: ignore
+    for case in case_tester.test_menu:
+        if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
+            case_tester.run_multi_dev_case(case=case, reset=True)

+ 2 - 0
components/driver/test_apps/spi/slave/sdkconfig.defaults

@@ -0,0 +1,2 @@
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESP_TASK_WDT=n

+ 10 - 0
components/driver/test_apps/spi/slave_hd/CMakeLists.txt

@@ -0,0 +1,10 @@
+# This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS
+    "$ENV{IDF_PATH}/tools/unit-test-app/components"
+    "$ENV{IDF_PATH}/components/driver/test_apps/components"
+)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(spi_slave_hd_test)

+ 2 - 0
components/driver/test_apps/spi/slave_hd/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |

+ 14 - 0
components/driver/test_apps/spi/slave_hd/main/CMakeLists.txt

@@ -0,0 +1,14 @@
+
+set(srcs
+    "test_app_main.c"
+    "test_spi_slave_hd.c"
+)
+
+
+# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
+# the component can be registered as WHOLE_ARCHIVE
+idf_component_register(
+    SRCS ${srcs}
+    PRIV_REQUIRES test_utils driver test_driver_utils esp_serial_slave_link sdmmc
+    WHOLE_ARCHIVE
+)

+ 48 - 0
components/driver/test_apps/spi/slave_hd/main/test_app_main.c

@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "unity.h"
+#include "unity_test_utils.h"
+#include "esp_heap_caps.h"
+
+#define TEST_MEMORY_LEAK_THRESHOLD (200)
+
+static size_t before_free_8bit;
+static size_t before_free_32bit;
+
+void setUp(void)
+{
+    before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+}
+
+void tearDown(void)
+{
+    esp_reent_cleanup();    //clean up some of the newlib's lazy allocations
+    size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
+    size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
+    printf("\n");
+    unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
+    unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
+}
+
+void app_main(void)
+{
+    //  ____  ____ ___   ____  _                   _   _ ____
+    // / ___||  _ \_ _| / ___|| | __ ___   _____  | | | |  _ `.
+    // \___ \| |_) | |  \___ \| |/ _` \ \ / / _ \ | |_| | | | |
+    //  ___) |  __/| |   ___) | | (_| |\ V /  __/ |  _  | |_| |
+    // |____/|_|  |___| |____/|_|\__,_| \_/ \___| |_| |_|____/
+
+    printf("\n");
+    printf("     ____  ____ ___   ____  _                   _   _ ____  \n");
+    printf("    / ___||  _ \\_ _| / ___|| | __ ___   _____  | | | |  _ \\ \n");
+    printf("    \\___ \\| |_) | |  \\___ \\| |/ _` \\ \\ / / _ \\ | |_| | | | |\n");
+    printf("     ___) |  __/| |   ___) | | (_| |\\ V /  __/ |  _  | |_| |\n");
+    printf("    |____/|_|  |___| |____/|_|\\__,_| \\_/ \\___| |_| |_|____/ \n");
+
+    unity_run_menu();
+}

+ 10 - 11
components/driver/test/test_spi_slave_hd.c → components/driver/test_apps/spi/slave_hd/main/test_spi_slave_hd.c

@@ -8,15 +8,10 @@
 */
 
 #include "esp_log.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-#include "freertos/semphr.h"
-#include "unity.h"
-
+#include "test_utils.h"
+#include "test_spi_utils.h"
 #include "soc/spi_periph.h"
-#include "driver/spi_master.h"
 #include "esp_serial_slave_link/essl_spi.h"
-#include "test/test_common_spi.h"
 
 #if SOC_SPI_SUPPORT_SLAVE_HD_VER2
 #include "driver/spi_slave_hd.h"
@@ -631,11 +626,12 @@ static void hd_master(void)
     WORD_ALIGNED_ATTR uint8_t *master_recv_buf = calloc(1, send_buf_size * 2);
     //This buffer is used for 2-board test and should be assigned totally the same as the ``hd_slave`` does.
     WORD_ALIGNED_ATTR uint8_t *slave_send_buf = malloc(send_buf_size * 2);
-    get_tx_buffer(199, master_send_buf, slave_send_buf, send_buf_size);
+    get_tx_buffer(199, master_send_buf, slave_send_buf, send_buf_size * 2);
 
     //This is the same as the ``hd_slave`` sets.
     int trans_len[] = {5, send_buf_size};
 
+    unity_send_signal("master ready");
     unity_wait_for_signal("slave ready");
     essl_spi_wrdma(spi, master_send_buf, send_buf_size, -1, 0);
 
@@ -676,13 +672,14 @@ static void hd_slave(void)
     };
     TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
 
+    unity_wait_for_signal("master ready");
     const int send_buf_size = 1024;
 
     WORD_ALIGNED_ATTR uint8_t *slave_send_buf = malloc(send_buf_size * 2);
     WORD_ALIGNED_ATTR uint8_t *slave_recv_buf = calloc(1, send_buf_size * 2);
     //This buffer is used for 2-board test and should be assigned totally the same as the ``hd_master`` does.
     WORD_ALIGNED_ATTR uint8_t *master_send_buf = malloc(send_buf_size * 2);
-    get_tx_buffer(199, master_send_buf, slave_send_buf, send_buf_size);
+    get_tx_buffer(199, master_send_buf, slave_send_buf, send_buf_size * 2);
 
     //make the first transaction shorter than the actual trans length of the master, so that the second one will be loaded while the master is still doing the first transaction.
     int trans_len[] = {5, send_buf_size};
@@ -739,7 +736,7 @@ static void hd_slave(void)
     spi_slave_hd_deinit(TEST_SLAVE_HOST);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI Slave HD: segment mode, master sends too long", "[spi_ms][test_env=Example_SPI_Multi_device]", hd_master, hd_slave);
+TEST_CASE_MULTIPLE_DEVICES("SPI Slave HD: segment mode, master sends too long", "[spi_ms][test_env=generic_multi_device]", hd_master, hd_slave);
 #endif  //#if (TEST_SPI_PERIPH_NUM == 1)
 
 /**
@@ -776,6 +773,7 @@ static void hd_master_quad(void){
     WORD_ALIGNED_ATTR uint8_t *slave_send_buf = heap_caps_malloc(BUF_SIZE, MALLOC_CAP_DMA);
     get_tx_buffer(199, master_send_buf, slave_send_buf, BUF_SIZE);
 
+    unity_send_signal("Master ready");
     unity_wait_for_signal("slave ready");
     essl_spi_wrdma(spi, master_send_buf, BUF_SIZE / 2, -1, SPI_TRANS_MODE_QIO);
 
@@ -851,6 +849,7 @@ static void hd_slave_quad(void){
         },
     };
 
+    unity_wait_for_signal("Master ready");
     for (int i = 0; i < 2; i ++) {
         TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &slave_trans[i], portMAX_DELAY));
         unity_send_signal("slave ready");
@@ -880,7 +879,7 @@ static void hd_slave_quad(void){
     spi_slave_hd_deinit(TEST_SLAVE_HOST);
 }
 
-TEST_CASE_MULTIPLE_DEVICES("SPI quad hd test ", "[spi_ms][test_env=Example_SPI_Quad_Multi_device]", hd_master_quad, hd_slave_quad);
+TEST_CASE_MULTIPLE_DEVICES("SPI quad hd test ", "[spi_ms][test_env=generic_multi_device]", hd_master_quad, hd_slave_quad);
 
 #endif  // #if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2)
 

+ 24 - 0
components/driver/test_apps/spi/slave_hd/pytest_spi_slave_hd.py

@@ -0,0 +1,24 @@
+# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+import pytest
+
+
+# If `test_env` is define, should not run on generic runner
+@pytest.mark.supported_targets
+@pytest.mark.generic
+def test_slave_hd_single_dev(case_tester) -> None:       # type: ignore
+    for case in case_tester.test_menu:
+        if 'test_env' in case.attributes:
+            continue
+        case_tester.run_normal_case(case=case, reset=True)
+
+
+# if `test_env` not defined, will run on `generic_multi_device` by default
+@pytest.mark.supported_targets
+@pytest.mark.generic_multi_device
+@pytest.mark.parametrize('count', [2,], indirect=True)
+def test_slave_hd_multi_dev(case_tester) -> None:        # type: ignore
+    for case in case_tester.test_menu:
+        if case.attributes.get('test_env', 'generic_multi_device') == 'generic_multi_device':
+            case_tester.run_multi_dev_case(case=case, reset=True)

+ 2 - 0
components/driver/test_apps/spi/slave_hd/sdkconfig.defaults

@@ -0,0 +1,2 @@
+CONFIG_FREERTOS_HZ=1000
+CONFIG_ESP_TASK_WDT=n

+ 1 - 1
tools/ci/idf_unity_tester.py

@@ -95,7 +95,7 @@ class NormalCaseTester(BaseTester):
             for retry in range(self.retry_times):
                 self.dut.write(str(case.index))
                 try:
-                    self.dut.expect('Running {}...'.format(case.name), timeout=1)
+                    self.dut.expect_exact('Running {}...'.format(case.name), timeout=1)
                     break
                 except TIMEOUT as e:
                     if retry >= self.retry_times - 1: