diff --git a/examples/get-started/hello_world/loadable_elf_example_test.py b/examples/get-started/hello_world/loadable_elf_example_test.py index bd1cbe05b..8eca5690b 100644 --- a/examples/get-started/hello_world/loadable_elf_example_test.py +++ b/examples/get-started/hello_world/loadable_elf_example_test.py @@ -9,91 +9,6 @@ from tiny_test_fw import Utility import ttfw_idf -class CustomProcess(object): - def __init__(self, cmd, logfile): - self.f = open(logfile, 'wb') - self.p = pexpect.spawn(cmd, timeout=60, logfile=self.f) - - def __enter__(self): - return self - - def close(self): - self.p.terminate(force=True) - - def __exit__(self, type, value, traceback): - self.close() - self.f.close() - - -class OCDProcess(CustomProcess): - def __init__(self, proj_path): - cmd = 'openocd -f board/esp32-wrover-kit-3.3v.cfg' - log_file = os.path.join(proj_path, 'openocd.log') - super(OCDProcess, self).__init__(cmd, log_file) - patterns = ['Info : Listening on port 3333 for gdb connections', - 'Error: type \'esp32\' is missing virt2phys'] - - try: - while True: - i = self.p.expect_exact(patterns, timeout=30) - # TIMEOUT or EOF exceptions will be thrown upon other errors - if i == 0: - Utility.console_log('openocd is listening for gdb connections') - break # success - elif i == 1: - Utility.console_log('Ignoring error: "{}"'.format(patterns[i])) - # this error message is ignored because it is not a fatal error - except Exception: - Utility.console_log('openocd initialization has failed', 'R') - raise - - def close(self): - try: - self.p.sendcontrol('c') - self.p.expect_exact('shutdown command invoked') - except Exception: - Utility.console_log('openocd needs to be killed', 'O') - super(OCDProcess, self).close() - - -class GDBProcess(CustomProcess): - def __init__(self, proj_path, elf_path): - cmd = 'xtensa-esp32-elf-gdb -x {} --directory={} {}'.format(os.path.join(proj_path, '.gdbinit.ci'), - os.path.join(proj_path, 'main'), - elf_path) - log_file = os.path.join(proj_path, 'gdb.log') - super(GDBProcess, self).__init__(cmd, log_file) - self.p.sendline('') # it is for "---Type to continue, or q to quit---" - i = self.p.expect_exact(['Thread 1 hit Temporary breakpoint 2, app_main ()', - 'Load failed']) - if i == 0: - Utility.console_log('gdb is at breakpoint') - elif i == 1: - raise RuntimeError('Load has failed. Please examine the logs.') - else: - Utility.console_log('i = {}'.format(i)) - Utility.console_log(str(self.p)) - # This really should not happen. TIMEOUT and EOF failures are exceptions. - raise RuntimeError('An unknown error has occurred. Please examine the logs.') - - self.p.expect_exact('(gdb)') - - def close(self): - try: - self.p.sendline('q') - self.p.expect_exact('Quit anyway? (y or n)') - self.p.sendline('y') - self.p.expect_exact('Ending remote debugging.') - except Exception: - Utility.console_log('gdb needs to be killed', 'O') - super(GDBProcess, self).close() - - def break_till_end(self): - self.p.sendline('b esp_restart') - self.p.sendline('c') - self.p.expect_exact('Thread 1 hit Breakpoint 3, esp_restart ()') - - class SerialThread(object): def run(self, log_path, exit_event): with serial.Serial('/dev/ttyUSB1', 115200) as ser, open(log_path, 'wb') as f: @@ -126,16 +41,33 @@ def test_examples_loadable_elf(env, extra_data): example = ttfw_idf.LoadableElfExample(rel_project_path, app_files, target="esp32") idf_path = example.get_sdk_path() proj_path = os.path.join(idf_path, rel_project_path) - sdkconfig = example.get_sdkconfig() elf_path = os.path.join(example.binary_path, 'hello-world.elf') esp_log_path = os.path.join(proj_path, 'esp.log') - assert(sdkconfig['CONFIG_IDF_TARGET_ESP32'] == 'y'), "Only ESP32 target is supported" - assert(sdkconfig['CONFIG_APP_BUILD_TYPE_ELF_RAM'] == 'y'), "ELF should be built with CONFIG_APP_BUILD_TYPE_ELF_RAM" - with SerialThread(esp_log_path): - with OCDProcess(proj_path), GDBProcess(proj_path, elf_path) as gdb: - gdb.break_till_end() + openocd_log = os.path.join(proj_path, 'openocd.log') + gdb_log = os.path.join(proj_path, 'gdb.log') + gdb_args = '-x {} --directory={}'.format(os.path.join(proj_path, '.gdbinit.ci'), + os.path.join(proj_path, 'main')) + + with ttfw_idf.OCDProcess(openocd_log), ttfw_idf.GDBProcess(gdb_log, elf_path, gdb_args) as gdb: + gdb.pexpect_proc.sendline('') # it is for "---Type to continue, or q to quit---" + i = gdb.pexpect_proc.expect_exact(['Thread 1 hit Temporary breakpoint 2, app_main ()', + 'Load failed']) + if i == 0: + Utility.console_log('gdb is at breakpoint') + elif i == 1: + raise RuntimeError('Load has failed. Please examine the logs.') + else: + Utility.console_log('i = {}'.format(i)) + Utility.console_log(str(gdb.pexpect_proc)) + # This really should not happen. TIMEOUT and EOF failures are exceptions. + raise RuntimeError('An unknown error has occurred. Please examine the logs.') + + gdb.pexpect_proc.expect_exact('(gdb)') + gdb.pexpect_proc.sendline('b esp_restart') + gdb.pexpect_proc.sendline('c') + gdb.pexpect_proc.expect_exact('Thread 1 hit Breakpoint 3, esp_restart ()') if pexpect.run('grep "Restarting now." {}'.format(esp_log_path), withexitstatus=True)[1]: raise RuntimeError('Expected output from ESP was not received') diff --git a/examples/storage/nvs_rw_blob/nvs_rw_blob_example_test.py b/examples/storage/nvs_rw_blob/nvs_rw_blob_example_test.py new file mode 100644 index 000000000..b3fef0de5 --- /dev/null +++ b/examples/storage/nvs_rw_blob/nvs_rw_blob_example_test.py @@ -0,0 +1,43 @@ +from tiny_test_fw import Utility +import random +import re +import time +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_nvs_rw_blob(env, extra_data): + + dut = env.get_dut('nvs_rw_blob', 'examples/storage/nvs_rw_blob') + dut.start_app() + + def expect_start_msg(index): + dut.expect_all('Restart counter = {}'.format(index), + 'Run time:', + timeout=10) + + expect_start_msg(0) + dut.expect('Nothing saved yet!', timeout=5) + + nvs_store = [] + for i in range(1, 10): + time.sleep(random.uniform(0.1, 2)) # in order to randomize the runtimes stored in NVS + try: + # Pulling GPIO0 low using DTR + dut.port_inst.setDTR(True) + dut.expect('Restarting...', timeout=5) # the application waits for a second + finally: + dut.port_inst.setDTR(False) + + expect_start_msg(i) + + dut.expect_all(*nvs_store, timeout=10) + Utility.console_log('Received: {}'.format(', '.join(nvs_store))) + + new_runtime = dut.expect(re.compile(r'{}: (\d+)'.format(i)), timeout=10)[0] + nvs_store.append('{}: {}'.format(i, new_runtime)) + Utility.console_log('loop {} has finished with runtime {}'.format(i, new_runtime)) + + +if __name__ == '__main__': + test_examples_nvs_rw_blob() diff --git a/examples/storage/nvs_rw_value/nvs_rw_value_example_test.py b/examples/storage/nvs_rw_value/nvs_rw_value_example_test.py new file mode 100644 index 000000000..980cb13be --- /dev/null +++ b/examples/storage/nvs_rw_value/nvs_rw_value_example_test.py @@ -0,0 +1,29 @@ +from tiny_test_fw import Utility +import ttfw_idf + +try: + from itertools import izip_longest as zip_longest +except ImportError: + # Python 3 + from itertools import zip_longest + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_nvs_rw_value(env, extra_data): + + dut = env.get_dut('nvs_rw_value', 'examples/storage/nvs_rw_value') + dut.start_app() + + for i, counter_state in zip_longest(range(4), ('The value is not initialized yet!', ), fillvalue='Done'): + dut.expect_all('Opening Non-Volatile Storage (NVS) handle... Done', + 'Reading restart counter from NVS ... {}'.format(counter_state), + 'Restart counter = {}'.format(i) if i > 0 else '', + 'Updating restart counter in NVS ... Done', + 'Committing updates in NVS ... Done', + 'Restarting in 10 seconds...', + timeout=20) + Utility.console_log('loop {} has finished'.format(i)) + + +if __name__ == '__main__': + test_examples_nvs_rw_value() diff --git a/examples/storage/nvs_rw_value_cxx/nvs_rw_value_cxx_example_test.py b/examples/storage/nvs_rw_value_cxx/nvs_rw_value_cxx_example_test.py new file mode 100644 index 000000000..e51cf6f1b --- /dev/null +++ b/examples/storage/nvs_rw_value_cxx/nvs_rw_value_cxx_example_test.py @@ -0,0 +1,29 @@ +from tiny_test_fw import Utility +import ttfw_idf + +try: + from itertools import izip_longest as zip_longest +except ImportError: + # Python 3 + from itertools import zip_longest + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_nvs_rw_value_cxx(env, extra_data): + + dut = env.get_dut('nvs_rw_value_cxx', 'examples/storage/nvs_rw_value_cxx') + dut.start_app() + + for i, counter_state in zip_longest(range(4), ('The value is not initialized yet!', ), fillvalue='Done'): + dut.expect_all('Opening Non-Volatile Storage (NVS) handle... Done', + 'Reading restart counter from NVS ... {}'.format(counter_state), + 'Restart counter = {}'.format(i) if i > 0 else '', + 'Updating restart counter in NVS ... Done', + 'Committing updates in NVS ... Done', + 'Restarting in 10 seconds...', + timeout=20) + Utility.console_log('loop {} has finished'.format(i)) + + +if __name__ == '__main__': + test_examples_nvs_rw_value_cxx() diff --git a/examples/storage/partition_api/partition_find/partition_find_example_test.py b/examples/storage/partition_api/partition_find/partition_find_example_test.py new file mode 100644 index 000000000..0a7b434c0 --- /dev/null +++ b/examples/storage/partition_api/partition_find/partition_find_example_test.py @@ -0,0 +1,45 @@ +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_partition_find(env, extra_data): + + dut = env.get_dut('partition_find', 'examples/storage/partition_api/partition_find') + dut.start_app() + + def expect_partition(name, offset, size): + dut.expect("found partition '{}' at offset {:#x} with size {:#x}".format(name, offset, size), timeout=5) + + def expect_find_partition(_type, subtype, label, name, offset, size): + dut.expect('Find partition with type {}, subtype {}, label {}'.format(_type, subtype, label), timeout=5) + expect_partition(name, offset, size) + + dut.expect('----------------Find partitions---------------', timeout=20) + expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_NVS', 'NULL', + 'nvs', 0x9000, 0x6000) + expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_PHY', 'NULL', + 'phy_init', 0xf000, 0x1000) + expect_find_partition('ESP_PARTITION_TYPE_APP', 'ESP_PARTITION_SUBTYPE_APP_FACTORY', 'NULL', + 'factory', 0x10000, 0x100000) + expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_FAT', 'NULL', + 'storage1', 0x110000, 0x40000) + + dut.expect('Find second FAT partition by specifying the label', timeout=5) + expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_FAT', 'storage2', + 'storage2', 0x150000, 0x40000) + + dut.expect_all('----------------Iterate through partitions---------------', + 'Iterating through app partitions...', timeout=5) + expect_partition('factory', 0x10000, 0x100000) + + dut.expect('Iterating through data partitions...', timeout=5) + expect_partition('nvs', 0x9000, 0x6000) + expect_partition('phy_init', 0xf000, 0x1000) + expect_partition('storage1', 0x110000, 0x40000) + expect_partition('storage2', 0x150000, 0x40000) + + dut.expect('Example end', timeout=5) + + +if __name__ == '__main__': + test_examples_partition_find() diff --git a/examples/storage/partition_api/partition_mmap/partition_mmap_example_test.py b/examples/storage/partition_api/partition_mmap/partition_mmap_example_test.py new file mode 100644 index 000000000..d666d7110 --- /dev/null +++ b/examples/storage/partition_api/partition_mmap/partition_mmap_example_test.py @@ -0,0 +1,22 @@ +import re +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_partition_mmap(env, extra_data): + + dut = env.get_dut('partition_mmap', 'examples/storage/partition_api/partition_mmap') + dut.start_app() + + # ESP_ERROR_CHECK or assert will cause abort on error and "Example end" won't be received + dut.expect_all('Written sample data to partition: ESP-IDF Partition Memory Map Example', + re.compile(r'Mapped partition to data memory address \S+'), + 'Read sample data from partition using mapped memory: ESP-IDF Partition Memory Map Example', + 'Data matches', + 'Unmapped partition from data memory', + 'Example end', + timeout=20) + + +if __name__ == '__main__': + test_examples_partition_mmap() diff --git a/examples/storage/partition_api/partition_ops/partition_ops_example_test.py b/examples/storage/partition_api/partition_ops/partition_ops_example_test.py new file mode 100644 index 000000000..3d3506576 --- /dev/null +++ b/examples/storage/partition_api/partition_ops/partition_ops_example_test.py @@ -0,0 +1,19 @@ +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_partition_ops(env, extra_data): + + dut = env.get_dut('partition_ops', 'examples/storage/partition_api/partition_ops') + dut.start_app() + + # ESP_ERROR_CHECK or assert will cause abort on error and "Example end" won't be received + dut.expect_all('Written data: ESP-IDF Partition Operations Example (Read, Erase, Write)', + 'Read data: ESP-IDF Partition Operations Example (Read, Erase, Write)', + 'Erased data', + 'Example end', + timeout=20) + + +if __name__ == '__main__': + test_examples_partition_ops() diff --git a/examples/storage/sd_card/README.md b/examples/storage/sd_card/README.md index 299d05132..127a80d3f 100644 --- a/examples/storage/sd_card/README.md +++ b/examples/storage/sd_card/README.md @@ -120,7 +120,7 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui ## Example output -Here is an example console output. In this case a 128MB SDSC card was connected, and `format_if_mount_failed` parameter was set to `true` in the source code. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again. +Here is an example console output. In this case a 128MB SDSC card was connected, and `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option enabled. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again. ``` I (336) example: Initializing SD card @@ -165,6 +165,6 @@ Connections between the card and the ESP32 are too long for the frequency used. ### Failure to mount filesystem ``` -example: Failed to mount filesystem. If you want the card to be formatted, set format_if_mount_failed = true. +example: Failed to mount filesystem. If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option. ``` -The example will be able to mount only cards formatted using FAT32 filesystem. If the card is formatted as exFAT or some other filesystem, you have an option to format it in the example code. Modify `format_if_mount_failed = false` to `format_if_mount_failed = true` in the example code, then build and flash the example. +The example will be able to mount only cards formatted using FAT32 filesystem. If the card is formatted as exFAT or some other filesystem, you have an option to format it in the example code. Enable the `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option, then build and flash the example. diff --git a/examples/storage/sd_card/main/Kconfig.projbuild b/examples/storage/sd_card/main/Kconfig.projbuild new file mode 100644 index 000000000..0a3e447f1 --- /dev/null +++ b/examples/storage/sd_card/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "SD Card Example menu" + + config EXAMPLE_FORMAT_IF_MOUNT_FAILED + bool "Format the card if mount failed" + default n + help + If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if + the mount has failed. +endmenu diff --git a/examples/storage/sd_card/main/sd_card_example_main.c b/examples/storage/sd_card/main/sd_card_example_main.c index 374c2d81d..e3ff6639e 100644 --- a/examples/storage/sd_card/main/sd_card_example_main.c +++ b/examples/storage/sd_card/main/sd_card_example_main.c @@ -16,6 +16,7 @@ #include "driver/sdspi_host.h" #include "driver/spi_common.h" #include "sdmmc_cmd.h" +#include "sdkconfig.h" #ifdef CONFIG_IDF_TARGET_ESP32 #include "driver/sdmmc_host.h" @@ -66,7 +67,11 @@ void app_main(void) // If format_if_mount_failed is set to true, SD card will be partitioned and // formatted in case when mounting fails. esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED + .format_if_mount_failed = true, +#else .format_if_mount_failed = false, +#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED .max_files = 5, .allocation_unit_size = 16 * 1024 }; @@ -129,7 +134,7 @@ void app_main(void) if (ret != ESP_OK) { if (ret == ESP_FAIL) { ESP_LOGE(TAG, "Failed to mount filesystem. " - "If you want the card to be formatted, set format_if_mount_failed = true."); + "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); } else { ESP_LOGE(TAG, "Failed to initialize the card (%s). " "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); diff --git a/examples/storage/sd_card/sd_card_example_test.py b/examples/storage/sd_card/sd_card_example_test.py new file mode 100644 index 000000000..05b04eb6c --- /dev/null +++ b/examples/storage/sd_card/sd_card_example_test.py @@ -0,0 +1,34 @@ +from tiny_test_fw import Utility +import ttfw_idf +import re + + +@ttfw_idf.idf_example_test(env_tag='UT_T1_SDMODE') +def test_examples_sd_card(env, extra_data): + + dut = env.get_dut('sd_card', 'examples/storage/sd_card') + dut.start_app() + dut.expect('example: Initializing SD card', timeout=20) + peripheral = dut.expect(re.compile(r'example: Using (\w+) peripheral'), timeout=5)[0] + Utility.console_log('peripheral {} detected'.format(peripheral)) + assert peripheral in ('SDMMC', 'SPI') + + # These lines are matched separately because of ASCII color codes in the output + name = dut.expect(re.compile(r'Name: (\w+)'), timeout=5)[0] + _type = dut.expect(re.compile(r'Type: (\S+)'), timeout=5)[0] + speed = dut.expect(re.compile(r'Speed: (\S+)'), timeout=5)[0] + size = dut.expect(re.compile(r'Size: (\S+)'), timeout=5)[0] + + Utility.console_log('Card {} {} {}MHz {} found'.format(name, _type, speed, size)) + + dut.expect_all('Opening file', + 'File written', + 'Renaming file', + 'Reading file', + "Read from file: 'Hello {}!".format(name), + 'Card unmounted', + timeout=5) + + +if __name__ == '__main__': + test_examples_sd_card() diff --git a/examples/storage/sd_card/sdkconfig.ci b/examples/storage/sd_card/sdkconfig.ci new file mode 100644 index 000000000..7847e636e --- /dev/null +++ b/examples/storage/sd_card/sdkconfig.ci @@ -0,0 +1 @@ +CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED=y diff --git a/examples/storage/semihost_vfs/semihost_vfs_example_test.py b/examples/storage/semihost_vfs/semihost_vfs_example_test.py new file mode 100644 index 000000000..0995c9bc3 --- /dev/null +++ b/examples/storage/semihost_vfs/semihost_vfs_example_test.py @@ -0,0 +1,58 @@ +from io import open +import os +import shutil +import tempfile +import ttfw_idf + +try: + from itertools import izip_longest as zip_longest +except ImportError: + # Python 3 + from itertools import zip_longest + + +@ttfw_idf.idf_example_test(env_tag="test_jtag_arm") +def test_examples_semihost_vfs(env, extra_data): + + rel_project_path = os.path.join('examples', 'storage', 'semihost_vfs') + dut = env.get_dut('semihost_vfs', rel_project_path) + idf_path = dut.app.get_sdk_path() + proj_path = os.path.join(idf_path, rel_project_path) + host_file_name = 'host_file.txt' + + try: + temp_dir = tempfile.mkdtemp() + host_file_path = os.path.join(proj_path, 'data', host_file_name) + shutil.copyfile(host_file_path, os.path.join(temp_dir, host_file_name)) + openocd_extra_args = '-c \'set ESP_SEMIHOST_BASEDIR {}\''.format(temp_dir) + + with ttfw_idf.OCDProcess(os.path.join(proj_path, 'openocd.log'), openocd_extra_args): + dut.start_app() + dut.expect_all('example: Switch to semihosted stdout', + 'example: Switched back to UART stdout', + 'example: Wrote 2798 bytes', + '====================== HOST DATA START =========================', + timeout=20) + with open(host_file_path) as f: + file_content = [line.strip() for line in f] + dut.expect_all(*file_content, timeout=20) + dut.expect_all('====================== HOST DATA END =========================', + 'example: Read 6121 bytes', + timeout=5) + + with open(os.path.join(temp_dir, 'esp32_stdout.txt')) as f: + def expected_content(): + yield 'example: Switched to semihosted stdout' + for i in range(100): + yield 'Semihosted stdout write {}'.format(i) + yield 'example: Switch to UART stdout' + + for actual, expected in zip_longest(f, expected_content(), fillvalue='-'): + if expected not in actual: # "in" used because of the printed ASCII color codes + raise RuntimeError('"{}" != "{}"'.format(expected, actual.strip())) + finally: + shutil.rmtree(temp_dir, ignore_errors=True) + + +if __name__ == '__main__': + test_examples_semihost_vfs() diff --git a/examples/storage/spiffs/spiffs_example_test.py b/examples/storage/spiffs/spiffs_example_test.py new file mode 100644 index 000000000..3baefe7d8 --- /dev/null +++ b/examples/storage/spiffs/spiffs_example_test.py @@ -0,0 +1,22 @@ +import re +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_spiffs(env, extra_data): + + dut = env.get_dut('spiffs', 'examples/storage/spiffs') + dut.start_app() + dut.expect_all('example: Initializing SPIFFS', + re.compile(r'example: Partition size: total: \d+, used: \d+'), + 'example: Opening file', + 'example: File written', + 'example: Renaming file', + 'example: Reading file', + 'example: Read from file: \'Hello World!\'', + 'example: SPIFFS unmounted', + timeout=20) + + +if __name__ == '__main__': + test_examples_spiffs() diff --git a/examples/storage/wear_levelling/wear_levelling_example_test.py b/examples/storage/wear_levelling/wear_levelling_example_test.py new file mode 100644 index 000000000..7c0e72e09 --- /dev/null +++ b/examples/storage/wear_levelling/wear_levelling_example_test.py @@ -0,0 +1,21 @@ +import re +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_WIFI') +def test_examples_wear_levelling(env, extra_data): + + dut = env.get_dut('wear_levelling', 'examples/storage/wear_levelling') + dut.start_app() + dut.expect_all('example: Mounting FAT filesystem', + 'example: Opening file', + 'example: File written', + 'example: Reading file', + re.compile(r'example: Read from file: \'written using ESP-IDF \S+\''), + 'example: Unmounting FAT filesystem', + 'example: Done', + timeout=20) + + +if __name__ == '__main__': + test_examples_wear_levelling() diff --git a/tools/ci/config/target-test.yml b/tools/ci/config/target-test.yml index 49aa3e016..b78e61ac7 100644 --- a/tools/ci/config/target-test.yml +++ b/tools/ci/config/target-test.yml @@ -291,6 +291,7 @@ example_test_009: when: always paths: - $CI_PROJECT_DIR/examples/get-started/hello_world/*.log + - $CI_PROJECT_DIR/examples/storage/semihost_vfs/*.log expire_in: 1 week variables: SETUP_TOOLS: "1" @@ -339,6 +340,12 @@ example_test_012: - ESP32 - Example_RMT_IR_PROTOCOLS +example_test_013: + extends: .example_test_template + tags: + - ESP32 + - UT_T1_SDMODE + UT_001: extends: .unit_test_template parallel: 37 diff --git a/tools/ci/python_packages/ttfw_idf/DebugUtils.py b/tools/ci/python_packages/ttfw_idf/DebugUtils.py new file mode 100644 index 000000000..ff93ab521 --- /dev/null +++ b/tools/ci/python_packages/ttfw_idf/DebugUtils.py @@ -0,0 +1,82 @@ +# Copyright 2020 Espressif Systems (Shanghai) PTE LTD +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tiny_test_fw import Utility +import pexpect + + +class CustomProcess(object): + def __init__(self, cmd, logfile, verbose): + self.verbose = verbose + self.f = open(logfile, 'wb') + if self.verbose: + Utility.console_log('Starting {} > {}'.format(cmd, self.f.name)) + self.pexpect_proc = pexpect.spawn(cmd, timeout=60, logfile=self.f) + + def __enter__(self): + return self + + def close(self): + self.pexpect_proc.terminate(force=True) + + def __exit__(self, type, value, traceback): + self.close() + self.f.close() + + +class OCDProcess(CustomProcess): + def __init__(self, logfile_path, extra_args='', verbose=True): + # TODO Use configuration file implied by the test environment (board) + cmd = 'openocd {} -f board/esp32-wrover-kit-3.3v.cfg'.format(extra_args) + super(OCDProcess, self).__init__(cmd, logfile_path, verbose) + patterns = ['Info : Listening on port 3333 for gdb connections'] + + try: + while True: + i = self.pexpect_proc.expect_exact(patterns, timeout=30) + # TIMEOUT or EOF exceptions will be thrown upon other errors + if i == 0: + if self.verbose: + Utility.console_log('openocd is listening for gdb connections') + break # success + except Exception: + if self.verbose: + Utility.console_log('openocd initialization has failed', 'R') + raise + + def close(self): + try: + self.pexpect_proc.sendcontrol('c') + self.pexpect_proc.expect_exact('shutdown command invoked') + except Exception: + if self.verbose: + Utility.console_log('openocd needs to be killed', 'O') + super(OCDProcess, self).close() + + +class GDBProcess(CustomProcess): + def __init__(self, logfile_path, elffile_path, extra_args='', verbose=True): + cmd = 'xtensa-esp32-elf-gdb {} {}'.format(extra_args, elffile_path) + super(GDBProcess, self).__init__(cmd, logfile_path, verbose) + + def close(self): + try: + self.pexpect_proc.sendline('q') + self.pexpect_proc.expect_exact('Quit anyway? (y or n)') + self.pexpect_proc.sendline('y') + self.pexpect_proc.expect_exact('Ending remote debugging.') + except Exception: + if self.verbose: + Utility.console_log('gdb needs to be killed', 'O') + super(GDBProcess, self).close() diff --git a/tools/ci/python_packages/ttfw_idf/__init__.py b/tools/ci/python_packages/ttfw_idf/__init__.py index 53dc7d50b..8983b167a 100644 --- a/tools/ci/python_packages/ttfw_idf/__init__.py +++ b/tools/ci/python_packages/ttfw_idf/__init__.py @@ -17,6 +17,7 @@ import re from tiny_test_fw import TinyFW, Utility from .IDFApp import IDFApp, Example, LoadableElfExample, UT, TestApp # noqa: export all Apps for users from .IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP8266DUT, ESP32QEMUDUT # noqa: export DUTs for users +from .DebugUtils import OCDProcess, GDBProcess # noqa: export DebugUtils for users def format_case_id(chip, case_name):