Prechádzať zdrojové kódy

Merge branch 'feature/ci_optimize_artifact_download' into 'master'

CI: optimize submodule fetch and artifact download

See merge request espressif/esp-idf!6842
Angus Gratton 6 rokov pred
rodič
commit
f50626dca6
98 zmenil súbory, kde vykonal 2140 pridanie a 1266 odobranie
  1. 15 7
      .gitlab-ci.yml
  2. 6 29
      examples/bluetooth/nimble/blecent/blecent_test.py
  3. 7 30
      examples/bluetooth/nimble/blehr/blehr_test.py
  4. 7 28
      examples/bluetooth/nimble/bleprph/bleprph_test.py
  5. 6 17
      examples/get-started/blink/example_test.py
  6. 9 15
      examples/get-started/hello_world/loadable_elf_example_test.py
  7. 3 15
      examples/peripherals/can/can_alert_and_recovery/example_test.py
  8. 9 16
      examples/peripherals/can/can_network/example_test.py
  9. 3 14
      examples/peripherals/can/can_self_test/example_test.py
  10. 4 13
      examples/peripherals/i2c/i2c_tools/example_test.py
  11. 2 10
      examples/peripherals/rmt/ir_protocols/example_test.py
  12. 6 20
      examples/peripherals/sdio/sdio_test.py
  13. 5 17
      examples/protocols/asio/chat_client/asio_chat_client_test.py
  14. 5 18
      examples/protocols/asio/chat_server/asio_chat_server_test.py
  15. 5 19
      examples/protocols/asio/tcp_echo_server/asio_tcp_server_test.py
  16. 5 19
      examples/protocols/asio/udp_echo_server/asio_udp_server_test.py
  17. 5 18
      examples/protocols/esp_http_client/esp_http_client_test.py
  18. 7 24
      examples/protocols/http_server/advanced_tests/http_server_advanced_test.py
  19. 8 22
      examples/protocols/http_server/persistent_sockets/http_server_persistence_test.py
  20. 7 21
      examples/protocols/http_server/simple/http_server_simple_test.py
  21. 5 17
      examples/protocols/https_request/example_test.py
  22. 6 21
      examples/protocols/mdns/mdns_example_test.py
  23. 4 13
      examples/protocols/modbus/serial/example_test.py
  24. 7 20
      examples/protocols/mqtt/ssl/mqtt_ssl_example_test.py
  25. 6 19
      examples/protocols/mqtt/tcp/mqtt_tcp_example_test.py
  26. 6 19
      examples/protocols/mqtt/ws/mqtt_ws_example_test.py
  27. 6 18
      examples/protocols/mqtt/wss/mqtt_wss_example_test.py
  28. 5 14
      examples/protocols/websocket/example_test.py
  29. 6 21
      examples/provisioning/ble_prov/ble_prov_test.py
  30. 6 21
      examples/provisioning/manager/wifi_prov_mgr_test.py
  31. 7 29
      examples/provisioning/softap_prov/softap_prov_test.py
  32. 3 13
      examples/security/flash_encryption/example_test.py
  33. 3 13
      examples/storage/ext_flash_fatfs/example_test.py
  34. 3 10
      examples/storage/parttool/example_test.py
  35. 3 11
      examples/storage/spiffsgen/example_test.py
  36. 2 10
      examples/system/console/example_test.py
  37. 3 12
      examples/system/cpp_exceptions/example_test.py
  38. 6 18
      examples/system/cpp_rtti/example_test.py
  39. 3 15
      examples/system/esp_event/default_event_loop/example_test.py
  40. 4 16
      examples/system/esp_event/user_event_loops/example_test.py
  41. 4 17
      examples/system/esp_timer/example_test.py
  42. 3 16
      examples/system/freertos/real_time_stats/example_test.py
  43. 3 12
      examples/system/light_sleep/example_test.py
  44. 5 16
      examples/system/ota/otatool/example_test.py
  45. 6 19
      examples/system/ota/simple_ota_example/example_test.py
  46. 23 41
      examples/wifi/iperf/iperf_test.py
  47. 9 4
      make/project.mk
  48. 0 0
      tools/ble/__init__.py
  49. 1 1
      tools/ble/lib_ble_client.py
  50. 1 1
      tools/ble/lib_gap.py
  51. 1 1
      tools/ble/lib_gatt.py
  52. 106 0
      tools/ci/ci_fetch_submodule.py
  53. 5 4
      tools/ci/config/assign-test.yml
  54. 1 1
      tools/ci/config/post_check.yml
  55. 7 21
      tools/ci/config/target-test.yml
  56. 0 1
      tools/ci/executable-list.txt
  57. 174 0
      tools/ci/python_packages/gitlab_api.py
  58. 0 0
      tools/ci/python_packages/idf_http_server_test/__init__.py
  59. 2 1
      tools/ci/python_packages/idf_http_server_test/adder.py
  60. 2 13
      tools/ci/python_packages/idf_http_server_test/client.py
  61. 1025 0
      tools/ci/python_packages/idf_http_server_test/test.py
  62. 0 0
      tools/ci/python_packages/idf_iperf_test_util/Attenuator.py
  63. 0 0
      tools/ci/python_packages/idf_iperf_test_util/LineChart.py
  64. 0 0
      tools/ci/python_packages/idf_iperf_test_util/PowerControl.py
  65. 0 0
      tools/ci/python_packages/idf_iperf_test_util/TestReport.py
  66. 0 0
      tools/ci/python_packages/idf_iperf_test_util/__init__.py
  67. 0 0
      tools/ci/python_packages/tiny_test_fw/App.py
  68. 0 0
      tools/ci/python_packages/tiny_test_fw/DUT.py
  69. 0 0
      tools/ci/python_packages/tiny_test_fw/Env.py
  70. 0 0
      tools/ci/python_packages/tiny_test_fw/EnvConfig.py
  71. 0 0
      tools/ci/python_packages/tiny_test_fw/EnvConfigTemplate.yml
  72. 0 0
      tools/ci/python_packages/tiny_test_fw/TinyFW.py
  73. 2 3
      tools/ci/python_packages/tiny_test_fw/Utility/CIAssignTest.py
  74. 6 9
      tools/ci/python_packages/tiny_test_fw/Utility/CaseConfig.py
  75. 0 0
      tools/ci/python_packages/tiny_test_fw/Utility/GitlabCIJob.py
  76. 2 1
      tools/ci/python_packages/tiny_test_fw/Utility/SearchCases.py
  77. 0 0
      tools/ci/python_packages/tiny_test_fw/Utility/TestCase.py
  78. 0 0
      tools/ci/python_packages/tiny_test_fw/Utility/__init__.py
  79. 0 0
      tools/ci/python_packages/tiny_test_fw/__init__.py
  80. 2 2
      tools/ci/python_packages/tiny_test_fw/bin/Runner.py
  81. 0 0
      tools/ci/python_packages/tiny_test_fw/bin/example.py
  82. 0 0
      tools/ci/python_packages/tiny_test_fw/docs/Makefile
  83. 0 0
      tools/ci/python_packages/tiny_test_fw/docs/_static/.keep
  84. 0 0
      tools/ci/python_packages/tiny_test_fw/docs/conf.py
  85. 0 0
      tools/ci/python_packages/tiny_test_fw/docs/index.rst
  86. 0 1
      tools/ci/python_packages/tiny_test_fw/requirements.txt
  87. 90 0
      tools/ci/python_packages/ttfw_idf/CIAssignExampleTest.py
  88. 2 10
      tools/ci/python_packages/ttfw_idf/CIAssignUnitTest.py
  89. 411 0
      tools/ci/python_packages/ttfw_idf/IDFApp.py
  90. 1 2
      tools/ci/python_packages/ttfw_idf/IDFDUT.py
  91. 3 4
      tools/ci/python_packages/ttfw_idf/__init__.py
  92. 0 0
      tools/ci/python_packages/wifi_tools.py
  93. 3 0
      tools/ci/setup_python.sh
  94. 8 0
      tools/cmake/git_submodules.cmake
  95. 1 0
      tools/esp_prov/__init__.py
  96. 0 56
      tools/tiny-test-fw/CIAssignExampleTest.py
  97. 0 273
      tools/tiny-test-fw/IDF/IDFApp.py
  98. 13 34
      tools/unit-test-app/unit_test.py

+ 15 - 7
.gitlab-ci.yml

@@ -23,9 +23,16 @@ variables:
   # GIT_STRATEGY is not defined here.
   # GIT_STRATEGY is not defined here.
   # Use an option from  "CI / CD Settings" - "General pipelines".
   # Use an option from  "CI / CD Settings" - "General pipelines".
 
 
-  # "normal" strategy for fetching only top-level submodules since nothing requires the sub-submodules code for building IDF.
-  # If the "recursive" strategy is used we have a problem with using relative URLs for sub-submodules.
-  GIT_SUBMODULE_STRATEGY: normal
+  # we will download archive for each submodule instead of clone.
+  # we don't do "recursive" when fetch submodule as they're not used in CI now.
+  GIT_SUBMODULE_STRATEGY: none
+  SUBMODULE_FETCH_TOOL: "tools/ci/ci_fetch_submodule.py"
+  # by default we will fetch all submodules
+  # jobs can overwrite this variable to only fetch submodules they required
+  # set to "none" if don't need to fetch submodules
+  SUBMODULES_TO_FETCH: "all"
+  # tell build system do not check submodule update as we download archive instead of clone
+  IDF_SKIP_CHECK_SUBMODULES: 1
 
 
   UNIT_TEST_BUILD_SYSTEM: cmake
   UNIT_TEST_BUILD_SYSTEM: cmake
   EXAMPLE_TEST_BUILD_SYSTEM: cmake
   EXAMPLE_TEST_BUILD_SYSTEM: cmake
@@ -44,6 +51,7 @@ variables:
   CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/tools/ci/config/target-test.yml"
   CI_TARGET_TEST_CONFIG_FILE: "$CI_PROJECT_DIR/tools/ci/config/target-test.yml"
 
 
 
 
+
 # before each job, we need to check if this job is filtered by bot stage/job filter
 # before each job, we need to check if this job is filtered by bot stage/job filter
 .apply_bot_filter: &apply_bot_filter
 .apply_bot_filter: &apply_bot_filter
   python $APPLY_BOT_FILTER_SCRIPT || exit 0
   python $APPLY_BOT_FILTER_SCRIPT || exit 0
@@ -72,12 +80,10 @@ variables:
   tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1
   tools/idf_tools.py --non-interactive install && eval "$(tools/idf_tools.py --non-interactive export)" || exit 1
   fi
   fi
 
 
-.show_submodule_urls: &show_submodule_urls |
-  git config --get-regexp '^submodule\..*\.url$' || true
+.fetch_submodules: &fetch_submodules |
+  python $SUBMODULE_FETCH_TOOL -s $SUBMODULES_TO_FETCH
 
 
 before_script:
 before_script:
-  - echo "Running common script"
-  - *show_submodule_urls
   - source tools/ci/setup_python.sh
   - source tools/ci/setup_python.sh
   # apply bot filter in before script
   # apply bot filter in before script
   - *apply_bot_filter
   - *apply_bot_filter
@@ -93,6 +99,8 @@ before_script:
 
 
   - *setup_tools_unless_target_test
   - *setup_tools_unless_target_test
 
 
+  - *fetch_submodules
+
   - *setup_custom_toolchain
   - *setup_custom_toolchain
 
 
 # used for check scripts which we want to run unconditionally
 # used for check scripts which we want to run unconditionally

+ 6 - 29
examples/bluetooth/nimble/blecent/blecent_test.py

@@ -16,43 +16,20 @@
 
 
 from __future__ import print_function
 from __future__ import print_function
 import os
 import os
-import sys
 import re
 import re
 import uuid
 import uuid
 import subprocess
 import subprocess
 
 
-try:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError as e:
-    print(e)
-    print("\nCheck your IDF_PATH\nOR")
-    print("Try `export TEST_FW_PATH=$IDF_PATH/tools/tiny-test-fw` for resolving the issue\nOR")
-    print("Try `pip install -r $IDF_PATH/tools/tiny-test-fw/requirements.txt` for resolving the issue")
-    import IDF
-
-try:
-    import lib_ble_client
-except ImportError:
-    lib_ble_client_path = os.getenv("IDF_PATH") + "/tools/ble"
-    if lib_ble_client_path and lib_ble_client_path not in sys.path:
-        sys.path.insert(0, lib_ble_client_path)
-    import lib_ble_client
-
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
+from ble import lib_ble_client
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
 # > make print_flash_cmd | tail -n 1 > build/download.config
 # > make print_flash_cmd | tail -n 1 > build/download.config
-# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_example_app_ble_central(env, extra_data):
 def test_example_app_ble_central(env, extra_data):
     """
     """
         Steps:
         Steps:
@@ -68,12 +45,12 @@ def test_example_app_ble_central(env, extra_data):
     subprocess.check_output(['rm','-rf','/var/lib/bluetooth/*'])
     subprocess.check_output(['rm','-rf','/var/lib/bluetooth/*'])
     subprocess.check_output(['hciconfig','hci0','reset'])
     subprocess.check_output(['hciconfig','hci0','reset'])
     # Acquire DUT
     # Acquire DUT
-    dut = env.get_dut("blecent", "examples/bluetooth/nimble/blecent", dut_class=ESP32DUT)
+    dut = env.get_dut("blecent", "examples/bluetooth/nimble/blecent", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut.app.binary_path, "blecent.bin")
     binary_file = os.path.join(dut.app.binary_path, "blecent.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("blecent_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.log_performance("blecent_bin_size", "{}KB".format(bin_size // 1024))
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting blecent example test app")
     Utility.console_log("Starting blecent example test app")

+ 7 - 30
examples/bluetooth/nimble/blehr/blehr_test.py

@@ -16,42 +16,19 @@
 
 
 from __future__ import print_function
 from __future__ import print_function
 import os
 import os
-import sys
 import re
 import re
 import threading
 import threading
 import traceback
 import traceback
 import Queue
 import Queue
 import subprocess
 import subprocess
 
 
-try:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError as e:
-    print(e)
-    print("\nCheck your IDF_PATH\nOR")
-    print("Try `export TEST_FW_PATH=$IDF_PATH/tools/tiny-test-fw` for resolving the issue\nOR")
-    print("Try `pip install -r $IDF_PATH/tools/tiny-test-fw/requirements.txt` for resolving the issue\n")
-    import IDF
-
-try:
-    import lib_ble_client
-except ImportError:
-    lib_ble_client_path = os.getenv("IDF_PATH") + "/tools/ble"
-    if lib_ble_client_path and lib_ble_client_path not in sys.path:
-        sys.path.insert(0, lib_ble_client_path)
-    import lib_ble_client
-
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
+from ble import lib_ble_client
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
 # > make print_flash_cmd | tail -n 1 > build/download.config
 # > make print_flash_cmd | tail -n 1 > build/download.config
-# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
 
 
 
 
 def blehr_client_task(hr_obj, dut_addr):
 def blehr_client_task(hr_obj, dut_addr):
@@ -115,7 +92,7 @@ class BleHRThread(threading.Thread):
             self.exceptions_queue.put(traceback.format_exc(), block=False)
             self.exceptions_queue.put(traceback.format_exc(), block=False)
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_example_app_ble_hr(env, extra_data):
 def test_example_app_ble_hr(env, extra_data):
     """
     """
         Steps:
         Steps:
@@ -129,13 +106,13 @@ def test_example_app_ble_hr(env, extra_data):
     subprocess.check_output(['hciconfig','hci0','reset'])
     subprocess.check_output(['hciconfig','hci0','reset'])
 
 
     # Acquire DUT
     # Acquire DUT
-    dut = env.get_dut("blehr", "examples/bluetooth/nimble/blehr", dut_class=ESP32DUT)
+    dut = env.get_dut("blehr", "examples/bluetooth/nimble/blehr", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut.app.binary_path, "blehr.bin")
     binary_file = os.path.join(dut.app.binary_path, "blehr.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("blehr_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("blehr_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("blehr_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("blehr_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting blehr simple example test app")
     Utility.console_log("Starting blehr simple example test app")

+ 7 - 28
examples/bluetooth/nimble/bleprph/bleprph_test.py

@@ -16,36 +16,15 @@
 
 
 from __future__ import print_function
 from __future__ import print_function
 import os
 import os
-import sys
 import re
 import re
 import Queue
 import Queue
 import traceback
 import traceback
 import threading
 import threading
 import subprocess
 import subprocess
 
 
-try:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError as e:
-    print(e)
-    print("Try `export TEST_FW_PATH=$IDF_PATH/tools/tiny-test-fw` for resolving the issue")
-    print("Try `pip install -r $IDF_PATH/tools/tiny-test-fw/requirements.txt` for resolving the issue")
-    import IDF
-
-try:
-    import lib_ble_client
-except ImportError:
-    lib_ble_client_path = os.getenv("IDF_PATH") + "/tools/ble"
-    if lib_ble_client_path and lib_ble_client_path not in sys.path:
-        sys.path.insert(0, lib_ble_client_path)
-    import lib_ble_client
-
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
+from ble import lib_ble_client
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
@@ -136,7 +115,7 @@ class BlePrphThread(threading.Thread):
             self.exceptions_queue.put(traceback.format_exc(), block=False)
             self.exceptions_queue.put(traceback.format_exc(), block=False)
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_example_app_ble_peripheral(env, extra_data):
 def test_example_app_ble_peripheral(env, extra_data):
     """
     """
         Steps:
         Steps:
@@ -150,13 +129,13 @@ def test_example_app_ble_peripheral(env, extra_data):
     subprocess.check_output(['hciconfig','hci0','reset'])
     subprocess.check_output(['hciconfig','hci0','reset'])
 
 
     # Acquire DUT
     # Acquire DUT
-    dut = env.get_dut("bleprph", "examples/bluetooth/nimble/bleprph", dut_class=ESP32DUT)
+    dut = env.get_dut("bleprph", "examples/bluetooth/nimble/bleprph", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut.app.binary_path, "bleprph.bin")
     binary_file = os.path.join(dut.app.binary_path, "bleprph.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("bleprph_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("bleprph_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("bleprph_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("bleprph_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting bleprph simple example test app")
     Utility.console_log("Starting bleprph simple example test app")

+ 6 - 17
examples/get-started/blink/example_test.py

@@ -5,21 +5,10 @@ from __future__ import print_function
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 import re
 import re
 import os
 import os
-import sys
 import hashlib
 import hashlib
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-
-    import IDF
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
 
 
 
 
 def verify_elf_sha256_embedding(dut):
 def verify_elf_sha256_embedding(dut):
@@ -39,13 +28,13 @@ def verify_elf_sha256_embedding(dut):
         raise ValueError('ELF file SHA256 mismatch')
         raise ValueError('ELF file SHA256 mismatch')
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_blink(env, extra_data):
 def test_examples_blink(env, extra_data):
-    dut = env.get_dut("blink", "examples/get-started/blink", dut_class=ESP32DUT)
+    dut = env.get_dut("blink", "examples/get-started/blink", dut_class=ttfw_idf.ESP32DUT)
     binary_file = os.path.join(dut.app.binary_path, "blink.bin")
     binary_file = os.path.join(dut.app.binary_path, "blink.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("blink_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("blink_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("blink_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("blink_bin_size", bin_size // 1024)
 
 
     dut.start_app()
     dut.start_app()
 
 

+ 9 - 15
examples/get-started/hello_world/loadable_elf_example_test.py

@@ -1,19 +1,12 @@
 import os
 import os
-import pexpect
-import serial
-import sys
 import threading
 import threading
 import time
 import time
 
 
-try:
-    import IDF
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import pexpect
+import serial
 
 
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
 
 
 
 
 class CustomProcess(object):
 class CustomProcess(object):
@@ -125,15 +118,16 @@ class SerialThread(object):
             Utility.console_log('The pyserial thread is still alive', 'O')
             Utility.console_log('The pyserial thread is still alive', 'O')
 
 
 
 
-@IDF.idf_example_test(env_tag="test_jtag_arm")
+@ttfw_idf.idf_example_test(env_tag="test_jtag_arm")
 def test_examples_loadable_elf(env, extra_data):
 def test_examples_loadable_elf(env, extra_data):
 
 
-    idf_path = os.environ['IDF_PATH']
     rel_project_path = os.path.join('examples', 'get-started', 'hello_world')
     rel_project_path = os.path.join('examples', 'get-started', 'hello_world')
+    app_files = ['hello-world.elf', 'partition_table/partition-table.bin']
+    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)
     proj_path = os.path.join(idf_path, rel_project_path)
-    example = IDF.Example(rel_project_path, target="esp32")
     sdkconfig = example.get_sdkconfig()
     sdkconfig = example.get_sdkconfig()
-    elf_path = os.path.join(example.get_binary_path(rel_project_path), 'hello-world.elf')
+    elf_path = os.path.join(example.binary_path, 'hello-world.elf')
     esp_log_path = os.path.join(proj_path, 'esp.log')
     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_IDF_TARGET_ESP32'] == 'y'), "Only ESP32 target is supported"

+ 3 - 15
examples/peripherals/can/can_alert_and_recovery/example_test.py

@@ -1,28 +1,16 @@
 # Need Python 3 string formatting functions
 # Need Python 3 string formatting functions
 from __future__ import print_function
 from __future__ import print_function
 
 
-import os
-import sys
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # The test cause is dependent on the Tiny Test Framework. Ensure the
-    # `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 # CAN Self Test Example constants
 # CAN Self Test Example constants
 STR_EXPECT = ("CAN Alert and Recovery: Driver installed", "CAN Alert and Recovery: Driver uninstalled")
 STR_EXPECT = ("CAN Alert and Recovery: Driver installed", "CAN Alert and Recovery: Driver uninstalled")
 EXPECT_TIMEOUT = 20
 EXPECT_TIMEOUT = 20
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_CAN1')
+@ttfw_idf.idf_example_test(env_tag='Example_CAN1')
 def test_can_alert_and_recovery_example(env, extra_data):
 def test_can_alert_and_recovery_example(env, extra_data):
-    dut = env.get_dut('dut1', 'examples/peripherals/can/can_alert_and_recovery', dut_class=ESP32DUT)
+    dut = env.get_dut('dut1', 'examples/peripherals/can/can_alert_and_recovery', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     for string in STR_EXPECT:
     for string in STR_EXPECT:

+ 9 - 16
examples/peripherals/can/can_network/example_test.py

@@ -1,19 +1,9 @@
 # Need Python 3 string formatting functions
 # Need Python 3 string formatting functions
 from __future__ import print_function
 from __future__ import print_function
 
 
-import os
-import sys
 from threading import Thread
 from threading import Thread
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # The test cause is dependent on the Tiny Test Framework. Ensure the
-    # `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+
+import ttfw_idf
 
 
 # Define tuple of strings to expect for each DUT.
 # Define tuple of strings to expect for each DUT.
 master_expect = ("CAN Master: Driver installed", "CAN Master: Driver uninstalled")
 master_expect = ("CAN Master: Driver installed", "CAN Master: Driver uninstalled")
@@ -37,13 +27,16 @@ def dut_thread_callback(**kwargs):
     result[0] = True
     result[0] = True
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_CAN2')
+@ttfw_idf.idf_example_test(env_tag='Example_CAN2')
 def test_can_network_example(env, extra_data):
 def test_can_network_example(env, extra_data):
 
 
     # Get device under test. "dut1", "dut2", and "dut3" must be properly defined in EnvConfig
     # Get device under test. "dut1", "dut2", and "dut3" must be properly defined in EnvConfig
-    dut_master = env.get_dut("dut1", "examples/peripherals/can/can_network/can_network_master", dut_class=ESP32DUT)
-    dut_slave = env.get_dut("dut2", "examples/peripherals/can/can_network/can_network_slave", dut_class=ESP32DUT)
-    dut_listen_only = env.get_dut("dut3", "examples/peripherals/can/can_network/can_network_listen_only", dut_class=ESP32DUT)
+    dut_master = env.get_dut("dut1", "examples/peripherals/can/can_network/can_network_master",
+                             dut_class=ttfw_idf.ESP32DUT)
+    dut_slave = env.get_dut("dut2", "examples/peripherals/can/can_network/can_network_slave",
+                            dut_class=ttfw_idf.ESP32DUT)
+    dut_listen_only = env.get_dut("dut3", "examples/peripherals/can/can_network/can_network_listen_only",
+                                  dut_class=ttfw_idf.ESP32DUT)
 
 
     # Flash app onto each DUT, each DUT is reset again at the start of each thread
     # Flash app onto each DUT, each DUT is reset again at the start of each thread
     dut_master.start_app()
     dut_master.start_app()

+ 3 - 14
examples/peripherals/can/can_self_test/example_test.py

@@ -1,18 +1,7 @@
 # Need Python 3 string formatting functions
 # Need Python 3 string formatting functions
 from __future__ import print_function
 from __future__ import print_function
 
 
-import os
-import sys
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # The test cause is dependent on the Tiny Test Framework. Ensure the
-    # `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
 # CAN Self Test Example constants
 # CAN Self Test Example constants
@@ -20,10 +9,10 @@ STR_EXPECT = ("CAN Self Test: Driver installed", "CAN Self Test: Driver uninstal
 EXPECT_TIMEOUT = 20
 EXPECT_TIMEOUT = 20
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_CAN1')
+@ttfw_idf.idf_example_test(env_tag='Example_CAN1')
 def test_can_self_test_example(env, extra_data):
 def test_can_self_test_example(env, extra_data):
     # Get device under test, flash and start example. "dut1" must be defined in EnvConfig
     # Get device under test, flash and start example. "dut1" must be defined in EnvConfig
-    dut = env.get_dut('dut1', 'examples/peripherals/can/can_self_test', dut_class=ESP32DUT)
+    dut = env.get_dut('dut1', 'examples/peripherals/can/can_self_test', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     for string in STR_EXPECT:
     for string in STR_EXPECT:

+ 4 - 13
examples/peripherals/i2c/i2c_tools/example_test.py

@@ -1,23 +1,14 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-EXPECT_TIMEOUT = 20
+import ttfw_idf
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+EXPECT_TIMEOUT = 20
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_I2C_CCS811_SENSOR')
+@ttfw_idf.idf_example_test(env_tag='Example_I2C_CCS811_SENSOR')
 def test_i2ctools_example(env, extra_data):
 def test_i2ctools_example(env, extra_data):
     # Get device under test, flash and start example. "i2ctool" must be defined in EnvConfig
     # Get device under test, flash and start example. "i2ctool" must be defined in EnvConfig
-    dut = env.get_dut('i2ctools', 'examples/peripherals/i2c/i2c_tools', dut_class=ESP32DUT)
+    dut = env.get_dut('i2ctools', 'examples/peripherals/i2c/i2c_tools', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
     dut.expect("esp32>", timeout=EXPECT_TIMEOUT)
     dut.expect("esp32>", timeout=EXPECT_TIMEOUT)
     # Get i2c address
     # Get i2c address

+ 2 - 10
examples/peripherals/rmt/ir_protocols/example_test.py

@@ -1,19 +1,11 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-try:
-    import IDF
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 EXPECT_TIMEOUT = 20
 EXPECT_TIMEOUT = 20
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_RMT_IR_PROTOCOLS')
+@ttfw_idf.idf_example_test(env_tag='Example_RMT_IR_PROTOCOLS')
 def test_examples_rmt_ir_protocols(env, extra_data):
 def test_examples_rmt_ir_protocols(env, extra_data):
     dut = env.get_dut('ir_protocols_example', 'examples/peripherals/rmt/ir_protocols', app_config_name='nec')
     dut = env.get_dut('ir_protocols_example', 'examples/peripherals/rmt/ir_protocols', app_config_name='nec')
     print("Using binary path: {}".format(dut.app.binary_path))
     print("Using binary path: {}".format(dut.app.binary_path))

+ 6 - 20
examples/peripherals/sdio/sdio_test.py

@@ -12,25 +12,11 @@
 # See the License for the specific language governing permissions and
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # limitations under the License.
 
 
-""" example of writing test with TinyTestFW """
-import os
-import sys
+from tiny_test_fw import TinyFW
+import ttfw_idf
 
 
-try:
-    import TinyFW
-except ImportError:
-    # if we want to run test case outside `tiny-test-fw` folder,
-    # we need to insert tiny-test-fw path into sys path
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import TinyFW
 
 
-import IDF
-from IDF.IDFDUT import ESP32DUT
-
-
-@IDF.idf_example_test(env_tag="Example_SDIO", ignore=True)
+@ttfw_idf.idf_example_test(env_tag="Example_SDIO", ignore=True)
 def test_example_sdio_communication(env, extra_data):
 def test_example_sdio_communication(env, extra_data):
     """
     """
     Configurations
     Configurations
@@ -50,8 +36,8 @@ def test_example_sdio_communication(env, extra_data):
     or use sdio test board, which has two wrover modules connect to a same FT3232
     or use sdio test board, which has two wrover modules connect to a same FT3232
     Assume that first dut is host and second is slave
     Assume that first dut is host and second is slave
     """
     """
-    dut1 = env.get_dut("sdio_host", "examples/peripherals/sdio/host", dut_class=ESP32DUT)
-    dut2 = env.get_dut("sdio_slave", "examples/peripherals/sdio/slave", dut_class=ESP32DUT)
+    dut1 = env.get_dut("sdio_host", "examples/peripherals/sdio/host", dut_class=ttfw_idf.ESP32DUT)
+    dut2 = env.get_dut("sdio_slave", "examples/peripherals/sdio/slave", dut_class=ttfw_idf.ESP32DUT)
     dut1.start_app()
     dut1.start_app()
     # wait until the master is ready to setup the slave
     # wait until the master is ready to setup the slave
     dut1.expect("host ready, start initializing slave...")
     dut1.expect("host ready, start initializing slave...")
@@ -133,5 +119,5 @@ def test_example_sdio_communication(env, extra_data):
 
 
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
-    TinyFW.set_default_config(env_config_file="EnvConfigTemplate.yml", dut=IDF.IDFDUT)
+    TinyFW.set_default_config(env_config_file="EnvConfigTemplate.yml", dut=ttfw_idf.IDFDUT)
     test_example_sdio_communication()
     test_example_sdio_communication()

+ 5 - 17
examples/protocols/asio/chat_client/asio_chat_client_test.py

@@ -1,22 +1,10 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 from threading import Thread
 from threading import Thread
 import time
 import time
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 global g_client_response
 global g_client_response
 global g_msg_to_client
 global g_msg_to_client
@@ -56,7 +44,7 @@ def chat_server_sketch(my_ip):
     print("server closed")
     print("server closed")
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_asio_chat_client(env, extra_data):
 def test_examples_protocol_asio_chat_client(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -70,12 +58,12 @@ def test_examples_protocol_asio_chat_client(env, extra_data):
     global g_client_response
     global g_client_response
     global g_msg_to_client
     global g_msg_to_client
     test_msg = "ABC"
     test_msg = "ABC"
-    dut1 = env.get_dut("chat_client", "examples/protocols/asio/chat_client", dut_class=ESP32DUT)
+    dut1 = env.get_dut("chat_client", "examples/protocols/asio/chat_client", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "asio_chat_client.bin")
     binary_file = os.path.join(dut1.app.binary_path, "asio_chat_client.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("asio_chat_client_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("asio_chat_client_size", bin_size // 1024)
+    ttfw_idf.log_performance("asio_chat_client_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("asio_chat_client_size", bin_size // 1024)
     # 1. start a tcp server on the host
     # 1. start a tcp server on the host
     host_ip = get_my_ip()
     host_ip = get_my_ip()
     thread1 = Thread(target=chat_server_sketch, args=(host_ip,))
     thread1 = Thread(target=chat_server_sketch, args=(host_ip,))

+ 5 - 18
examples/protocols/asio/chat_server/asio_chat_server_test.py

@@ -1,24 +1,11 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 
 
+import ttfw_idf
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
 
 
-
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_asio_chat_server(env, extra_data):
 def test_examples_protocol_asio_chat_server(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -28,12 +15,12 @@ def test_examples_protocol_asio_chat_server(env, extra_data):
       4. Test evaluates received test message from server
       4. Test evaluates received test message from server
     """
     """
     test_msg = b"   4ABC\n"
     test_msg = b"   4ABC\n"
-    dut1 = env.get_dut("chat_server", "examples/protocols/asio/chat_server", dut_class=ESP32DUT)
+    dut1 = env.get_dut("chat_server", "examples/protocols/asio/chat_server", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "asio_chat_server.bin")
     binary_file = os.path.join(dut1.app.binary_path, "asio_chat_server.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("asio_chat_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("asio_chat_server_size", bin_size // 1024)
+    ttfw_idf.log_performance("asio_chat_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("asio_chat_server_size", bin_size // 1024)
     # 1. start test
     # 1. start test
     dut1.start_app()
     dut1.start_app()
     # 2. get the server IP address
     # 2. get the server IP address

+ 5 - 19
examples/protocols/asio/tcp_echo_server/asio_tcp_server_test.py

@@ -1,25 +1,11 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 
 
+import ttfw_idf
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
 
 
-    import IDF
-
-
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_asio_tcp_server(env, extra_data):
 def test_examples_protocol_asio_tcp_server(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -30,12 +16,12 @@ def test_examples_protocol_asio_tcp_server(env, extra_data):
       5. Test evaluates received test message on server stdout
       5. Test evaluates received test message on server stdout
     """
     """
     test_msg = b"echo message from client to server"
     test_msg = b"echo message from client to server"
-    dut1 = env.get_dut("tcp_echo_server", "examples/protocols/asio/tcp_echo_server", dut_class=ESP32DUT)
+    dut1 = env.get_dut("tcp_echo_server", "examples/protocols/asio/tcp_echo_server", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "asio_tcp_echo_server.bin")
     binary_file = os.path.join(dut1.app.binary_path, "asio_tcp_echo_server.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("asio_tcp_echo_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("asio_tcp_echo_server_size", bin_size // 1024)
+    ttfw_idf.log_performance("asio_tcp_echo_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("asio_tcp_echo_server_size", bin_size // 1024)
     # 1. start test
     # 1. start test
     dut1.start_app()
     dut1.start_app()
     # 2. get the server IP address
     # 2. get the server IP address

+ 5 - 19
examples/protocols/asio/udp_echo_server/asio_udp_server_test.py

@@ -1,25 +1,11 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 
 
+import ttfw_idf
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
 
 
-    import IDF
-
-
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_asio_udp_server(env, extra_data):
 def test_examples_protocol_asio_udp_server(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -30,12 +16,12 @@ def test_examples_protocol_asio_udp_server(env, extra_data):
       5. Test evaluates received test message on server stdout
       5. Test evaluates received test message on server stdout
     """
     """
     test_msg = b"echo message from client to server"
     test_msg = b"echo message from client to server"
-    dut1 = env.get_dut("udp_echo_server", "examples/protocols/asio/udp_echo_server", dut_class=ESP32DUT)
+    dut1 = env.get_dut("udp_echo_server", "examples/protocols/asio/udp_echo_server", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "asio_udp_echo_server.bin")
     binary_file = os.path.join(dut1.app.binary_path, "asio_udp_echo_server.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("asio_udp_echo_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("asio_udp_echo_server_size", bin_size // 1024)
+    ttfw_idf.log_performance("asio_udp_echo_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("asio_udp_echo_server_size", bin_size // 1024)
     # 1. start test
     # 1. start test
     dut1.start_app()
     dut1.start_app()
     # 2. get the server IP address
     # 2. get the server IP address

+ 5 - 18
examples/protocols/esp_http_client/esp_http_client_test.py

@@ -1,35 +1,22 @@
 import re
 import re
 import os
 import os
-import sys
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
+import ttfw_idf
 
 
-    import IDF
 
 
-
-@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI", ignore=True)
 def test_examples_protocol_esp_http_client(env, extra_data):
 def test_examples_protocol_esp_http_client(env, extra_data):
     """
     """
     steps: |
     steps: |
       1. join AP
       1. join AP
       2. Send HTTP request to httpbin.org
       2. Send HTTP request to httpbin.org
     """
     """
-    dut1 = env.get_dut("esp_http_client", "examples/protocols/esp_http_client", dut_class=ESP32DUT)
+    dut1 = env.get_dut("esp_http_client", "examples/protocols/esp_http_client", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "esp-http-client-example.bin")
     binary_file = os.path.join(dut1.app.binary_path, "esp-http-client-example.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("esp_http_client_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("esp_http_client_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("esp_http_client_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("esp_http_client_bin_size", bin_size // 1024)
     # start test
     # start test
     dut1.start_app()
     dut1.start_app()
     dut1.expect("Connected to AP, begin http example", timeout=30)
     dut1.expect("Connected to AP, begin http example", timeout=30)

+ 7 - 24
examples/protocols/http_server/advanced_tests/http_server_advanced_test.py

@@ -19,31 +19,14 @@ from __future__ import print_function
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 import re
 import re
 import os
 import os
-import sys
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-
-    import IDF
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
+from idf_http_server_test import test as client
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
 # > make print_flash_cmd | tail -n 1 > build/download.config
 # > make print_flash_cmd | tail -n 1 > build/download.config
-# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
-
-# Import client module
-# TODO: replace with import
-expath = os.path.dirname(os.path.realpath(__file__))
-client = Utility.load_source(expath + "/scripts/test.py")
-
 
 
 # Due to connectivity issues (between runner host and DUT) in the runner environment,
 # Due to connectivity issues (between runner host and DUT) in the runner environment,
 # some of the `advanced_tests` are ignored. These tests are intended for verifying
 # some of the `advanced_tests` are ignored. These tests are intended for verifying
@@ -51,16 +34,16 @@ client = Utility.load_source(expath + "/scripts/test.py")
 # of large HTTP packets and malformed requests, running multiple parallel sessions, etc.
 # of large HTTP packets and malformed requests, running multiple parallel sessions, etc.
 # It is advised that all these tests be run locally, when making changes or adding new
 # It is advised that all these tests be run locally, when making changes or adding new
 # features to this component.
 # features to this component.
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_http_server_advanced(env, extra_data):
 def test_examples_protocol_http_server_advanced(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("http_server", "examples/protocols/http_server/advanced_tests", dut_class=ESP32DUT)
+    dut1 = env.get_dut("http_server", "examples/protocols/http_server/advanced_tests", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "tests.bin")
     binary_file = os.path.join(dut1.app.binary_path, "tests.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("http_server_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("http_server_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting http_server advanced test app")
     Utility.console_log("Starting http_server advanced test app")

+ 8 - 22
examples/protocols/http_server/persistent_sockets/http_server_persistence_test.py

@@ -21,42 +21,28 @@ from builtins import str
 from builtins import range
 from builtins import range
 import re
 import re
 import os
 import os
-import sys
 import random
 import random
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import Utility
+from tiny_test_fw import Utility
+import ttfw_idf
+from idf_http_server_test import adder as client
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
 # > make print_flash_cmd | tail -n 1 > build/download.config
 # > make print_flash_cmd | tail -n 1 > build/download.config
-# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
-
-# Import client module
-# TODO: replace with import
-expath = os.path.dirname(os.path.realpath(__file__))
-client = Utility.load_source(expath + "/scripts/adder.py")
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_http_server_persistence(env, extra_data):
 def test_examples_protocol_http_server_persistence(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("http_server", "examples/protocols/http_server/persistent_sockets", dut_class=ESP32DUT)
+    dut1 = env.get_dut("http_server", "examples/protocols/http_server/persistent_sockets",
+                       dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "persistent_sockets.bin")
     binary_file = os.path.join(dut1.app.binary_path, "persistent_sockets.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("http_server_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("http_server_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting http_server persistance test app")
     Utility.console_log("Starting http_server persistance test app")

+ 7 - 21
examples/protocols/http_server/simple/http_server_simple_test.py

@@ -20,43 +20,29 @@ from __future__ import unicode_literals
 from builtins import range
 from builtins import range
 import re
 import re
 import os
 import os
-import sys
 import string
 import string
 import random
 import random
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # This environment variable is expected on the host machine
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
+from tiny_test_fw import Utility
+import ttfw_idf
+from idf_http_server_test import client
 
 
-    import IDF
-
-import Utility
 
 
 # When running on local machine execute the following before running this script
 # When running on local machine execute the following before running this script
 # > make app bootloader
 # > make app bootloader
 # > make print_flash_cmd | tail -n 1 > build/download.config
 # > make print_flash_cmd | tail -n 1 > build/download.config
-# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
-
-# Import client module
-expath = os.path.dirname(os.path.realpath(__file__))
-client = Utility.load_source(expath + "/scripts/client.py")
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_http_server_simple(env, extra_data):
 def test_examples_protocol_http_server_simple(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("http_server", "examples/protocols/http_server/simple", dut_class=ESP32DUT)
+    dut1 = env.get_dut("http_server", "examples/protocols/http_server/simple", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "simple.bin")
     binary_file = os.path.join(dut1.app.binary_path, "simple.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("http_server_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("http_server_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     Utility.console_log("Starting http_server simple test app")
     Utility.console_log("Starting http_server simple test app")

+ 5 - 17
examples/protocols/https_request/example_test.py

@@ -1,22 +1,10 @@
 import re
 import re
 import os
 import os
-import sys
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI", ignore=True)
 def test_examples_protocol_https_request(env, extra_data):
 def test_examples_protocol_https_request(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -24,12 +12,12 @@ def test_examples_protocol_https_request(env, extra_data):
       2. connect to www.howsmyssl.com:443
       2. connect to www.howsmyssl.com:443
       3. send http request
       3. send http request
     """
     """
-    dut1 = env.get_dut("https_request", "examples/protocols/https_request", dut_class=ESP32DUT)
+    dut1 = env.get_dut("https_request", "examples/protocols/https_request", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "https_request.bin")
     binary_file = os.path.join(dut1.app.binary_path, "https_request.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("https_request_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("https_request_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("https_request_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("https_request_bin_size", bin_size // 1024)
     # start test
     # start test
     dut1.start_app()
     dut1.start_app()
     dut1.expect("Connection established...", timeout=30)
     dut1.expect("Connection established...", timeout=30)

+ 6 - 21
examples/protocols/mdns/mdns_example_test.py

@@ -1,6 +1,5 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 import time
 import time
 import struct
 import struct
@@ -8,22 +7,8 @@ import dpkt
 import dpkt.dns
 import dpkt.dns
 from threading import Thread, Event
 from threading import Thread, Event
 
 
-
-# this is a test case write with tiny-test-fw.
-# to run test cases outside tiny-test-fw,
-# we need to set environment variable `TEST_FW_PATH`,
-# then get and insert `TEST_FW_PATH` to sys path before import FW module
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
 # g_run_server = True
 # g_run_server = True
 # g_done = False
 # g_done = False
@@ -103,7 +88,7 @@ def mdns_server(esp_host):
             continue
             continue
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_mdns(env, extra_data):
 def test_examples_protocol_mdns(env, extra_data):
     global stop_mdns_server
     global stop_mdns_server
     """
     """
@@ -113,12 +98,12 @@ def test_examples_protocol_mdns(env, extra_data):
       3. check the mdns name is accessible
       3. check the mdns name is accessible
       4. check DUT output if mdns advertized host is resolved
       4. check DUT output if mdns advertized host is resolved
     """
     """
-    dut1 = env.get_dut("mdns-test", "examples/protocols/mdns", dut_class=ESP32DUT)
+    dut1 = env.get_dut("mdns-test", "examples/protocols/mdns", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "mdns-test.bin")
     binary_file = os.path.join(dut1.app.binary_path, "mdns-test.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("mdns-test_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("mdns-test_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("mdns-test_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("mdns-test_bin_size", bin_size // 1024)
     # 1. start mdns application
     # 1. start mdns application
     dut1.start_app()
     dut1.start_app()
     # 2. get the dut host name (and IP address)
     # 2. get the dut host name (and IP address)

+ 4 - 13
examples/protocols/modbus/serial/example_test.py

@@ -2,20 +2,11 @@
 from __future__ import print_function
 from __future__ import print_function
 
 
 import os
 import os
-import sys
 import re
 import re
 import logging
 import logging
 from threading import Thread
 from threading import Thread
 
 
-try:
-    import IDF
-except ImportError:
-    # The test cause is dependent on the Tiny Test Framework. Ensure the
-    # `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 LOG_LEVEL = logging.DEBUG
 LOG_LEVEL = logging.DEBUG
 LOGGER_NAME = "modbus_test"
 LOGGER_NAME = "modbus_test"
@@ -174,13 +165,13 @@ def test_check_mode(dut=None, mode_str=None, value=None):
     return False
     return False
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_T2_RS485')
+@ttfw_idf.idf_example_test(env_tag='Example_T2_RS485')
 def test_modbus_communication(env, comm_mode):
 def test_modbus_communication(env, comm_mode):
     global logger
     global logger
 
 
     # Get device under test. "dut1 - master", "dut2 - slave" must be properly connected through RS485 interface driver
     # Get device under test. "dut1 - master", "dut2 - slave" must be properly connected through RS485 interface driver
-    dut_master = env.get_dut("modbus_master", "examples/protocols/modbus/serial/mb_master")
-    dut_slave = env.get_dut("modbus_slave", "examples/protocols/modbus/serial/mb_slave")
+    dut_master = env.get_dut("modbus_master", "examples/protocols/modbus/serial/mb_master", dut_class=ttfw_idf.ESP32DUT)
+    dut_slave = env.get_dut("modbus_slave", "examples/protocols/modbus/serial/mb_slave", dut_class=ttfw_idf.ESP32DUT)
 
 
     try:
     try:
         logger.debug("Environment vars: %s\r\n" % os.environ)
         logger.debug("Environment vars: %s\r\n" % os.environ)

+ 7 - 20
examples/protocols/mqtt/ssl/mqtt_ssl_example_test.py

@@ -8,21 +8,8 @@ import ssl
 import paho.mqtt.client as mqtt
 import paho.mqtt.client as mqtt
 from threading import Thread, Event
 from threading import Thread, Event
 
 
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
 
 
 event_client_connected = Event()
 event_client_connected = Event()
@@ -73,7 +60,7 @@ def on_message(client, userdata, msg):
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_mqtt_ssl(env, extra_data):
 def test_examples_protocol_mqtt_ssl(env, extra_data):
     broker_url = ""
     broker_url = ""
     broker_port = 0
     broker_port = 0
@@ -85,13 +72,13 @@ def test_examples_protocol_mqtt_ssl(env, extra_data):
       4. Test ESP32 client received correct qos0 message
       4. Test ESP32 client received correct qos0 message
       5. Test python client receives binary data from running partition and compares it with the binary
       5. Test python client receives binary data from running partition and compares it with the binary
     """
     """
-    dut1 = env.get_dut("mqtt_ssl", "examples/protocols/mqtt/ssl", dut_class=ESP32DUT)
+    dut1 = env.get_dut("mqtt_ssl", "examples/protocols/mqtt/ssl", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_ssl.bin")
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_ssl.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("mqtt_ssl_bin_size", "{}KB"
-                        .format(bin_size // 1024))
-    IDF.check_performance("mqtt_ssl_size", bin_size // 1024)
+    ttfw_idf.log_performance("mqtt_ssl_bin_size", "{}KB"
+                             .format(bin_size // 1024))
+    ttfw_idf.check_performance("mqtt_ssl_size", bin_size // 1024)
     # Look for host:port in sdkconfig
     # Look for host:port in sdkconfig
     try:
     try:
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])

+ 6 - 19
examples/protocols/mqtt/tcp/mqtt_tcp_example_test.py

@@ -6,21 +6,8 @@ from threading import Thread
 import struct
 import struct
 import time
 import time
 
 
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
 msgid = -1
 msgid = -1
 
 
@@ -66,7 +53,7 @@ def mqqt_server_sketch(my_ip, port):
     print("server closed")
     print("server closed")
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_mqtt_qos1(env, extra_data):
 def test_examples_protocol_mqtt_qos1(env, extra_data):
     global msgid
     global msgid
     """
     """
@@ -76,12 +63,12 @@ def test_examples_protocol_mqtt_qos1(env, extra_data):
       3. Test evaluates that qos1 message is queued and removed from queued after ACK received
       3. Test evaluates that qos1 message is queued and removed from queued after ACK received
       4. Test the broker received the same message id evaluated in step 3
       4. Test the broker received the same message id evaluated in step 3
     """
     """
-    dut1 = env.get_dut("mqtt_tcp", "examples/protocols/mqtt/tcp", dut_class=ESP32DUT)
+    dut1 = env.get_dut("mqtt_tcp", "examples/protocols/mqtt/tcp", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_tcp.bin")
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_tcp.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("mqtt_tcp_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("mqtt_tcp_size", bin_size // 1024)
+    ttfw_idf.log_performance("mqtt_tcp_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("mqtt_tcp_size", bin_size // 1024)
     # 1. start mqtt broker sketch
     # 1. start mqtt broker sketch
     host_ip = get_my_ip()
     host_ip = get_my_ip()
     thread1 = Thread(target=mqqt_server_sketch, args=(host_ip,1883))
     thread1 = Thread(target=mqqt_server_sketch, args=(host_ip,1883))

+ 6 - 19
examples/protocols/mqtt/ws/mqtt_ws_example_test.py

@@ -7,21 +7,8 @@ import sys
 import paho.mqtt.client as mqtt
 import paho.mqtt.client as mqtt
 from threading import Thread, Event
 from threading import Thread, Event
 
 
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except Exception:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
 event_client_connected = Event()
 event_client_connected = Event()
 event_stop_client = Event()
 event_stop_client = Event()
@@ -52,7 +39,7 @@ def on_message(client, userdata, msg):
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_mqtt_ws(env, extra_data):
 def test_examples_protocol_mqtt_ws(env, extra_data):
     broker_url = ""
     broker_url = ""
     broker_port = 0
     broker_port = 0
@@ -63,12 +50,12 @@ def test_examples_protocol_mqtt_ws(env, extra_data):
       3. Test evaluates it received correct qos0 message
       3. Test evaluates it received correct qos0 message
       4. Test ESP32 client received correct qos0 message
       4. Test ESP32 client received correct qos0 message
     """
     """
-    dut1 = env.get_dut("mqtt_websocket", "examples/protocols/mqtt/ws", dut_class=ESP32DUT)
+    dut1 = env.get_dut("mqtt_websocket", "examples/protocols/mqtt/ws", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket.bin")
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("mqtt_websocket_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("mqtt_websocket_size", bin_size // 1024)
+    ttfw_idf.log_performance("mqtt_websocket_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("mqtt_websocket_size", bin_size // 1024)
     # Look for host:port in sdkconfig
     # Look for host:port in sdkconfig
     try:
     try:
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])

+ 6 - 18
examples/protocols/mqtt/wss/mqtt_wss_example_test.py

@@ -8,21 +8,9 @@ import ssl
 import paho.mqtt.client as mqtt
 import paho.mqtt.client as mqtt
 from threading import Thread, Event
 from threading import Thread, Event
 
 
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
 
 
 event_client_connected = Event()
 event_client_connected = Event()
 event_stop_client = Event()
 event_stop_client = Event()
@@ -53,7 +41,7 @@ def on_message(client, userdata, msg):
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
     message_log += "Received data:" + msg.topic + " " + payload + "\n"
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_mqtt_wss(env, extra_data):
 def test_examples_protocol_mqtt_wss(env, extra_data):
     broker_url = ""
     broker_url = ""
     broker_port = 0
     broker_port = 0
@@ -64,12 +52,12 @@ def test_examples_protocol_mqtt_wss(env, extra_data):
       3. Test evaluates it received correct qos0 message
       3. Test evaluates it received correct qos0 message
       4. Test ESP32 client received correct qos0 message
       4. Test ESP32 client received correct qos0 message
     """
     """
-    dut1 = env.get_dut("mqtt_websocket_secure", "examples/protocols/mqtt/wss", dut_class=ESP32DUT)
+    dut1 = env.get_dut("mqtt_websocket_secure", "examples/protocols/mqtt/wss", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket_secure.bin")
     binary_file = os.path.join(dut1.app.binary_path, "mqtt_websocket_secure.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("mqtt_websocket_secure_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("mqtt_websocket_secure_size", bin_size // 1024)
+    ttfw_idf.log_performance("mqtt_websocket_secure_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("mqtt_websocket_secure_size", bin_size // 1024)
     # Look for host:port in sdkconfig
     # Look for host:port in sdkconfig
     try:
     try:
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])
         value = re.search(r'\:\/\/([^:]+)\:([0-9]+)', dut1.app.get_sdkconfig()["CONFIG_BROKER_URI"])

+ 5 - 14
examples/protocols/websocket/example_test.py

@@ -1,19 +1,10 @@
 import re
 import re
 import os
 import os
-import sys
-import IDF
-from IDF.IDFDUT import ESP32DUT
 
 
-# this is a test case write with tiny-test-fw.
-# to run test cases outside tiny-test-fw,
-# we need to set environment variable `TEST_FW_PATH`,
-# then get and insert `TEST_FW_PATH` to sys path before import FW module
-test_fw_path = os.getenv("TEST_FW_PATH")
-if test_fw_path and test_fw_path not in sys.path:
-    sys.path.insert(0, test_fw_path)
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI", ignore=True)
 def test_examples_protocol_websocket(env, extra_data):
 def test_examples_protocol_websocket(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -21,12 +12,12 @@ def test_examples_protocol_websocket(env, extra_data):
       2. connect to ws://echo.websocket.org
       2. connect to ws://echo.websocket.org
       3. send and receive data
       3. send and receive data
     """
     """
-    dut1 = env.get_dut("websocket", "examples/protocols/websocket", dut_class=ESP32DUT)
+    dut1 = env.get_dut("websocket", "examples/protocols/websocket", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "websocket-example.bin")
     binary_file = os.path.join(dut1.app.binary_path, "websocket-example.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("websocket_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("websocket_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("websocket_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("websocket_bin_size", bin_size // 1024)
     # start test
     # start test
     dut1.start_app()
     dut1.start_app()
     dut1.expect("Waiting for wifi ...")
     dut1.expect("Waiting for wifi ...")

+ 6 - 21
examples/provisioning/ble_prov/ble_prov_test.py

@@ -17,40 +17,25 @@
 from __future__ import print_function
 from __future__ import print_function
 import re
 import re
 import os
 import os
-import sys
 import time
 import time
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-try:
-    import esp_prov
-except ImportError:
-    esp_prov_path = os.getenv("IDF_PATH") + "/tools/esp_prov"
-    if esp_prov_path and esp_prov_path not in sys.path:
-        sys.path.insert(0, esp_prov_path)
-    import esp_prov
+import ttfw_idf
+import esp_prov
 
 
 # Have esp_prov throw exception
 # Have esp_prov throw exception
 esp_prov.config_throw_except = True
 esp_prov.config_throw_except = True
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_examples_provisioning_ble(env, extra_data):
 def test_examples_provisioning_ble(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("ble_prov", "examples/provisioning/ble_prov", dut_class=ESP32DUT)
+    dut1 = env.get_dut("ble_prov", "examples/provisioning/ble_prov", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "ble_prov.bin")
     binary_file = os.path.join(dut1.app.binary_path, "ble_prov.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("ble_prov_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("ble_prov_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("ble_prov_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("ble_prov_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     dut1.start_app()
     dut1.start_app()

+ 6 - 21
examples/provisioning/manager/wifi_prov_mgr_test.py

@@ -17,40 +17,25 @@
 from __future__ import print_function
 from __future__ import print_function
 import re
 import re
 import os
 import os
-import sys
 import time
 import time
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-try:
-    import esp_prov
-except ImportError:
-    esp_prov_path = os.getenv("IDF_PATH") + "/tools/esp_prov"
-    if esp_prov_path and esp_prov_path not in sys.path:
-        sys.path.insert(0, esp_prov_path)
-    import esp_prov
+import ttfw_idf
+import esp_prov
 
 
 # Have esp_prov throw exception
 # Have esp_prov throw exception
 esp_prov.config_throw_except = True
 esp_prov.config_throw_except = True
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_examples_wifi_prov_mgr(env, extra_data):
 def test_examples_wifi_prov_mgr(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("wifi_prov_mgr", "examples/provisioning/manager", dut_class=ESP32DUT)
+    dut1 = env.get_dut("wifi_prov_mgr", "examples/provisioning/manager", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "wifi_prov_mgr.bin")
     binary_file = os.path.join(dut1.app.binary_path, "wifi_prov_mgr.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("wifi_prov_mgr_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("wifi_prov_mgr_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("wifi_prov_mgr_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("wifi_prov_mgr_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     dut1.start_app()
     dut1.start_app()

+ 7 - 29
examples/provisioning/softap_prov/softap_prov_test.py

@@ -17,48 +17,26 @@
 from __future__ import print_function
 from __future__ import print_function
 import re
 import re
 import os
 import os
-import sys
 import time
 import time
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-try:
-    import esp_prov
-except ImportError:
-    esp_prov_path = os.getenv("IDF_PATH") + "/tools/esp_prov"
-    if esp_prov_path and esp_prov_path not in sys.path:
-        sys.path.insert(0, esp_prov_path)
-    import esp_prov
-
-try:
-    import wifi_tools
-except ImportError:
-    wifi_tools_path = os.getenv("IDF_PATH") + "/examples/provisioning/softap_prov/utils"
-    if wifi_tools_path and wifi_tools_path not in sys.path:
-        sys.path.insert(0, wifi_tools_path)
-    import wifi_tools
+import ttfw_idf
+import esp_prov
+import wifi_tools
 
 
 # Have esp_prov throw exception
 # Have esp_prov throw exception
 esp_prov.config_throw_except = True
 esp_prov.config_throw_except = True
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI_BT")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI_BT")
 def test_examples_provisioning_softap(env, extra_data):
 def test_examples_provisioning_softap(env, extra_data):
     # Acquire DUT
     # Acquire DUT
-    dut1 = env.get_dut("softap_prov", "examples/provisioning/softap_prov", dut_class=ESP32DUT)
+    dut1 = env.get_dut("softap_prov", "examples/provisioning/softap_prov", dut_class=ttfw_idf.ESP32DUT)
 
 
     # Get binary file
     # Get binary file
     binary_file = os.path.join(dut1.app.binary_path, "softap_prov.bin")
     binary_file = os.path.join(dut1.app.binary_path, "softap_prov.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("softap_prov_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("softap_prov_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("softap_prov_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("softap_prov_bin_size", bin_size // 1024)
 
 
     # Upload binary and start testing
     # Upload binary and start testing
     dut1.start_app()
     dut1.start_app()

+ 3 - 13
examples/security/flash_encryption/example_test.py

@@ -1,15 +1,5 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
 # To prepare a test runner for this example:
 # To prepare a test runner for this example:
@@ -19,9 +9,9 @@ except ImportError:
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CONFIG 0xf
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CONFIG 0xf
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CNT 0x1
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_efuse FLASH_CRYPT_CNT 0x1
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_key flash_encryption key.bin
 #   espefuse.py --do-not-confirm -p $ESPPORT burn_key flash_encryption key.bin
-@IDF.idf_example_test(env_tag='Example_Flash_Encryption')
+@ttfw_idf.idf_example_test(env_tag='Example_Flash_Encryption')
 def test_examples_security_flash_encryption(env, extra_data):
 def test_examples_security_flash_encryption(env, extra_data):
-    dut = env.get_dut('flash_encryption', 'examples/security/flash_encryption', dut_class=ESP32DUT)
+    dut = env.get_dut('flash_encryption', 'examples/security/flash_encryption', dut_class=ttfw_idf.ESP32DUT)
     # start test
     # start test
     dut.start_app()
     dut.start_app()
     lines = [
     lines = [

+ 3 - 13
examples/storage/ext_flash_fatfs/example_test.py

@@ -1,21 +1,11 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-try:
-    import IDF
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
-from IDF.IDFDUT import ESP32DUT
 
 
-
-@IDF.idf_example_test(env_tag='Example_ExtFlash')
+@ttfw_idf.idf_example_test(env_tag='Example_ExtFlash')
 def test_examples_storage_ext_flash_fatfs(env, extra_data):
 def test_examples_storage_ext_flash_fatfs(env, extra_data):
-    dut = env.get_dut('ext_flash_fatfs', 'examples/storage/ext_flash_fatfs', dut_class=ESP32DUT)
+    dut = env.get_dut('ext_flash_fatfs', 'examples/storage/ext_flash_fatfs', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     dut.expect('Initialized external Flash')
     dut.expect('Initialized external Flash')

+ 3 - 10
examples/storage/parttool/example_test.py

@@ -3,19 +3,12 @@ import os
 import sys
 import sys
 import subprocess
 import subprocess
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_parttool(env, extra_data):
 def test_examples_parttool(env, extra_data):
-    dut = env.get_dut('parttool', 'examples/storage/parttool', dut_class=ESP32DUT)
+    dut = env.get_dut('parttool', 'examples/storage/parttool', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app(False)
     dut.start_app(False)
 
 
     # Verify factory firmware
     # Verify factory firmware

+ 3 - 11
examples/storage/spiffsgen/example_test.py

@@ -1,22 +1,14 @@
 from __future__ import print_function
 from __future__ import print_function
 import os
 import os
-import sys
 import hashlib
 import hashlib
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_spiffsgen(env, extra_data):
 def test_examples_spiffsgen(env, extra_data):
     # Test with default build configurations
     # Test with default build configurations
-    dut = env.get_dut('spiffsgen', 'examples/storage/spiffsgen', dut_class=ESP32DUT)
+    dut = env.get_dut('spiffsgen', 'examples/storage/spiffsgen', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'spiffs_image')
     base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'spiffs_image')

+ 2 - 10
examples/system/console/example_test.py

@@ -1,17 +1,9 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-try:
-    import IDF
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_system_console(env, extra_data):
 def test_examples_system_console(env, extra_data):
     dut = env.get_dut('console_example', 'examples/system/console', app_config_name='history')
     dut = env.get_dut('console_example', 'examples/system/console', app_config_name='history')
     print("Using binary path: {}".format(dut.app.binary_path))
     print("Using binary path: {}".format(dut.app.binary_path))

+ 3 - 12
examples/system/cpp_exceptions/example_test.py

@@ -1,20 +1,11 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_system_cpp_exceptions(env, extra_data):
 def test_examples_system_cpp_exceptions(env, extra_data):
-    dut = env.get_dut('cpp_exceptions_example', 'examples/system/cpp_exceptions', dut_class=ESP32DUT)
+    dut = env.get_dut('cpp_exceptions_example', 'examples/system/cpp_exceptions', dut_class=ttfw_idf.ESP32DUT)
     # start test
     # start test
     dut.start_app()
     dut.start_app()
     lines = ['app_main starting',
     lines = ['app_main starting',

+ 6 - 18
examples/system/cpp_rtti/example_test.py

@@ -1,23 +1,11 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
-
-try:
-    import IDF
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-
-@IDF.idf_example_test(env_tag='Example_WIFI')
+
+import ttfw_idf
+
+
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_cpp_rtti_example(env, extra_data):
 def test_cpp_rtti_example(env, extra_data):
-    dut = env.get_dut('cpp_rtti', 'examples/system/cpp_rtti')
+    dut = env.get_dut('cpp_rtti', 'examples/system/cpp_rtti', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     dut.expect('Type name of std::cout is: std::ostream')
     dut.expect('Type name of std::cout is: std::ostream')

+ 3 - 15
examples/system/esp_event/default_event_loop/example_test.py

@@ -1,18 +1,6 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
-
-try:
-    import IDF
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+
+import ttfw_idf
 
 
 # Timer events
 # Timer events
 TIMER_EVENT_LIMIT = 3
 TIMER_EVENT_LIMIT = 3
@@ -91,7 +79,7 @@ def _test_iteration_events(dut):
     print("Deleted task event source")
     print("Deleted task event source")
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_default_event_loop_example(env, extra_data):
 def test_default_event_loop_example(env, extra_data):
     dut = env.get_dut('default_event_loop', 'examples/system/esp_event/default_event_loop')
     dut = env.get_dut('default_event_loop', 'examples/system/esp_event/default_event_loop')
 
 

+ 4 - 16
examples/system/esp_event/user_event_loops/example_test.py

@@ -1,18 +1,6 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
-
-try:
-    import IDF
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+
+import ttfw_idf
 
 
 TASK_ITERATION_LIMIT = 10
 TASK_ITERATION_LIMIT = 10
 
 
@@ -20,9 +8,9 @@ TASK_ITERATION_POSTING = "posting TASK_EVENTS:TASK_ITERATION_EVENT to {}, iterat
 TASK_ITERATION_HANDLING = "handling TASK_EVENTS:TASK_ITERATION_EVENT from {}, iteration {}"
 TASK_ITERATION_HANDLING = "handling TASK_EVENTS:TASK_ITERATION_EVENT from {}, iteration {}"
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_user_event_loops_example(env, extra_data):
 def test_user_event_loops_example(env, extra_data):
-    dut = env.get_dut('user_event_loops', 'examples/system/esp_event/user_event_loops')
+    dut = env.get_dut('user_event_loops', 'examples/system/esp_event/user_event_loops', dut_class=ttfw_idf.ESP32DUT)
 
 
     dut.start_app()
     dut.start_app()
 
 

+ 4 - 17
examples/system/esp_timer/example_test.py

@@ -1,20 +1,7 @@
 from __future__ import print_function
 from __future__ import print_function
 import re
 import re
-import os
-import sys
-
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+
+import ttfw_idf
 
 
 STARTING_TIMERS_REGEX = re.compile(r'Started timers, time since boot: (\d+) us')
 STARTING_TIMERS_REGEX = re.compile(r'Started timers, time since boot: (\d+) us')
 
 
@@ -38,9 +25,9 @@ LIGHT_SLEEP_TIME = 500000
 ONE_SHOT_TIMER_PERIOD = 5000000
 ONE_SHOT_TIMER_PERIOD = 5000000
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_system_esp_timer(env, extra_data):
 def test_examples_system_esp_timer(env, extra_data):
-    dut = env.get_dut('esp_timer_example', 'examples/system/esp_timer', dut_class=ESP32DUT)
+    dut = env.get_dut('esp_timer_example', 'examples/system/esp_timer', dut_class=ttfw_idf.ESP32DUT)
     # start test
     # start test
     dut.start_app()
     dut.start_app()
     groups = dut.expect(STARTING_TIMERS_REGEX, timeout=30)
     groups = dut.expect(STARTING_TIMERS_REGEX, timeout=30)

+ 3 - 16
examples/system/freertos/real_time_stats/example_test.py

@@ -1,27 +1,14 @@
 from __future__ import print_function
 from __future__ import print_function
-import os
-import sys
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 STATS_TASK_ITERS = 3
 STATS_TASK_ITERS = 3
 STATS_TASK_EXPECT = "Real time stats obtained"
 STATS_TASK_EXPECT = "Real time stats obtained"
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_real_time_stats_example(env, extra_data):
 def test_real_time_stats_example(env, extra_data):
-    dut = env.get_dut('real_time_stats', 'examples/system/freertos/real_time_stats', dut_class=ESP32DUT)
+    dut = env.get_dut('real_time_stats', 'examples/system/freertos/real_time_stats', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     for iteration in range(0, STATS_TASK_ITERS):
     for iteration in range(0, STATS_TASK_ITERS):

+ 3 - 12
examples/system/light_sleep/example_test.py

@@ -1,17 +1,8 @@
 from __future__ import print_function
 from __future__ import print_function
 import re
 import re
-import os
-import sys
 import time
 import time
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
+import ttfw_idf
 
 
 ENTERING_SLEEP_STR = 'Entering light sleep'
 ENTERING_SLEEP_STR = 'Entering light sleep'
 EXIT_SLEEP_REGEX = re.compile(r'Returned from light sleep, reason: (\w+), t=(\d+) ms, slept for (\d+) ms')
 EXIT_SLEEP_REGEX = re.compile(r'Returned from light sleep, reason: (\w+), t=(\d+) ms, slept for (\d+) ms')
@@ -20,9 +11,9 @@ WAITING_FOR_GPIO_STR = 'Waiting for GPIO0 to go high...'
 WAKEUP_INTERVAL_MS = 2000
 WAKEUP_INTERVAL_MS = 2000
 
 
 
 
-@IDF.idf_example_test(env_tag='Example_WIFI')
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_examples_system_light_sleep(env, extra_data):
 def test_examples_system_light_sleep(env, extra_data):
-    dut = env.get_dut('light_sleep_example', 'examples/system/light_sleep', dut_class=ESP32DUT)
+    dut = env.get_dut('light_sleep_example', 'examples/system/light_sleep', dut_class=ttfw_idf.ESP32DUT)
     dut.start_app()
     dut.start_app()
 
 
     # Ensure DTR and RTS are de-asserted for proper control of GPIO0
     # Ensure DTR and RTS are de-asserted for proper control of GPIO0

+ 5 - 16
examples/system/ota/otatool/example_test.py

@@ -3,23 +3,12 @@ import os
 import sys
 import sys
 import subprocess
 import subprocess
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv('TEST_FW_PATH')
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-
-@IDF.idf_example_test(env_tag='Example_WIFI')
+import ttfw_idf
+
+
+@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
 def test_otatool_example(env, extra_data):
 def test_otatool_example(env, extra_data):
-    dut = env.get_dut('otatool', 'examples/system/ota/otatool', dut_class=ESP32DUT)
+    dut = env.get_dut('otatool', 'examples/system/ota/otatool', dut_class=ttfw_idf.ESP32DUT)
 
 
     # Verify factory firmware
     # Verify factory firmware
     dut.start_app()
     dut.start_app()

+ 6 - 19
examples/system/ota/simple_ota_example/example_test.py

@@ -1,26 +1,13 @@
 import re
 import re
 import os
 import os
-import sys
 import socket
 import socket
 import BaseHTTPServer
 import BaseHTTPServer
 import SimpleHTTPServer
 import SimpleHTTPServer
 from threading import Thread
 from threading import Thread
 import ssl
 import ssl
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
+from tiny_test_fw import DUT
+import ttfw_idf
 
 
 server_cert = "-----BEGIN CERTIFICATE-----\n" \
 server_cert = "-----BEGIN CERTIFICATE-----\n" \
               "MIIDXTCCAkWgAwIBAgIJAP4LF7E72HakMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n"\
               "MIIDXTCCAkWgAwIBAgIJAP4LF7E72HakMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n"\
@@ -108,7 +95,7 @@ def start_https_server(ota_image_dir, server_ip, server_port):
     httpd.serve_forever()
     httpd.serve_forever()
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_WIFI")
+@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
 def test_examples_protocol_simple_ota_example(env, extra_data):
 def test_examples_protocol_simple_ota_example(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -116,12 +103,12 @@ def test_examples_protocol_simple_ota_example(env, extra_data):
       2. Fetch OTA image over HTTPS
       2. Fetch OTA image over HTTPS
       3. Reboot with the new OTA image
       3. Reboot with the new OTA image
     """
     """
-    dut1 = env.get_dut("simple_ota_example", "examples/system/ota/simple_ota_example", dut_class=ESP32DUT)
+    dut1 = env.get_dut("simple_ota_example", "examples/system/ota/simple_ota_example", dut_class=ttfw_idf.ESP32DUT)
     # check and log bin size
     # check and log bin size
     binary_file = os.path.join(dut1.app.binary_path, "simple_ota.bin")
     binary_file = os.path.join(dut1.app.binary_path, "simple_ota.bin")
     bin_size = os.path.getsize(binary_file)
     bin_size = os.path.getsize(binary_file)
-    IDF.log_performance("simple_ota_bin_size", "{}KB".format(bin_size // 1024))
-    IDF.check_performance("simple_ota_bin_size", bin_size // 1024)
+    ttfw_idf.log_performance("simple_ota_bin_size", "{}KB".format(bin_size // 1024))
+    ttfw_idf.check_performance("simple_ota_bin_size", bin_size // 1024)
     # start test
     # start test
     host_ip = get_my_ip()
     host_ip = get_my_ip()
     thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, 8000))
     thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, 8000))

+ 23 - 41
examples/wifi/iperf/iperf_test.py

@@ -26,34 +26,13 @@ from builtins import range
 from builtins import object
 from builtins import object
 import re
 import re
 import os
 import os
-import sys
 import time
 import time
 import subprocess
 import subprocess
 
 
-try:
-    import IDF
-    from IDF.IDFDUT import ESP32DUT
-except ImportError:
-    # this is a test case write with tiny-test-fw.
-    # to run test cases outside tiny-test-fw,
-    # we need to set environment variable `TEST_FW_PATH`,
-    # then get and insert `TEST_FW_PATH` to sys path before import FW module
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    import IDF
-
-import DUT
-import TinyFW
-import Utility
-from Utility import (Attenuator, PowerControl, LineChart)
-
-try:
-    from test_report import (ThroughputForConfigsReport, ThroughputVsRssiReport)
-except ImportError:
-    # add current folder to system path for importing test_report
-    sys.path.append(os.path.dirname(__file__))
-    from test_report import (ThroughputForConfigsReport, ThroughputVsRssiReport)
+from tiny_test_fw import TinyFW, DUT, Utility
+import ttfw_idf
+from idf_iperf_test_util import (Attenuator, PowerControl, LineChart, TestReport)
+
 
 
 # configurations
 # configurations
 TEST_TIME = TEST_TIMEOUT = 60
 TEST_TIME = TEST_TIMEOUT = 60
@@ -166,8 +145,8 @@ class TestResult(object):
             throughput = 0.0
             throughput = 0.0
 
 
         if throughput == 0 and rssi > self.ZERO_THROUGHPUT_THRESHOLD:
         if throughput == 0 and rssi > self.ZERO_THROUGHPUT_THRESHOLD:
-                self.error_list.append("[Error][Fatal][{}][att: {}][rssi: {}]: No throughput data found"
-                                       .format(ap_ssid, att, rssi))
+            self.error_list.append("[Error][Fatal][{}][att: {}][rssi: {}]: No throughput data found"
+                                   .format(ap_ssid, att, rssi))
 
 
         self._save_result(throughput, ap_ssid, att, rssi, heap_size)
         self._save_result(throughput, ap_ssid, att, rssi, heap_size)
 
 
@@ -467,7 +446,7 @@ class IperfTestUtility(object):
         return ret
         return ret
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_ShieldBox_Basic", category="stress")
+@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox_Basic", category="stress")
 def test_wifi_throughput_with_different_configs(env, extra_data):
 def test_wifi_throughput_with_different_configs(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -492,7 +471,8 @@ def test_wifi_throughput_with_different_configs(env, extra_data):
                                                     "sdkconfig.ci.{}".format(config_name))
                                                     "sdkconfig.ci.{}".format(config_name))
 
 
         # 2. get DUT and download
         # 2. get DUT and download
-        dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ESP32DUT, app_config_name=config_name)
+        dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
+                          app_config_name=config_name)
         dut.start_app()
         dut.start_app()
         dut.expect("esp32>")
         dut.expect("esp32>")
 
 
@@ -519,12 +499,12 @@ def test_wifi_throughput_with_different_configs(env, extra_data):
         env.close_dut("iperf")
         env.close_dut("iperf")
 
 
     # 5. generate report
     # 5. generate report
-    report = ThroughputForConfigsReport(os.path.join(env.log_path, "ThroughputForConfigsReport"),
-                                        ap_info["ssid"], test_result, sdkconfig_files)
+    report = TestReport.ThroughputForConfigsReport(os.path.join(env.log_path, "ThroughputForConfigsReport"),
+                                                   ap_info["ssid"], test_result, sdkconfig_files)
     report.generate_report()
     report.generate_report()
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_ShieldBox", category="stress")
+@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox", category="stress")
 def test_wifi_throughput_vs_rssi(env, extra_data):
 def test_wifi_throughput_vs_rssi(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -547,7 +527,8 @@ def test_wifi_throughput_vs_rssi(env, extra_data):
     }
     }
 
 
     # 1. get DUT and download
     # 1. get DUT and download
-    dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ESP32DUT, app_config_name=BEST_PERFORMANCE_CONFIG)
+    dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
+                      app_config_name=BEST_PERFORMANCE_CONFIG)
     dut.start_app()
     dut.start_app()
     dut.expect("esp32>")
     dut.expect("esp32>")
 
 
@@ -573,12 +554,12 @@ def test_wifi_throughput_vs_rssi(env, extra_data):
     env.close_dut("iperf")
     env.close_dut("iperf")
 
 
     # 4. generate report
     # 4. generate report
-    report = ThroughputVsRssiReport(os.path.join(env.log_path, "ThroughputVsRssiReport"),
-                                    test_result)
+    report = TestReport.ThroughputVsRssiReport(os.path.join(env.log_path, "ThroughputVsRssiReport"),
+                                               test_result)
     report.generate_report()
     report.generate_report()
 
 
 
 
-@IDF.idf_example_test(env_tag="Example_ShieldBox_Basic")
+@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox_Basic")
 def test_wifi_throughput_basic(env, extra_data):
 def test_wifi_throughput_basic(env, extra_data):
     """
     """
     steps: |
     steps: |
@@ -593,7 +574,8 @@ def test_wifi_throughput_basic(env, extra_data):
     }
     }
 
 
     # 1. get DUT
     # 1. get DUT
-    dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ESP32DUT, app_config_name=BEST_PERFORMANCE_CONFIG)
+    dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
+                      app_config_name=BEST_PERFORMANCE_CONFIG)
     dut.start_app()
     dut.start_app()
     dut.expect("esp32>")
     dut.expect("esp32>")
 
 
@@ -615,8 +597,8 @@ def test_wifi_throughput_basic(env, extra_data):
     # 4. log performance and compare with pass standard
     # 4. log performance and compare with pass standard
     performance_items = []
     performance_items = []
     for throughput_type in test_result:
     for throughput_type in test_result:
-        IDF.log_performance("{}_throughput".format(throughput_type),
-                            "{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput()))
+        ttfw_idf.log_performance("{}_throughput".format(throughput_type),
+                                 "{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput()))
         performance_items.append(["{}_throughput".format(throughput_type),
         performance_items.append(["{}_throughput".format(throughput_type),
                                   "{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput())])
                                   "{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput())])
 
 
@@ -624,8 +606,8 @@ def test_wifi_throughput_basic(env, extra_data):
     TinyFW.JunitReport.update_performance(performance_items)
     TinyFW.JunitReport.update_performance(performance_items)
     # do check after logging, otherwise test will exit immediately if check fail, some performance can't be logged.
     # do check after logging, otherwise test will exit immediately if check fail, some performance can't be logged.
     for throughput_type in test_result:
     for throughput_type in test_result:
-        IDF.check_performance("{}_throughput".format(throughput_type),
-                              test_result[throughput_type].get_best_throughput())
+        ttfw_idf.check_performance("{}_throughput".format(throughput_type),
+                                   test_result[throughput_type].get_best_throughput())
 
 
     env.close_dut("iperf")
     env.close_dut("iperf")
 
 

+ 9 - 4
make/project.mk

@@ -144,7 +144,7 @@ EXTRA_COMPONENT_DIRS ?=
 COMPONENT_DIRS := $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components $(PROJECT_PATH)/main
 COMPONENT_DIRS := $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components $(PROJECT_PATH)/main
 endif
 endif
 # Make sure that every directory in the list is an absolute path without trailing slash.
 # Make sure that every directory in the list is an absolute path without trailing slash.
-# This is necessary to split COMPONENT_DIRS into SINGLE_COMPONENT_DIRS and MULTI_COMPONENT_DIRS below. 
+# This is necessary to split COMPONENT_DIRS into SINGLE_COMPONENT_DIRS and MULTI_COMPONENT_DIRS below.
 COMPONENT_DIRS := $(foreach cd,$(COMPONENT_DIRS),$(abspath $(cd)))
 COMPONENT_DIRS := $(foreach cd,$(COMPONENT_DIRS),$(abspath $(cd)))
 export COMPONENT_DIRS
 export COMPONENT_DIRS
 
 
@@ -153,11 +153,11 @@ $(warning SRCDIRS variable is deprecated. These paths can be added to EXTRA_COMP
 COMPONENT_DIRS += $(abspath $(SRCDIRS))
 COMPONENT_DIRS += $(abspath $(SRCDIRS))
 endif
 endif
 
 
-# List of component directories, i.e. directories which contain a component.mk file 
+# List of component directories, i.e. directories which contain a component.mk file
 SINGLE_COMPONENT_DIRS := $(abspath $(dir $(dir $(foreach cd,$(COMPONENT_DIRS),\
 SINGLE_COMPONENT_DIRS := $(abspath $(dir $(dir $(foreach cd,$(COMPONENT_DIRS),\
                              $(wildcard $(cd)/component.mk)))))
                              $(wildcard $(cd)/component.mk)))))
 
 
-# List of components directories, i.e. directories which may contain components 
+# List of components directories, i.e. directories which may contain components
 MULTI_COMPONENT_DIRS := $(filter-out $(SINGLE_COMPONENT_DIRS),$(COMPONENT_DIRS))
 MULTI_COMPONENT_DIRS := $(filter-out $(SINGLE_COMPONENT_DIRS),$(COMPONENT_DIRS))
 
 
 # The project Makefile can define a list of components, but if it does not do this
 # The project Makefile can define a list of components, but if it does not do this
@@ -635,6 +635,11 @@ clean: app-clean bootloader-clean config-clean ldgen-clean
 #
 #
 # This only works for components inside IDF_PATH
 # This only works for components inside IDF_PATH
 check-submodules:
 check-submodules:
+# for internal use:
+# skip submodule check if running on Gitlab CI and job is configured as not clone submodules
+ifeq ($(IDF_SKIP_CHECK_SUBMODULES),1)
+	@echo "skip submodule check on internal CI"
+else
 # Check if .gitmodules exists, otherwise skip submodule check, assuming flattened structure
 # Check if .gitmodules exists, otherwise skip submodule check, assuming flattened structure
 ifneq ("$(wildcard ${IDF_PATH}/.gitmodules)","")
 ifneq ("$(wildcard ${IDF_PATH}/.gitmodules)","")
 
 
@@ -662,7 +667,7 @@ endef
 # so the argument is suitable for use with 'git submodule' commands
 # so the argument is suitable for use with 'git submodule' commands
 $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule))))
 $(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule))))
 endif # End check for .gitmodules existence
 endif # End check for .gitmodules existence
-
+endif
 
 
 # PHONY target to list components in the build and their paths
 # PHONY target to list components in the build and their paths
 list-components:
 list-components:

+ 0 - 0
tools/tiny-test-fw/docs/_static/.keep → tools/ble/__init__.py


+ 1 - 1
tools/ble/lib_ble_client.py

@@ -29,7 +29,7 @@ try:
     from gi.repository import GLib
     from gi.repository import GLib
 except ImportError as e:
 except ImportError as e:
     if 'linux' not in sys.platform:
     if 'linux' not in sys.platform:
-        sys.exit("Error: Only supported on Linux platform")
+        raise e
     print(e)
     print(e)
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")

+ 1 - 1
tools/ble/lib_gap.py

@@ -25,7 +25,7 @@ try:
     import dbus.service
     import dbus.service
 except ImportError as e:
 except ImportError as e:
     if 'linux' not in sys.platform:
     if 'linux' not in sys.platform:
-        sys.exit("Error: Only supported on Linux platform")
+        raise e
     print(e)
     print(e)
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")

+ 1 - 1
tools/ble/lib_gatt.py

@@ -25,7 +25,7 @@ try:
     import dbus.service
     import dbus.service
 except ImportError as e:
 except ImportError as e:
     if 'linux' not in sys.platform:
     if 'linux' not in sys.platform:
-        sys.exit("Error: Only supported on Linux platform")
+        raise e
     print(e)
     print(e)
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Install packages `libgirepository1.0-dev gir1.2-gtk-3.0 libcairo2-dev libdbus-1-dev libdbus-glib-1-dev` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")
     print("Run `pip install -r $IDF_PATH/tools/ble/requirements.txt` for resolving the issue")

+ 106 - 0
tools/ci/ci_fetch_submodule.py

@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+# internal use only for CI
+# download archive of one commit instead of cloning entire submodule repo
+
+import re
+import os
+import subprocess
+import argparse
+import shutil
+import time
+
+import gitlab_api
+
+SUBMODULE_PATTERN = re.compile(r"\[submodule \"([^\"]+)\"]")
+PATH_PATTERN = re.compile(r"path\s+=\s+(\S+)")
+URL_PATTERN = re.compile(r"url\s+=\s+(\S+)")
+
+SUBMODULE_ARCHIVE_TEMP_FOLDER = "submodule_archive"
+
+
+class SubModule(object):
+    # We don't need to support recursive submodule clone now
+
+    GIT_LS_TREE_OUTPUT_PATTERN = re.compile(r"\d+\s+commit\s+([0-9a-f]+)\s+")
+
+    def __init__(self, gitlab_inst, path, url):
+        self.path = path
+        self.gitlab_inst = gitlab_inst
+        self.project_id = self._get_project_id(url)
+        self.commit_id = self._get_commit_id(path)
+
+    def _get_commit_id(self, path):
+        output = subprocess.check_output(["git", "ls-tree", "HEAD", path])
+        # example output: 160000 commit d88a262fbdf35e5abb372280eb08008749c3faa0	components/esp_wifi/lib
+        match = self.GIT_LS_TREE_OUTPUT_PATTERN.search(output)
+        return match.group(1)
+
+    def _get_project_id(self, url):
+        base_name = os.path.basename(url)
+        project_id = self.gitlab_inst.get_project_id(os.path.splitext(base_name)[0],  # remove .git
+                                                     namespace="espressif")
+        return project_id
+
+    def download_archive(self):
+        print("Update submodule: {}: {}".format(self.path, self.commit_id))
+        path_name = self.gitlab_inst.download_archive(self.commit_id, SUBMODULE_ARCHIVE_TEMP_FOLDER,
+                                                      self.project_id)
+        renamed_path = os.path.join(os.path.dirname(path_name), os.path.basename(self.path))
+        os.rename(path_name, renamed_path)
+        shutil.rmtree(self.path, ignore_errors=True)
+        shutil.move(renamed_path, os.path.dirname(self.path))
+
+
+def update_submodule(git_module_file, submodules_to_update):
+    gitlab_inst = gitlab_api.Gitlab()
+    submodules = []
+    with open(git_module_file, "r") as f:
+        data = f.read()
+    match = SUBMODULE_PATTERN.search(data)
+    while True:
+        next_match = SUBMODULE_PATTERN.search(data, pos=match.end())
+        if next_match:
+            end_pos = next_match.start()
+        else:
+            end_pos = len(data)
+        path_match = PATH_PATTERN.search(data, pos=match.end(), endpos=end_pos)
+        url_match = URL_PATTERN.search(data, pos=match.end(), endpos=end_pos)
+        path = path_match.group(1)
+        url = url_match.group(1)
+
+        filter_result = True
+        if submodules_to_update:
+            if path not in submodules_to_update:
+                filter_result = False
+        if filter_result:
+            submodules.append(SubModule(gitlab_inst, path, url))
+
+        match = next_match
+        if not match:
+            break
+
+    shutil.rmtree(SUBMODULE_ARCHIVE_TEMP_FOLDER, ignore_errors=True)
+
+    for submodule in submodules:
+        submodule.download_archive()
+
+
+if __name__ == '__main__':
+    start_time = time.time()
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--repo_path", "-p", default=".", help="repo path")
+    parser.add_argument("--submodule", "-s", default="all",
+                        help="Submodules to update. By default update all submodules. "
+                             "For multiple submodules, separate them with `;`. "
+                             "`all` and `none` are special values that indicates we fetch all / none submodules")
+    args = parser.parse_args()
+    if args.submodule == "none":
+        print("don't need to update submodules")
+        exit(0)
+    if args.submodule == "all":
+        _submodules = []
+    else:
+        _submodules = args.submodule.split(";")
+    update_submodule(os.path.join(args.repo_path, ".gitmodules"), _submodules)
+    print("total time spent on update submodule: {:.02f}s".format(time.time() - start_time))

+ 5 - 4
tools/ci/config/assign-test.yml

@@ -10,7 +10,7 @@ assign_test:
     - build_ssc
     - build_ssc
     - build_esp_idf_tests_cmake
     - build_esp_idf_tests_cmake
   variables:
   variables:
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
+    SUBMODULES_TO_FETCH: "components/esptool_py/esptool"
     EXAMPLE_CONFIG_OUTPUT_PATH: "$CI_PROJECT_DIR/examples/test_configs"
     EXAMPLE_CONFIG_OUTPUT_PATH: "$CI_PROJECT_DIR/examples/test_configs"
     UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
     UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
   artifacts:
   artifacts:
@@ -18,6 +18,7 @@ assign_test:
       - components/idf_test/*/CIConfigs
       - components/idf_test/*/CIConfigs
       - components/idf_test/*/TC.sqlite
       - components/idf_test/*/TC.sqlite
       - $EXAMPLE_CONFIG_OUTPUT_PATH
       - $EXAMPLE_CONFIG_OUTPUT_PATH
+      - build_examples/artifact_index.json
     expire_in: 1 week
     expire_in: 1 week
   only:
   only:
     variables:
     variables:
@@ -27,9 +28,9 @@ assign_test:
       - $BOT_LABEL_EXAMPLE_TEST
       - $BOT_LABEL_EXAMPLE_TEST
   script:
   script:
     # assign example tests
     # assign example tests
-    - python $TEST_FW_PATH/CIAssignExampleTest.py $IDF_PATH/examples $CI_TARGET_TEST_CONFIG_FILE $EXAMPLE_CONFIG_OUTPUT_PATH
+    - python tools/ci/python_packages/ttfw_idf/CIAssignExampleTest.py $IDF_PATH/examples $CI_TARGET_TEST_CONFIG_FILE $EXAMPLE_CONFIG_OUTPUT_PATH
     # assign unit test cases
     # assign unit test cases
-    - python $TEST_FW_PATH/CIAssignUnitTest.py $UNIT_TEST_CASE_FILE $CI_TARGET_TEST_CONFIG_FILE $IDF_PATH/components/idf_test/unit_test/CIConfigs
+    - python tools/ci/python_packages/ttfw_idf/CIAssignUnitTest.py $UNIT_TEST_CASE_FILE $CI_TARGET_TEST_CONFIG_FILE $IDF_PATH/components/idf_test/unit_test/CIConfigs
     # clone test script to assign tests
     # clone test script to assign tests
     - git clone $TEST_SCRIPT_REPOSITORY
     - git clone $TEST_SCRIPT_REPOSITORY
     - python $CHECKOUT_REF_SCRIPT auto_test_script auto_test_script
     - python $CHECKOUT_REF_SCRIPT auto_test_script auto_test_script
@@ -55,9 +56,9 @@ update_test_cases:
       - ${CI_PROJECT_DIR}/test-management/*.log
       - ${CI_PROJECT_DIR}/test-management/*.log
     expire_in: 1 week
     expire_in: 1 week
   variables:
   variables:
+    SUBMODULES_TO_FETCH: "components/esptool_py/esptool"
     UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
     UNIT_TEST_CASE_FILE: "${CI_PROJECT_DIR}/components/idf_test/unit_test/TestCaseAll.yml"
     BOT_ACCOUNT_CONFIG_FILE: "${CI_PROJECT_DIR}/test-management/Config/Account.local.yml"
     BOT_ACCOUNT_CONFIG_FILE: "${CI_PROJECT_DIR}/test-management/Config/Account.local.yml"
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
     AUTO_TEST_SCRIPT_PATH: "${CI_PROJECT_DIR}/auto_test_script"
     AUTO_TEST_SCRIPT_PATH: "${CI_PROJECT_DIR}/auto_test_script"
     PYTHON_VER: 3
     PYTHON_VER: 3
   script:
   script:

+ 1 - 1
tools/ci/config/post_check.yml

@@ -10,7 +10,7 @@ check_submodule_sync:
   retry: 2
   retry: 2
   variables:
   variables:
     GIT_STRATEGY: clone
     GIT_STRATEGY: clone
-    GIT_SUBMODULE_STRATEGY: none
+    SUBMODULES_TO_FETCH: "none"
     PUBLIC_IDF_URL: "https://github.com/espressif/esp-idf.git"
     PUBLIC_IDF_URL: "https://github.com/espressif/esp-idf.git"
   before_script: []
   before_script: []
   after_script: []
   after_script: []

+ 7 - 21
tools/ci/config/target-test.yml

@@ -21,7 +21,6 @@
       - $BOT_LABEL_EXAMPLE_TEST
       - $BOT_LABEL_EXAMPLE_TEST
   dependencies:
   dependencies:
     - assign_test
     - assign_test
-    - build_examples_cmake_esp32
   artifacts:
   artifacts:
     when: always
     when: always
     paths:
     paths:
@@ -30,12 +29,11 @@
     reports:
     reports:
         junit: $LOG_PATH/*/XUNIT_RESULT.xml
         junit: $LOG_PATH/*/XUNIT_RESULT.xml
   variables:
   variables:
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
     CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
     CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     ENV_FILE: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/EnvConfig.yml"
     ENV_FILE: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/EnvConfig.yml"
-    GIT_SUBMODULE_STRATEGY: none
+    SUBMODULES_TO_FETCH: "components/esptool_py/esptool"
   script:
   script:
     - *define_config_file_name
     - *define_config_file_name
     # first test if config file exists, if not exist, exit 0
     # first test if config file exists, if not exist, exit 0
@@ -43,10 +41,7 @@
     # clone test env configs
     # clone test env configs
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
-    # Get esptool
-    - git submodule sync
-    - git submodule update --init components/esptool_py/esptool
-    - cd $TEST_FW_PATH
+    - cd tools/ci/python_packages/tiny_test_fw/bin
     # run test
     # run test
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
 
 
@@ -65,8 +60,6 @@
       - $BOT_LABEL_EXAMPLE_TEST
       - $BOT_LABEL_EXAMPLE_TEST
   dependencies:
   dependencies:
     - assign_test
     - assign_test
-    - build_examples_make
-    - build_examples_cmake_esp32
   artifacts:
   artifacts:
     when: always
     when: always
     paths:
     paths:
@@ -75,7 +68,6 @@
     reports:
     reports:
         junit: $LOG_PATH/*/XUNIT_RESULT.xml
         junit: $LOG_PATH/*/XUNIT_RESULT.xml
   variables:
   variables:
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/examples"
     CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
     CONFIG_FILE_PATH: "${CI_PROJECT_DIR}/examples/test_configs"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
@@ -87,7 +79,7 @@
     # clone test env configs
     # clone test env configs
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
-    - cd $TEST_FW_PATH
+    - cd tools/ci/python_packages/tiny_test_fw/bin
     # run test
     # run test
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
 
 
@@ -139,7 +131,7 @@
       - $LOG_PATH
       - $LOG_PATH
     expire_in: 1 week
     expire_in: 1 week
   variables:
   variables:
-    GIT_SUBMODULE_STRATEGY: none
+    SUBMODULES_TO_FETCH: "components/esptool_py/esptool"
     LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
     LOCAL_ENV_CONFIG_PATH: "$CI_PROJECT_DIR/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/ESP32_IDF"
     LOG_PATH: "${CI_PROJECT_DIR}/${CI_COMMIT_SHA}"
     LOG_PATH: "${CI_PROJECT_DIR}/${CI_COMMIT_SHA}"
     TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/integration_test"
     TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/integration_test"
@@ -170,7 +162,6 @@ test_weekend_mqtt:
       - $BOT_LABEL_WEEKEND_TEST
       - $BOT_LABEL_WEEKEND_TEST
   variables:
   variables:
     TEST_CASE_PATH: "$CI_PROJECT_DIR/components/mqtt/weekend_test"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/components/mqtt/weekend_test"
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     ENV_FILE: "$CI_PROJECT_DIR/components/mqtt/weekend_test/env.yml"
     ENV_FILE: "$CI_PROJECT_DIR/components/mqtt/weekend_test/env.yml"
     CONFIG_FILE_PATH: "$CI_PROJECT_DIR/components/mqtt/weekend_test"
     CONFIG_FILE_PATH: "$CI_PROJECT_DIR/components/mqtt/weekend_test"
@@ -187,7 +178,6 @@ test_weekend_network:
       - $BOT_LABEL_WEEKEND_TEST
       - $BOT_LABEL_WEEKEND_TEST
   variables:
   variables:
     TEST_CASE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
     TEST_CASE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
-    TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS"
     ENV_FILE: "$CI_PROJECT_DIR/components/lwip/weekend_test/env.yml"
     ENV_FILE: "$CI_PROJECT_DIR/components/lwip/weekend_test/env.yml"
     CONFIG_FILE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
     CONFIG_FILE_PATH: "$CI_PROJECT_DIR/components/lwip/weekend_test"
@@ -202,8 +192,6 @@ example_test_001:
 example_test_002:
 example_test_002:
   extends: .example_test_template
   extends: .example_test_template
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
   image: $CI_DOCKER_REGISTRY/ubuntu-test-env$BOT_DOCKER_IMAGE_TAG
-  variables:
-    GIT_SUBMODULE_STRATEGY: normal
   tags:
   tags:
     - ESP32
     - ESP32
     - Example_ShieldBox_Basic
     - Example_ShieldBox_Basic
@@ -214,7 +202,7 @@ example_test_002:
     # clone test env configs
     # clone test env configs
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - git clone $TEST_ENV_CONFIG_REPOSITORY
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
     - python $CHECKOUT_REF_SCRIPT ci-test-runner-configs ci-test-runner-configs
-    - cd $TEST_FW_PATH
+    - cd tools/ci/python_packages/tiny_test_fw/bin
     # run test
     # run test
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
     - python Runner.py $TEST_CASE_PATH -c $CONFIG_FILE -e $ENV_FILE
 
 
@@ -286,7 +274,6 @@ example_test_010:
 
 
 example_test_011:
 example_test_011:
   extends: .example_debug_template
   extends: .example_debug_template
-  parallel: 4
   tags:
   tags:
     - ESP32
     - ESP32
     - Example_T2_RS485
     - Example_T2_RS485
@@ -316,7 +303,7 @@ UT_001:
 
 
 UT_002:
 UT_002:
   extends: .unit_test_template
   extends: .unit_test_template
-  parallel: 11
+  parallel: 10
   tags:
   tags:
     - ESP32_IDF
     - ESP32_IDF
     - UT_T1_1
     - UT_T1_1
@@ -466,7 +453,7 @@ UT_034:
 
 
 UT_035:
 UT_035:
   extends: .unit_test_template
   extends: .unit_test_template
-  parallel: 20
+  parallel: 19
   tags:
   tags:
     - ESP32S2BETA_IDF
     - ESP32S2BETA_IDF
     - UT_T1_1
     - UT_T1_1
@@ -500,7 +487,6 @@ UT_043:
 
 
 UT_044:
 UT_044:
   extends: .unit_test_template
   extends: .unit_test_template
-  parallel: 4
   tags:
   tags:
     - ESP32_IDF
     - ESP32_IDF
     - UT_SDIO
     - UT_SDIO

+ 0 - 1
tools/ci/executable-list.txt

@@ -6,7 +6,6 @@ components/espcoredump/espcoredump.py
 components/espcoredump/test/test_espcoredump.py
 components/espcoredump/test/test_espcoredump.py
 components/espcoredump/test/test_espcoredump.sh
 components/espcoredump/test/test_espcoredump.sh
 components/heap/test_multi_heap_host/test_all_configs.sh
 components/heap/test_multi_heap_host/test_all_configs.sh
-components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py
 components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py
 components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py
 components/partition_table/gen_empty_partition.py
 components/partition_table/gen_empty_partition.py
 components/partition_table/gen_esp32part.py
 components/partition_table/gen_esp32part.py

+ 174 - 0
tools/ci/python_packages/gitlab_api.py

@@ -0,0 +1,174 @@
+import os
+import re
+import argparse
+import tempfile
+import tarfile
+import zipfile
+
+import gitlab
+
+
+class Gitlab(object):
+    JOB_NAME_PATTERN = re.compile(r"(\w+)(\s+(\d+)/(\d+))?")
+
+    def __init__(self, project_id=None):
+        config_data_from_env = os.getenv("PYTHON_GITLAB_CONFIG")
+        if config_data_from_env:
+            # prefer to load config from env variable
+            with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+                temp_file.write(config_data_from_env)
+            config_files = [temp_file.name]
+        else:
+            # otherwise try to use config file at local filesystem
+            config_files = None
+        self.gitlab_inst = gitlab.Gitlab.from_config(config_files=config_files)
+        self.gitlab_inst.auth()
+        if project_id:
+            self.project = self.gitlab_inst.projects.get(project_id)
+        else:
+            self.project = None
+
+    def get_project_id(self, name, namespace=None):
+        """
+        search project ID by name
+
+        :param name: project name
+        :param namespace: namespace to match when we have multiple project with same name
+        :return: project ID
+        """
+        projects = self.gitlab_inst.projects.list(search=name)
+        for project in projects:
+            if namespace is None:
+                if len(projects) == 1:
+                    project_id = project.id
+                    break
+            if project.namespace["path"] == namespace:
+                project_id = project.id
+                break
+        else:
+            raise ValueError("Can't find project")
+        return project_id
+
+    def download_artifacts(self, job_id, destination):
+        """
+        download full job artifacts and extract to destination.
+
+        :param job_id: Gitlab CI job ID
+        :param destination: extract artifacts to path.
+        """
+        job = self.project.jobs.get(job_id)
+
+        with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+            job.artifacts(streamed=True, action=temp_file.write)
+
+        with zipfile.ZipFile(temp_file.name, "r") as archive_file:
+            archive_file.extractall(destination)
+
+    def download_artifact(self, job_id, artifact_path, destination=None):
+        """
+        download specific path of job artifacts and extract to destination.
+
+        :param job_id: Gitlab CI job ID
+        :param artifact_path: list of path in artifacts (relative path to artifact root path)
+        :param destination: destination of artifact. Do not save to file if destination is None
+        :return: A list of artifact file raw data.
+        """
+        job = self.project.jobs.get(job_id)
+
+        raw_data_list = []
+
+        for a_path in artifact_path:
+            try:
+                data = job.artifact(a_path)
+            except gitlab.GitlabGetError as e:
+                print("Failed to download '{}' form job {}".format(a_path, job_id))
+                raise e
+            raw_data_list.append(data)
+            if destination:
+                file_path = os.path.join(destination, a_path)
+                try:
+                    os.makedirs(os.path.dirname(file_path))
+                except OSError:
+                    # already exists
+                    pass
+                with open(file_path, "wb") as f:
+                    f.write(data)
+
+        return raw_data_list
+
+    def find_job_id(self, job_name, pipeline_id=None):
+        """
+        Get Job ID from job name of specific pipeline
+
+        :param job_name: job name
+        :param pipeline_id: If None, will get pipeline id from CI pre-defined variable.
+        :return: a list of job IDs (parallel job will generate multiple jobs)
+        """
+        job_id_list = []
+        if pipeline_id is None:
+            pipeline_id = os.getenv("CI_PIPELINE_ID")
+        pipeline = self.project.pipelines.get(pipeline_id)
+        jobs = pipeline.jobs.list(all=True)
+        for job in jobs:
+            match = self.JOB_NAME_PATTERN.match(job.name)
+            if match:
+                if match.group(1) == job_name:
+                    job_id_list.append({"id": job.id, "parallel_num": match.group(3)})
+        return job_id_list
+
+    def download_archive(self, ref, destination, project_id=None):
+        """
+        Download archive of certain commit of a repository and extract to destination path
+
+        :param ref: commit or branch name
+        :param destination: destination path of extracted archive file
+        :param project_id: download project of current instance if project_id is None
+        :return: root path name of archive file
+        """
+        if project_id is None:
+            project = self.project
+        else:
+            project = self.gitlab_inst.projects.get(project_id)
+
+        with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+            try:
+                project.repository_archive(sha=ref, streamed=True, action=temp_file.write)
+            except gitlab.GitlabGetError as e:
+                print("Failed to archive from project {}".format(project_id))
+                raise e
+
+        print("archive size: {:.03f}MB".format(float(os.path.getsize(temp_file.name)) / (1024 * 1024)))
+
+        with tarfile.open(temp_file.name, "r") as archive_file:
+            root_name = archive_file.getnames()[0]
+            archive_file.extractall(destination)
+
+        return os.path.join(os.path.realpath(destination), root_name)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument("action")
+    parser.add_argument("project_id", type=int)
+    parser.add_argument("--pipeline_id", "-i", type=int, default=None)
+    parser.add_argument("--ref", "-r", default="master")
+    parser.add_argument("--job_id", "-j", type=int, default=None)
+    parser.add_argument("--job_name", "-n", default=None)
+    parser.add_argument("--project_name", "-m", default=None)
+    parser.add_argument("--destination", "-d", default=None)
+    parser.add_argument("--artifact_path", "-a", nargs="*", default=None)
+    args = parser.parse_args()
+
+    gitlab_inst = Gitlab(args.project_id)
+    if args.action == "download_artifacts":
+        gitlab_inst.download_artifacts(args.job_id, args.destination)
+    if args.action == "download_artifact":
+        gitlab_inst.download_artifact(args.job_id, args.artifact_path, args.destination)
+    elif args.action == "find_job_id":
+        job_ids = gitlab_inst.find_job_id(args.job_name, args.pipeline_id)
+        print(";".join([",".join([str(j["id"]), j["parallel_num"]]) for j in job_ids]))
+    elif args.action == "download_archive":
+        gitlab_inst.download_archive(args.ref, args.destination)
+    elif args.action == "get_project_id":
+        ret = gitlab_inst.get_project_id(args.project_name)
+        print("project id: {}".format(ret))

+ 0 - 0
tools/ci/python_packages/idf_http_server_test/__init__.py


+ 2 - 1
examples/protocols/http_server/persistent_sockets/scripts/adder.py → tools/ci/python_packages/idf_http_server_test/adder.py

@@ -20,7 +20,8 @@ from builtins import str
 from builtins import range
 from builtins import range
 import http.client
 import http.client
 import argparse
 import argparse
-import Utility
+
+from tiny_test_fw import Utility
 
 
 
 
 def start_session(ip, port):
 def start_session(ip, port):

+ 2 - 13
examples/protocols/http_server/simple/scripts/client.py → tools/ci/python_packages/idf_http_server_test/client.py

@@ -20,19 +20,8 @@ from builtins import str
 import http.client
 import http.client
 import argparse
 import argparse
 
 
-try:
-    import Utility
-except ImportError:
-    import sys
-    import os
-
-    # This environment variable is expected on the host machine
-    # > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-
-    import Utility
+
+from tiny_test_fw import Utility
 
 
 
 
 def verbose_print(verbosity, *args):
 def verbose_print(verbosity, *args):

+ 1025 - 0
tools/ci/python_packages/idf_http_server_test/test.py

@@ -0,0 +1,1025 @@
+#!/usr/bin/env python
+#
+# Copyright 2018 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.
+
+# Utility for testing the web server. Test cases:
+# Assume the device supports 'n' simultaneous open sockets
+#
+# HTTP Server Tests
+#
+# 0. Firmware Settings:
+# - Create a dormant thread whose sole job is to call httpd_stop() when instructed
+# - Measure the following before httpd_start() is called:
+#     - current free memory
+#     - current free sockets
+# - Measure the same whenever httpd_stop is called
+# - Register maximum possible URI handlers: should be successful
+# - Register one more URI handler: should fail
+# - Deregister on URI handler: should be successful
+# - Register on more URI handler: should succeed
+# - Register separate handlers for /hello, /hello/type_html. Also
+#   ensure that /hello/type_html is registered BEFORE  /hello. (tests
+#   that largest matching URI is picked properly)
+# - Create URI handler /adder. Make sure it uses a custom free_ctx
+#   structure to free it up
+
+# 1. Using Standard Python HTTP Client
+# - simple GET on /hello (returns Hello World. Ensures that basic
+#   firmware tests are complete, or returns error)
+# - POST on /hello (should fail)
+# - PUT on /hello (should fail)
+# - simple POST on /echo (returns whatever the POST data)
+# - simple PUT on /echo (returns whatever the PUT data)
+# - GET on /echo (should fail)
+# - simple GET on /hello/type_html (returns Content type as text/html)
+# - simple GET on /hello/status_500 (returns HTTP status 500)
+# - simple GET on /false_uri (returns HTTP status 404)
+# - largest matching URI handler is picked is already verified because
+#   of /hello and /hello/type_html tests
+#
+#
+# 2. Session Tests
+# - Sessions + Pipelining basics:
+#    - Create max supported sessions
+#    - On session i,
+#          - send 3 back-to-back POST requests with data i on /adder
+#          - read back 3 responses. They should be i, 2i and 3i
+#    - Tests that
+#          - pipelining works
+#          - per-session context is maintained for all supported
+#            sessions
+#    - Close all sessions
+#
+# - Cleanup leftover data: Tests that the web server properly cleans
+#   up leftover data
+#    - Create a session
+#    - POST on /leftover_data with 52 bytes of data (data includes
+#      \r\n)(the handler only
+#      reads first 10 bytes and returns them, leaving the rest of the
+#      bytes unread)
+#    - GET on /hello (should return 'Hello World')
+#    - POST on /false_uri with 52  bytes of data (data includes \r\n)
+#      (should return HTTP 404)
+#    - GET on /hello (should return 'Hello World')
+#
+# - Test HTTPd Asynchronous response
+#   - Create a session
+#   - GET on /async_data
+#   - returns 'Hello World!' as a response
+#   - the handler schedules an async response, which generates a second
+#     response 'Hello Double World!'
+#
+# - Spillover test
+#    - Create max supported sessions with the web server
+#    - GET /hello on all the sessions (should return Hello World)
+#    - Create one more session, this should fail
+#    - GET /hello on all the sessions (should return Hello World)
+#
+# - Timeout test
+#    - Create a session and only Send 'GE' on the same (simulates a
+#      client that left the network halfway through a request)
+#    - Wait for recv-wait-timeout
+#    - Server should automatically close the socket
+
+
+# ############ TODO TESTS #############
+
+# 3. Stress Tests
+#
+# - httperf
+#     - Run the following httperf command:
+#     httperf --server=10.31.130.126 --wsess=8,50,0.5 --rate 8 --burst-length 2
+#
+#     - The above implies that the test suite will open
+#           - 8 simultaneous connections with the server
+#           - the rate of opening the sessions will be 8 per sec. So in our
+#             case, a new connection will be opened every 0.2 seconds for 1 second
+#           - The burst length 2 indicates that 2 requests will be sent
+#             simultaneously on the same connection in a single go
+#           - 0.5 seconds is the time between sending out 2 bursts
+#           - 50 is the total number of requests that will be sent out
+#
+#     - So in the above example, the test suite will open 8
+#       connections, each separated by 0.2 seconds. On each connection
+#       it will send 2 requests in a single burst. The bursts on a
+#       single connection will be separated by 0.5 seconds. A total of
+#       25 bursts (25 x 2 = 50) will be sent out
+
+# 4. Leak Tests
+# - Simple Leak test
+#    - Simple GET on /hello/restart (returns success, stop web server, measures leaks, restarts webserver)
+#    - Simple GET on /hello/restart_results (returns the leak results)
+# - Leak test with open sockets
+#    - Open 8 sessions
+#    - Simple GET on /hello/restart (returns success, stop web server,
+#      measures leaks, restarts webserver)
+#    - All sockets should get closed
+#    - Simple GET on /hello/restart_results (returns the leak results)
+
+
+from __future__ import division
+from __future__ import print_function
+from builtins import str
+from builtins import range
+from builtins import object
+import threading
+import socket
+import time
+import argparse
+import http.client
+import sys
+import string
+import random
+
+from tiny_test_fw import Utility
+
+_verbose_ = False
+
+
+class Session(object):
+    def __init__(self, addr, port, timeout=15):
+        self.client = socket.create_connection((addr, int(port)), timeout=timeout)
+        self.target = addr
+        self.status = 0
+        self.encoding = ''
+        self.content_type = ''
+        self.content_len = 0
+
+    def send_err_check(self, request, data=None):
+        rval = True
+        try:
+            self.client.sendall(request.encode())
+            if data:
+                self.client.sendall(data.encode())
+        except socket.error as err:
+            self.client.close()
+            Utility.console_log("Socket Error in send :", err)
+            rval = False
+        return rval
+
+    def send_get(self, path, headers=None):
+        request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
+        if headers:
+            for field, value in headers.items():
+                request += "\r\n" + field + ": " + value
+        request += "\r\n\r\n"
+        return self.send_err_check(request)
+
+    def send_put(self, path, data, headers=None):
+        request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
+        if headers:
+            for field, value in headers.items():
+                request += "\r\n" + field + ": " + value
+        request += "\r\nContent-Length: " + str(len(data)) + "\r\n\r\n"
+        return self.send_err_check(request, data)
+
+    def send_post(self, path, data, headers=None):
+        request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
+        if headers:
+            for field, value in headers.items():
+                request += "\r\n" + field + ": " + value
+        request += "\r\nContent-Length: " + str(len(data)) + "\r\n\r\n"
+        return self.send_err_check(request, data)
+
+    def read_resp_hdrs(self):
+        try:
+            state = 'nothing'
+            resp_read = ''
+            while True:
+                char = self.client.recv(1).decode()
+                if char == '\r' and state == 'nothing':
+                    state = 'first_cr'
+                elif char == '\n' and state == 'first_cr':
+                    state = 'first_lf'
+                elif char == '\r' and state == 'first_lf':
+                    state = 'second_cr'
+                elif char == '\n' and state == 'second_cr':
+                    state = 'second_lf'
+                else:
+                    state = 'nothing'
+                resp_read += char
+                if state == 'second_lf':
+                    break
+            # Handle first line
+            line_hdrs = resp_read.splitlines()
+            line_comp = line_hdrs[0].split()
+            self.status = line_comp[1]
+            del line_hdrs[0]
+            self.encoding = ''
+            self.content_type = ''
+            headers = dict()
+            # Process other headers
+            for h in range(len(line_hdrs)):
+                line_comp = line_hdrs[h].split(':')
+                if line_comp[0] == 'Content-Length':
+                    self.content_len = int(line_comp[1])
+                if line_comp[0] == 'Content-Type':
+                    self.content_type = line_comp[1].lstrip()
+                if line_comp[0] == 'Transfer-Encoding':
+                    self.encoding = line_comp[1].lstrip()
+                if len(line_comp) == 2:
+                    headers[line_comp[0]] = line_comp[1].lstrip()
+            return headers
+        except socket.error as err:
+            self.client.close()
+            Utility.console_log("Socket Error in recv :", err)
+            return None
+
+    def read_resp_data(self):
+        try:
+            read_data = ''
+            if self.encoding != 'chunked':
+                while len(read_data) != self.content_len:
+                    read_data += self.client.recv(self.content_len).decode()
+            else:
+                chunk_data_buf = ''
+                while (True):
+                    # Read one character into temp  buffer
+                    read_ch = self.client.recv(1)
+                    # Check CRLF
+                    if (read_ch == '\r'):
+                        read_ch = self.client.recv(1).decode()
+                        if (read_ch == '\n'):
+                            # If CRLF decode length of chunk
+                            chunk_len = int(chunk_data_buf, 16)
+                            # Keep adding to contents
+                            self.content_len += chunk_len
+                            rem_len = chunk_len
+                            while (rem_len):
+                                new_data = self.client.recv(rem_len)
+                                read_data += new_data
+                                rem_len -= len(new_data)
+                            chunk_data_buf = ''
+                            # Fetch remaining CRLF
+                            if self.client.recv(2) != "\r\n":
+                                # Error in packet
+                                Utility.console_log("Error in chunked data")
+                                return None
+                            if not chunk_len:
+                                # If last chunk
+                                break
+                            continue
+                        chunk_data_buf += '\r'
+                    # If not CRLF continue appending
+                    # character to chunked data buffer
+                    chunk_data_buf += read_ch
+            return read_data
+        except socket.error as err:
+            self.client.close()
+            Utility.console_log("Socket Error in recv :", err)
+            return None
+
+    def close(self):
+        self.client.close()
+
+
+def test_val(text, expected, received):
+    if expected != received:
+        Utility.console_log(" Fail!")
+        Utility.console_log("  [reason] " + text + ":")
+        Utility.console_log("        expected: " + str(expected))
+        Utility.console_log("        received: " + str(received))
+        return False
+    return True
+
+
+class adder_thread (threading.Thread):
+    def __init__(self, id, dut, port):
+        threading.Thread.__init__(self)
+        self.id = id
+        self.dut = dut
+        self.depth = 3
+        self.session = Session(dut, port)
+
+    def run(self):
+        self.response = []
+
+        # Pipeline 3 requests
+        if (_verbose_):
+            Utility.console_log("   Thread: Using adder start " + str(self.id))
+
+        for _ in range(self.depth):
+            self.session.send_post('/adder', str(self.id))
+            time.sleep(2)
+
+        for _ in range(self.depth):
+            self.session.read_resp_hdrs()
+            self.response.append(self.session.read_resp_data())
+
+    def adder_result(self):
+        if len(self.response) != self.depth:
+            Utility.console_log("Error : missing response packets")
+            return False
+        for i in range(len(self.response)):
+            if not test_val("Thread" + str(self.id) + " response[" + str(i) + "]",
+                            str(self.id * (i + 1)), str(self.response[i])):
+                return False
+        return True
+
+    def close(self):
+        self.session.close()
+
+
+def get_hello(dut, port):
+    # GET /hello should return 'Hello World!'
+    Utility.console_log("[test] GET /hello returns 'Hello World!' =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("GET", "/hello")
+    resp = conn.getresponse()
+    if not test_val("status_code", 200, resp.status):
+        conn.close()
+        return False
+    if not test_val("data", "Hello World!", resp.read().decode()):
+        conn.close()
+        return False
+    if not test_val("data", "text/html", resp.getheader('Content-Type')):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def put_hello(dut, port):
+    # PUT /hello returns 405'
+    Utility.console_log("[test] PUT /hello returns 405 =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("PUT", "/hello", "Hello")
+    resp = conn.getresponse()
+    if not test_val("status_code", 405, resp.status):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def post_hello(dut, port):
+    # POST /hello returns 405'
+    Utility.console_log("[test] POST /hello returns 405 =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("POST", "/hello", "Hello")
+    resp = conn.getresponse()
+    if not test_val("status_code", 405, resp.status):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def post_echo(dut, port):
+    # POST /echo echoes data'
+    Utility.console_log("[test] POST /echo echoes data =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("POST", "/echo", "Hello")
+    resp = conn.getresponse()
+    if not test_val("status_code", 200, resp.status):
+        conn.close()
+        return False
+    if not test_val("data", "Hello", resp.read().decode()):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def put_echo(dut, port):
+    # PUT /echo echoes data'
+    Utility.console_log("[test] PUT /echo echoes data =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("PUT", "/echo", "Hello")
+    resp = conn.getresponse()
+    if not test_val("status_code", 200, resp.status):
+        conn.close()
+        return False
+    if not test_val("data", "Hello", resp.read().decode()):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def get_echo(dut, port):
+    # GET /echo returns 404'
+    Utility.console_log("[test] GET /echo returns 405 =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("GET", "/echo")
+    resp = conn.getresponse()
+    if not test_val("status_code", 405, resp.status):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def get_test_headers(dut, port):
+    # GET /test_header returns data of Header2'
+    Utility.console_log("[test] GET /test_header =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    custom_header = {"Header1": "Value1", "Header3": "Value3"}
+    header2_values = ["", "  ", "Value2", "   Value2", "Value2  ", "  Value2  "]
+    for val in header2_values:
+        custom_header["Header2"] = val
+        conn.request("GET", "/test_header", headers=custom_header)
+        resp = conn.getresponse()
+        if not test_val("status_code", 200, resp.status):
+            conn.close()
+            return False
+        hdr_val_start_idx = val.find("Value2")
+        if hdr_val_start_idx == -1:
+            if not test_val("header: Header2", "", resp.read().decode()):
+                conn.close()
+                return False
+        else:
+            if not test_val("header: Header2", val[hdr_val_start_idx:], resp.read().decode()):
+                conn.close()
+                return False
+        resp.read()
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def get_hello_type(dut, port):
+    # GET /hello/type_html returns text/html as Content-Type'
+    Utility.console_log("[test] GET /hello/type_html has Content-Type of text/html =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("GET", "/hello/type_html")
+    resp = conn.getresponse()
+    if not test_val("status_code", 200, resp.status):
+        conn.close()
+        return False
+    if not test_val("data", "Hello World!", resp.read().decode()):
+        conn.close()
+        return False
+    if not test_val("data", "text/html", resp.getheader('Content-Type')):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def get_hello_status(dut, port):
+    # GET /hello/status_500 returns status 500'
+    Utility.console_log("[test] GET /hello/status_500 returns status 500 =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("GET", "/hello/status_500")
+    resp = conn.getresponse()
+    if not test_val("status_code", 500, resp.status):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def get_false_uri(dut, port):
+    # GET /false_uri returns status 404'
+    Utility.console_log("[test] GET /false_uri returns status 404 =>", end=' ')
+    conn = http.client.HTTPConnection(dut, int(port), timeout=15)
+    conn.request("GET", "/false_uri")
+    resp = conn.getresponse()
+    if not test_val("status_code", 404, resp.status):
+        conn.close()
+        return False
+    Utility.console_log("Success")
+    conn.close()
+    return True
+
+
+def parallel_sessions_adder(dut, port, max_sessions):
+    # POSTs on /adder in parallel sessions
+    Utility.console_log("[test] POST {pipelined} on /adder in " + str(max_sessions) + " sessions =>", end=' ')
+    t = []
+    # Create all sessions
+    for i in range(max_sessions):
+        t.append(adder_thread(i, dut, port))
+
+    for i in range(len(t)):
+        t[i].start()
+
+    for i in range(len(t)):
+        t[i].join()
+
+    res = True
+    for i in range(len(t)):
+        if not test_val("Thread" + str(i) + " Failed", t[i].adder_result(), True):
+            res = False
+        t[i].close()
+    if (res):
+        Utility.console_log("Success")
+    return res
+
+
+def async_response_test(dut, port):
+    # Test that an asynchronous work is executed in the HTTPD's context
+    # This is tested by reading two responses over the same session
+    Utility.console_log("[test] Test HTTPD Work Queue (Async response) =>", end=' ')
+    s = Session(dut, port)
+
+    s.send_get('/async_data')
+    s.read_resp_hdrs()
+    if not test_val("First Response", "Hello World!", s.read_resp_data()):
+        s.close()
+        return False
+    s.read_resp_hdrs()
+    if not test_val("Second Response", "Hello Double World!", s.read_resp_data()):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def leftover_data_test(dut, port):
+    # Leftover data in POST is purged (valid and invalid URIs)
+    Utility.console_log("[test] Leftover data in POST is purged (valid and invalid URIs) =>", end=' ')
+    s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
+
+    s.request("POST", url='/leftover_data', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
+    resp = s.getresponse()
+    if not test_val("Partial data", "abcdefghij", resp.read().decode()):
+        s.close()
+        return False
+
+    s.request("GET", url='/hello')
+    resp = s.getresponse()
+    if not test_val("Hello World Data", "Hello World!", resp.read().decode()):
+        s.close()
+        return False
+
+    s.request("POST", url='/false_uri', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
+    resp = s.getresponse()
+    if not test_val("False URI Status", str(404), str(resp.status)):
+        s.close()
+        return False
+    # socket would have been closed by server due to error
+    s.close()
+
+    s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
+    s.request("GET", url='/hello')
+    resp = s.getresponse()
+    if not test_val("Hello World Data", "Hello World!", resp.read().decode()):
+        s.close()
+        return False
+
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def spillover_session(dut, port, max_sess):
+    # Session max_sess_sessions + 1 is rejected
+    Utility.console_log("[test] Session max_sess_sessions (" + str(max_sess) + ") + 1 is rejected =>", end=' ')
+    s = []
+    _verbose_ = True
+    for i in range(max_sess + 1):
+        if (_verbose_):
+            Utility.console_log("Executing " + str(i))
+        try:
+            a = http.client.HTTPConnection(dut + ":" + port, timeout=15)
+            a.request("GET", url='/hello')
+            resp = a.getresponse()
+            if not test_val("Connection " + str(i), "Hello World!", resp.read().decode()):
+                a.close()
+                break
+            s.append(a)
+        except Exception:
+            if (_verbose_):
+                Utility.console_log("Connection " + str(i) + " rejected")
+            a.close()
+            break
+
+    # Close open connections
+    for a in s:
+        a.close()
+
+    # Check if number of connections is equal to max_sess
+    Utility.console_log(["Fail","Success"][len(s) == max_sess])
+    return (len(s) == max_sess)
+
+
+def recv_timeout_test(dut, port):
+    Utility.console_log("[test] Timeout occurs if partial packet sent =>", end=' ')
+    s = Session(dut, port)
+    s.client.sendall(b"GE")
+    s.read_resp_hdrs()
+    resp = s.read_resp_data()
+    if not test_val("Request Timeout", "Server closed this connection", resp):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def packet_size_limit_test(dut, port, test_size):
+    Utility.console_log("[test] send size limit test =>", end=' ')
+    retry = 5
+    while (retry):
+        retry -= 1
+        Utility.console_log("data size = ", test_size)
+        s = http.client.HTTPConnection(dut + ":" + port, timeout=15)
+        random_data = ''.join(string.printable[random.randint(0,len(string.printable)) - 1] for _ in list(range(test_size)))
+        path = "/echo"
+        s.request("POST", url=path, body=random_data)
+        resp = s.getresponse()
+        if not test_val("Error", "200", str(resp.status)):
+            if test_val("Error", "500", str(resp.status)):
+                Utility.console_log("Data too large to be allocated")
+                test_size = test_size // 10
+            else:
+                Utility.console_log("Unexpected error")
+            s.close()
+            Utility.console_log("Retry...")
+            continue
+        resp = resp.read().decode()
+        result = (resp == random_data)
+        if not result:
+            test_val("Data size", str(len(random_data)), str(len(resp)))
+            s.close()
+            Utility.console_log("Retry...")
+            continue
+        s.close()
+        Utility.console_log("Success")
+        return True
+    Utility.console_log("Failed")
+    return False
+
+
+def arbitrary_termination_test(dut, port):
+    Utility.console_log("[test] Arbitrary termination test =>", end=' ')
+    cases = [
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\r\n\r\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\nHost: " + dut + "\r\nCustom: SomeValue\r\n\r\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\nCustom: SomeValue\r\n\r\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\n\r\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: SomeValue\r\n\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\nHost: " + dut + "\nCustom: SomeValue\n\n",
+            "code": "200",
+            "header": "SomeValue"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\r\nABCDE",
+            "code": "200",
+            "body": "ABCDE"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\r\n\nABCDE",
+            "code": "200",
+            "body": "ABCDE"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\nABCDE",
+            "code": "200",
+            "body": "ABCDE"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 5\n\n\rABCD",
+            "code": "200",
+            "body": "\rABCD"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\r\nCustom: SomeValue\r\r\n\r\r\n",
+            "code": "400"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\r\nHost: " + dut + "\r\n\r\n",
+            "code": "400"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\n\rHost: " + dut + "\r\n\r\n",
+            "code": "400"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\rCustom: SomeValue\r\n",
+            "code": "400"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom: Some\rValue\r\n",
+            "code": "400"
+        },
+        {
+            "request": "POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nCustom- SomeValue\r\n\r\n",
+            "code": "400"
+        }
+    ]
+    for case in cases:
+        s = Session(dut, port)
+        s.client.sendall((case['request']).encode())
+        resp_hdrs = s.read_resp_hdrs()
+        resp_body = s.read_resp_data()
+        s.close()
+        if not test_val("Response Code", case["code"], s.status):
+            return False
+        if "header" in case.keys():
+            resp_hdr_val = None
+            if "Custom" in resp_hdrs.keys():
+                resp_hdr_val = resp_hdrs["Custom"]
+            if not test_val("Response Header", case["header"], resp_hdr_val):
+                return False
+        if "body" in case.keys():
+            if not test_val("Response Body", case["body"], resp_body):
+                return False
+    Utility.console_log("Success")
+    return True
+
+
+def code_500_server_error_test(dut, port):
+    Utility.console_log("[test] 500 Server Error test =>", end=' ')
+    s = Session(dut, port)
+    # Sending a very large content length will cause malloc to fail
+    content_len = 2**30
+    s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: " + str(content_len) + "\r\n\r\nABCD").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Server Error", "500", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_501_method_not_impl(dut, port):
+    Utility.console_log("[test] 501 Method Not Implemented =>", end=' ')
+    s = Session(dut, port)
+    path = "/hello"
+    s.client.sendall(("ABC " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    # Presently server sends back 400 Bad Request
+    # if not test_val("Server Error", "501", s.status):
+    #     s.close()
+    #     return False
+    if not test_val("Server Error", "400", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_505_version_not_supported(dut, port):
+    Utility.console_log("[test] 505 Version Not Supported =>", end=' ')
+    s = Session(dut, port)
+    path = "/hello"
+    s.client.sendall(("GET " + path + " HTTP/2.0\r\nHost: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Server Error", "505", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_400_bad_request(dut, port):
+    Utility.console_log("[test] 400 Bad Request =>", end=' ')
+    s = Session(dut, port)
+    path = "/hello"
+    s.client.sendall(("XYZ " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Client Error", "400", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_404_not_found(dut, port):
+    Utility.console_log("[test] 404 Not Found =>", end=' ')
+    s = Session(dut, port)
+    path = "/dummy"
+    s.client.sendall(("GET " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Client Error", "404", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_405_method_not_allowed(dut, port):
+    Utility.console_log("[test] 405 Method Not Allowed =>", end=' ')
+    s = Session(dut, port)
+    path = "/hello"
+    s.client.sendall(("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Client Error", "405", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_408_req_timeout(dut, port):
+    Utility.console_log("[test] 408 Request Timeout =>", end=' ')
+    s = Session(dut, port)
+    s.client.sendall(("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 10\r\n\r\nABCD").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Client Error", "408", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def code_411_length_required(dut, port):
+    Utility.console_log("[test] 411 Length Required =>", end=' ')
+    s = Session(dut, port)
+    path = "/echo"
+    s.client.sendall(("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    # Presently server sends back 400 Bad Request
+    # if not test_val("Client Error", "411", s.status):
+    #    s.close()
+    #    return False
+    if not test_val("Client Error", "400", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+def send_getx_uri_len(dut, port, length):
+    s = Session(dut, port)
+    method = "GET "
+    version = " HTTP/1.1\r\n"
+    path = "/" + "x" * (length - len(method) - len(version) - len("/"))
+    s.client.sendall(method.encode())
+    time.sleep(1)
+    s.client.sendall(path.encode())
+    time.sleep(1)
+    s.client.sendall((version + "Host: " + dut + "\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    s.close()
+    return s.status
+
+
+def code_414_uri_too_long(dut, port, max_uri_len):
+    Utility.console_log("[test] 414 URI Too Long =>", end=' ')
+    status = send_getx_uri_len(dut, port, max_uri_len)
+    if not test_val("Client Error", "404", status):
+        return False
+    status = send_getx_uri_len(dut, port, max_uri_len + 1)
+    if not test_val("Client Error", "414", status):
+        return False
+    Utility.console_log("Success")
+    return True
+
+
+def send_postx_hdr_len(dut, port, length):
+    s = Session(dut, port)
+    path = "/echo"
+    host = "Host: " + dut
+    custom_hdr_field = "\r\nCustom: "
+    custom_hdr_val = "x" * (length - len(host) - len(custom_hdr_field) - len("\r\n\r\n") + len("0"))
+    request = ("POST " + path + " HTTP/1.1\r\n" + host + custom_hdr_field + custom_hdr_val + "\r\n\r\n").encode()
+    s.client.sendall(request[:length // 2])
+    time.sleep(1)
+    s.client.sendall(request[length // 2:])
+    hdr = s.read_resp_hdrs()
+    resp = s.read_resp_data()
+    s.close()
+    if hdr and ("Custom" in hdr):
+        return (hdr["Custom"] == custom_hdr_val), resp
+    return False, s.status
+
+
+def code_431_hdr_too_long(dut, port, max_hdr_len):
+    Utility.console_log("[test] 431 Header Too Long =>", end=' ')
+    res, status = send_postx_hdr_len(dut, port, max_hdr_len)
+    if not res:
+        return False
+    res, status = send_postx_hdr_len(dut, port, max_hdr_len + 1)
+    if not test_val("Client Error", "431", status):
+        return False
+    Utility.console_log("Success")
+    return True
+
+
+def test_upgrade_not_supported(dut, port):
+    Utility.console_log("[test] Upgrade Not Supported =>", end=' ')
+    s = Session(dut, port)
+    # path = "/hello"
+    s.client.sendall(("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n").encode())
+    s.read_resp_hdrs()
+    s.read_resp_data()
+    if not test_val("Client Error", "400", s.status):
+        s.close()
+        return False
+    s.close()
+    Utility.console_log("Success")
+    return True
+
+
+if __name__ == '__main__':
+    # Execution begins here...
+    # Configuration
+    # Max number of threads/sessions
+    max_sessions = 7
+    max_uri_len = 512
+    max_hdr_len = 512
+
+    parser = argparse.ArgumentParser(description='Run HTTPD Test')
+    parser.add_argument('-4','--ipv4', help='IPv4 address')
+    parser.add_argument('-6','--ipv6', help='IPv6 address')
+    parser.add_argument('-p','--port', help='Port')
+    args = vars(parser.parse_args())
+
+    dut4 = args['ipv4']
+    dut6 = args['ipv6']
+    port = args['port']
+    dut  = dut4
+
+    _verbose_ = True
+
+    Utility.console_log("### Basic HTTP Client Tests")
+    get_hello(dut, port)
+    post_hello(dut, port)
+    put_hello(dut, port)
+    post_echo(dut, port)
+    get_echo(dut, port)
+    put_echo(dut, port)
+    get_hello_type(dut, port)
+    get_hello_status(dut, port)
+    get_false_uri(dut, port)
+    get_test_headers(dut, port)
+
+    Utility.console_log("### Error code tests")
+    code_500_server_error_test(dut, port)
+    code_501_method_not_impl(dut, port)
+    code_505_version_not_supported(dut, port)
+    code_400_bad_request(dut, port)
+    code_404_not_found(dut, port)
+    code_405_method_not_allowed(dut, port)
+    code_408_req_timeout(dut, port)
+    code_414_uri_too_long(dut, port, max_uri_len)
+    code_431_hdr_too_long(dut, port, max_hdr_len)
+    test_upgrade_not_supported(dut, port)
+
+    # Not supported yet (Error on chunked request)
+    # code_411_length_required(dut, port)
+
+    Utility.console_log("### Sessions and Context Tests")
+    parallel_sessions_adder(dut, port, max_sessions)
+    leftover_data_test(dut, port)
+    async_response_test(dut, port)
+    spillover_session(dut, port, max_sessions)
+    recv_timeout_test(dut, port)
+    packet_size_limit_test(dut, port, 50 * 1024)
+    arbitrary_termination_test(dut, port)
+    get_hello(dut, port)
+
+    sys.exit()

+ 0 - 0
tools/tiny-test-fw/Utility/Attenuator.py → tools/ci/python_packages/idf_iperf_test_util/Attenuator.py


+ 0 - 0
tools/tiny-test-fw/Utility/LineChart.py → tools/ci/python_packages/idf_iperf_test_util/LineChart.py


+ 0 - 0
tools/tiny-test-fw/Utility/PowerControl.py → tools/ci/python_packages/idf_iperf_test_util/PowerControl.py


+ 0 - 0
examples/wifi/iperf/test_report.py → tools/ci/python_packages/idf_iperf_test_util/TestReport.py


+ 0 - 0
tools/ci/python_packages/idf_iperf_test_util/__init__.py


+ 0 - 0
tools/tiny-test-fw/App.py → tools/ci/python_packages/tiny_test_fw/App.py


+ 0 - 0
tools/tiny-test-fw/DUT.py → tools/ci/python_packages/tiny_test_fw/DUT.py


+ 0 - 0
tools/tiny-test-fw/Env.py → tools/ci/python_packages/tiny_test_fw/Env.py


+ 0 - 0
tools/tiny-test-fw/EnvConfig.py → tools/ci/python_packages/tiny_test_fw/EnvConfig.py


+ 0 - 0
tools/tiny-test-fw/EnvConfigTemplate.yml → tools/ci/python_packages/tiny_test_fw/EnvConfigTemplate.yml


+ 0 - 0
tools/tiny-test-fw/TinyFW.py → tools/ci/python_packages/tiny_test_fw/TinyFW.py


+ 2 - 3
tools/tiny-test-fw/Utility/CIAssignTest.py → tools/ci/python_packages/tiny_test_fw/Utility/CIAssignTest.py

@@ -44,14 +44,13 @@ import re
 import json
 import json
 
 
 import yaml
 import yaml
-
-from Utility import (CaseConfig, SearchCases, GitlabCIJob, console_log)
-
 try:
 try:
     from yaml import CLoader as Loader
     from yaml import CLoader as Loader
 except ImportError:
 except ImportError:
     from yaml import Loader as Loader
     from yaml import Loader as Loader
 
 
+from . import (CaseConfig, SearchCases, GitlabCIJob, console_log)
+
 
 
 class Group(object):
 class Group(object):
 
 

+ 6 - 9
tools/tiny-test-fw/Utility/CaseConfig.py → tools/ci/python_packages/tiny_test_fw/Utility/CaseConfig.py

@@ -20,7 +20,7 @@ Template Config File::
 
 
     TestConfig:
     TestConfig:
       app:
       app:
-        path: Users/Test/TinyTestFW/IDF/IDFApp.py
+        package: ttfw_idf
         class: Example
         class: Example
       dut:
       dut:
         path:
         path:
@@ -38,22 +38,20 @@ Template Config File::
         extra_data: some extra data passed to case with kwarg extra_data
         extra_data: some extra data passed to case with kwarg extra_data
         overwrite:  # overwrite test configs
         overwrite:  # overwrite test configs
           app:
           app:
-            path: Users/Test/TinyTestFW/IDF/IDFApp.py
+            package: ttfw_idf
             class: Example
             class: Example
       - name: xxx
       - name: xxx
 """
 """
+import importlib
 
 
 import yaml
 import yaml
-
-import TestCase
-
-from Utility import load_source
-
 try:
 try:
     from yaml import CLoader as Loader
     from yaml import CLoader as Loader
 except ImportError:
 except ImportError:
     from yaml import Loader as Loader
     from yaml import Loader as Loader
 
 
+from . import TestCase
+
 
 
 def _convert_to_lower_case_bytes(item):
 def _convert_to_lower_case_bytes(item):
     """
     """
@@ -172,8 +170,7 @@ class Parser(object):
         """
         """
         output = dict()
         output = dict()
         for key in overwrite:
         for key in overwrite:
-            path = overwrite[key]["path"]
-            module = load_source(path)
+            module = importlib.import_module(overwrite[key]["package"])
             output[key] = module.__getattribute__(overwrite[key]["class"])
             output[key] = module.__getattribute__(overwrite[key]["class"])
         return output
         return output
 
 

+ 0 - 0
tools/tiny-test-fw/Utility/GitlabCIJob.py → tools/ci/python_packages/tiny_test_fw/Utility/GitlabCIJob.py


+ 2 - 1
tools/tiny-test-fw/Utility/SearchCases.py → tools/ci/python_packages/tiny_test_fw/Utility/SearchCases.py

@@ -17,7 +17,8 @@ import os
 import fnmatch
 import fnmatch
 import types
 import types
 import copy
 import copy
-from Utility import load_source
+
+from . import load_source
 
 
 
 
 class Search(object):
 class Search(object):

+ 0 - 0
tools/tiny-test-fw/TestCase.py → tools/ci/python_packages/tiny_test_fw/Utility/TestCase.py


+ 0 - 0
tools/tiny-test-fw/Utility/__init__.py → tools/ci/python_packages/tiny_test_fw/Utility/__init__.py


+ 0 - 0
tools/ci/python_packages/tiny_test_fw/__init__.py


+ 2 - 2
tools/tiny-test-fw/Runner.py → tools/ci/python_packages/tiny_test_fw/bin/Runner.py

@@ -26,8 +26,8 @@ import sys
 import argparse
 import argparse
 import threading
 import threading
 
 
-import TinyFW
-from Utility import SearchCases, CaseConfig
+from tiny_test_fw import TinyFW
+from tiny_test_fw.Utility import SearchCases, CaseConfig
 
 
 
 
 class Runner(threading.Thread):
 class Runner(threading.Thread):

+ 0 - 0
tools/tiny-test-fw/example.py → tools/ci/python_packages/tiny_test_fw/bin/example.py


+ 0 - 0
tools/tiny-test-fw/docs/Makefile → tools/ci/python_packages/tiny_test_fw/docs/Makefile


+ 0 - 0
tools/ci/python_packages/tiny_test_fw/docs/_static/.keep


+ 0 - 0
tools/tiny-test-fw/docs/conf.py → tools/ci/python_packages/tiny_test_fw/docs/conf.py


+ 0 - 0
tools/tiny-test-fw/docs/index.rst → tools/ci/python_packages/tiny_test_fw/docs/index.rst


+ 0 - 1
tools/tiny-test-fw/requirements.txt → tools/ci/python_packages/tiny_test_fw/requirements.txt

@@ -2,4 +2,3 @@ pyserial
 pyyaml
 pyyaml
 junit_xml
 junit_xml
 netifaces
 netifaces
-matplotlib

+ 90 - 0
tools/ci/python_packages/ttfw_idf/CIAssignExampleTest.py

@@ -0,0 +1,90 @@
+# Copyright 2015-2017 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.
+
+"""
+Command line tool to assign example tests to CI test jobs.
+"""
+
+# TODO: Need to handle running examples on different chips
+import os
+import re
+import argparse
+import json
+
+import gitlab_api
+from tiny_test_fw.Utility import CIAssignTest
+
+
+EXAMPLE_BUILD_JOB_NAMES = ["build_examples_cmake_esp32", "build_examples_cmake_esp32s2"]
+IDF_PATH_FROM_ENV = os.getenv("IDF_PATH")
+if IDF_PATH_FROM_ENV:
+    ARTIFACT_INDEX_FILE = os.path.join(IDF_PATH_FROM_ENV,
+                                       "build_examples", "artifact_index.json")
+else:
+    ARTIFACT_INDEX_FILE = "artifact_index.json"
+
+
+class ExampleGroup(CIAssignTest.Group):
+    SORT_KEYS = CI_JOB_MATCH_KEYS = ["env_tag", "chip"]
+
+
+class CIExampleAssignTest(CIAssignTest.AssignTest):
+    CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
+
+
+def create_artifact_index_file(project_id=None, pipeline_id=None):
+    if project_id is None:
+        project_id = os.getenv("CI_PROJECT_ID")
+    if pipeline_id is None:
+        pipeline_id = os.getenv("CI_PIPELINE_ID")
+    gitlab_inst = gitlab_api.Gitlab(project_id)
+    artifact_index_list = []
+
+    def format_build_log_path():
+        return "build_examples/list_job_{}.json".format(job_info["parallel_num"])
+
+    for build_job_name in EXAMPLE_BUILD_JOB_NAMES:
+        job_info_list = gitlab_inst.find_job_id(build_job_name, pipeline_id=pipeline_id)
+        for job_info in job_info_list:
+            raw_data = gitlab_inst.download_artifact(job_info["id"], [format_build_log_path()])[0]
+            build_info_list = [json.loads(line) for line in raw_data.splitlines()]
+            for build_info in build_info_list:
+                build_info["ci_job_id"] = job_info["id"]
+                artifact_index_list.append(build_info)
+    try:
+        os.makedirs(os.path.dirname(ARTIFACT_INDEX_FILE))
+    except OSError:
+        # already created
+        pass
+
+    with open(ARTIFACT_INDEX_FILE, "w") as f:
+        json.dump(artifact_index_list, f)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument("test_case",
+                        help="test case folder or file")
+    parser.add_argument("ci_config_file",
+                        help="gitlab ci config file")
+    parser.add_argument("output_path",
+                        help="output path of config files")
+    parser.add_argument("--pipeline_id", "-p", type=int, default=None,
+                        help="pipeline_id")
+    args = parser.parse_args()
+
+    assign_test = CIExampleAssignTest(args.test_case, args.ci_config_file, case_group=ExampleGroup)
+    assign_test.assign_cases()
+    assign_test.output_configs(args.output_path)
+    create_artifact_index_file()

+ 2 - 10
tools/tiny-test-fw/CIAssignUnitTest.py → tools/ci/python_packages/ttfw_idf/CIAssignUnitTest.py

@@ -3,8 +3,6 @@ Command line tool to assign unit tests to CI test jobs.
 """
 """
 
 
 import re
 import re
-import os
-import sys
 import argparse
 import argparse
 
 
 import yaml
 import yaml
@@ -14,13 +12,7 @@ try:
 except ImportError:
 except ImportError:
     from yaml import Loader as Loader
     from yaml import Loader as Loader
 
 
-try:
-    from Utility import CIAssignTest
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path:
-        sys.path.insert(0, test_fw_path)
-    from Utility import CIAssignTest
+from tiny_test_fw.Utility import CIAssignTest
 
 
 
 
 class Group(CIAssignTest.Group):
 class Group(CIAssignTest.Group):
@@ -119,7 +111,7 @@ class Group(CIAssignTest.Group):
         if target:
         if target:
             overwrite = {
             overwrite = {
                 "dut": {
                 "dut": {
-                    "path": "IDF/IDFDUT.py",
+                    "package": "ttfw_idf",
                     "class": self.DUT_CLS_NAME[target],
                     "class": self.DUT_CLS_NAME[target],
                 }
                 }
             }
             }

+ 411 - 0
tools/ci/python_packages/ttfw_idf/IDFApp.py

@@ -0,0 +1,411 @@
+# Copyright 2015-2017 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.
+
+""" IDF Test Applications """
+import subprocess
+
+import os
+import json
+
+from tiny_test_fw import App
+from . import CIAssignExampleTest
+
+try:
+    import gitlab_api
+except ImportError:
+    gitlab_api = None
+
+
+def parse_flash_settings(path):
+    file_name = os.path.basename(path)
+    if file_name == "flasher_args.json":
+        # CMake version using build metadata file
+        with open(path, "r") as f:
+            args = json.load(f)
+        flash_files = [(offs, binary) for (offs, binary) in args["flash_files"].items() if offs != ""]
+        flash_settings = args["flash_settings"]
+        app_name = os.path.splitext(args["app"]["file"])[0]
+    else:
+        # GNU Make version uses download.config arguments file
+        with open(path, "r") as f:
+            args = f.readlines()[-1].split(" ")
+            flash_files = []
+            flash_settings = {}
+            for idx in range(0, len(args), 2):  # process arguments in pairs
+                if args[idx].startswith("--"):
+                    # strip the -- from the command line argument
+                    flash_settings[args[idx][2:]] = args[idx + 1]
+                else:
+                    # offs, filename
+                    flash_files.append((args[idx], args[idx + 1]))
+            # we can only guess app name in download.config.
+            for p in flash_files:
+                if not os.path.dirname(p[1]) and "partition" not in p[1]:
+                    # app bin usually in the same dir with download.config and it's not partition table
+                    app_name = os.path.splitext(p[1])[0]
+                    break
+            else:
+                app_name = None
+    return flash_files, flash_settings, app_name
+
+
+class Artifacts(object):
+    def __init__(self, dest_root_path, artifact_index_file, app_path, config_name, target):
+        assert gitlab_api
+        # at least one of app_path or config_name is not None. otherwise we can't match artifact
+        assert app_path or config_name
+        assert os.path.exists(artifact_index_file)
+        self.gitlab_inst = gitlab_api.Gitlab(os.getenv("CI_PROJECT_ID"))
+        self.dest_root_path = dest_root_path
+        with open(artifact_index_file, "r") as f:
+            artifact_index = json.load(f)
+        self.artifact_info = self._find_artifact(artifact_index, app_path, config_name, target)
+
+    @staticmethod
+    def _find_artifact(artifact_index, app_path, config_name, target):
+        for artifact_info in artifact_index:
+            match_result = True
+            if app_path:
+                match_result = app_path in artifact_info["app_dir"]
+            if config_name:
+                match_result = match_result and config_name == artifact_info["config"]
+            if target:
+                match_result = match_result and target == artifact_info["target"]
+            if match_result:
+                ret = artifact_info
+                break
+        else:
+            ret = None
+        return ret
+
+    def download_artifacts(self):
+        if self.artifact_info:
+            base_path = os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
+            job_id = self.artifact_info["ci_job_id"]
+
+            # 1. download flash args file
+            if self.artifact_info["build_system"] == "cmake":
+                flash_arg_file = os.path.join(base_path, "flasher_args.json")
+            else:
+                flash_arg_file = os.path.join(base_path, "download.config")
+
+            self.gitlab_inst.download_artifact(job_id, [flash_arg_file], self.dest_root_path)
+
+            # 2. download all binary files
+            flash_files, flash_settings, app_name = parse_flash_settings(os.path.join(self.dest_root_path,
+                                                                                      flash_arg_file))
+            artifact_files = [os.path.join(base_path, p[1]) for p in flash_files]
+            artifact_files.append(os.path.join(base_path, app_name + ".elf"))
+
+            self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
+
+            # 3. download sdkconfig file
+            self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), "sdkconfig")],
+                                               self.dest_root_path)
+        else:
+            base_path = None
+        return base_path
+
+    def download_artifact_files(self, file_names):
+        if self.artifact_info:
+            base_path = os.path.join(self.artifact_info["work_dir"], self.artifact_info["build_dir"])
+            job_id = self.artifact_info["ci_job_id"]
+
+            # download all binary files
+            artifact_files = [os.path.join(base_path, fn) for fn in file_names]
+            self.gitlab_inst.download_artifact(job_id, artifact_files, self.dest_root_path)
+
+            # download sdkconfig file
+            self.gitlab_inst.download_artifact(job_id, [os.path.join(os.path.dirname(base_path), "sdkconfig")],
+                                               self.dest_root_path)
+        else:
+            base_path = None
+        return base_path
+
+
+class IDFApp(App.BaseApp):
+    """
+    Implements common esp-idf application behavior.
+    idf applications should inherent from this class and overwrite method get_binary_path.
+    """
+
+    IDF_DOWNLOAD_CONFIG_FILE = "download.config"
+    IDF_FLASH_ARGS_FILE = "flasher_args.json"
+
+    def __init__(self, app_path, config_name=None, target=None):
+        super(IDFApp, self).__init__(app_path)
+        self.config_name = config_name
+        self.target = target
+        self.idf_path = self.get_sdk_path()
+        self.binary_path = self.get_binary_path(app_path, config_name, target)
+        self.elf_file = self._get_elf_file_path(self.binary_path)
+        assert os.path.exists(self.binary_path)
+        if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path):
+            if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path):
+                msg = ("Neither {} nor {} exists. "
+                       "Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' "
+                       "or 'idf.py build' "
+                       "for resolving the issue."
+                       "").format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE,
+                                  self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
+                raise AssertionError(msg)
+
+        self.flash_files, self.flash_settings = self._parse_flash_download_config()
+        self.partition_table = self._parse_partition_table()
+
+    @classmethod
+    def get_sdk_path(cls):
+        # type: () -> str
+        idf_path = os.getenv("IDF_PATH")
+        assert idf_path
+        assert os.path.exists(idf_path)
+        return idf_path
+
+    def _get_sdkconfig_paths(self):
+        """
+        returns list of possible paths where sdkconfig could be found
+
+        Note: could be overwritten by a derived class to provide other locations or order
+        """
+        return [os.path.join(self.binary_path, "sdkconfig"), os.path.join(self.binary_path, "..", "sdkconfig")]
+
+    def get_sdkconfig(self):
+        """
+        reads sdkconfig and returns a dictionary with all configuredvariables
+
+        :raise: AssertionError: if sdkconfig file does not exist in defined paths
+        """
+        d = {}
+        sdkconfig_file = None
+        for i in self._get_sdkconfig_paths():
+            if os.path.exists(i):
+                sdkconfig_file = i
+                break
+        assert sdkconfig_file is not None
+        with open(sdkconfig_file) as f:
+            for line in f:
+                configs = line.split('=')
+                if len(configs) == 2:
+                    d[configs[0]] = configs[1].rstrip()
+        return d
+
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        # type: (str, str, str) -> str
+        """
+        get binary path according to input app_path.
+
+        subclass must overwrite this method.
+
+        :param app_path: path of application
+        :param config_name: name of the application build config. Will match any config if None
+        :param target: target name. Will match for target if None
+        :return: abs app binary path
+        """
+        pass
+
+    @staticmethod
+    def _get_elf_file_path(binary_path):
+        ret = ""
+        file_names = os.listdir(binary_path)
+        for fn in file_names:
+            if os.path.splitext(fn)[1] == ".elf":
+                ret = os.path.join(binary_path, fn)
+        return ret
+
+    def _parse_flash_download_config(self):
+        """
+        Parse flash download config from build metadata files
+
+        Sets self.flash_files, self.flash_settings
+
+        (Called from constructor)
+
+        Returns (flash_files, flash_settings)
+        """
+
+        if self.IDF_FLASH_ARGS_FILE in os.listdir(self.binary_path):
+            # CMake version using build metadata file
+            path = os.path.join(self.binary_path, self.IDF_FLASH_ARGS_FILE)
+        else:
+            # GNU Make version uses download.config arguments file
+            path = os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
+
+        flash_files, flash_settings, app_name = parse_flash_settings(path)
+        # The build metadata file does not currently have details, which files should be encrypted and which not.
+        # Assume that all files should be encrypted if flash encryption is enabled in development mode.
+        sdkconfig_dict = self.get_sdkconfig()
+        flash_settings["encrypt"] = "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT" in sdkconfig_dict
+
+        # make file offsets into integers, make paths absolute
+        flash_files = [(int(offs, 0), os.path.join(self.binary_path, path.strip())) for (offs, path) in flash_files]
+
+        return flash_files, flash_settings
+
+    def _parse_partition_table(self):
+        """
+        Parse partition table contents based on app binaries
+
+        Returns partition_table data
+
+        (Called from constructor)
+        """
+        partition_tool = os.path.join(self.idf_path,
+                                      "components",
+                                      "partition_table",
+                                      "gen_esp32part.py")
+        assert os.path.exists(partition_tool)
+
+        for (_, path) in self.flash_files:
+            if "partition" in path:
+                partition_file = os.path.join(self.binary_path, path)
+                break
+        else:
+            raise ValueError("No partition table found for IDF binary path: {}".format(self.binary_path))
+
+        process = subprocess.Popen(["python", partition_tool, partition_file],
+                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        raw_data = process.stdout.read()
+        if isinstance(raw_data, bytes):
+            raw_data = raw_data.decode()
+        partition_table = dict()
+
+        for line in raw_data.splitlines():
+            if line[0] != "#":
+                try:
+                    _name, _type, _subtype, _offset, _size, _flags = line.split(",")
+                    if _size[-1] == "K":
+                        _size = int(_size[:-1]) * 1024
+                    elif _size[-1] == "M":
+                        _size = int(_size[:-1]) * 1024 * 1024
+                    else:
+                        _size = int(_size)
+                except ValueError:
+                    continue
+                partition_table[_name] = {
+                    "type": _type,
+                    "subtype": _subtype,
+                    "offset": _offset,
+                    "size": _size,
+                    "flags": _flags
+                }
+
+        return partition_table
+
+
+class Example(IDFApp):
+    def _get_sdkconfig_paths(self):
+        """
+        overrides the parent method to provide exact path of sdkconfig for example tests
+        """
+        return [os.path.join(self.binary_path, "..", "sdkconfig")]
+
+    def _try_get_binary_from_local_fs(self, app_path, config_name=None, target=None):
+        # build folder of example path
+        path = os.path.join(self.idf_path, app_path, "build")
+        if os.path.exists(path):
+            return path
+
+        if not config_name:
+            config_name = "default"
+
+        # Search for CI build folders.
+        # Path format: $IDF_PATH/build_examples/app_path_with_underscores/config/target
+        # (see tools/ci/build_examples_cmake.sh)
+        # For example: $IDF_PATH/build_examples/examples_get-started_blink/default/esp32
+        app_path_underscored = app_path.replace(os.path.sep, "_")
+        example_path = os.path.join(self.idf_path, "build_examples")
+        for dirpath in os.listdir(example_path):
+            if os.path.basename(dirpath) == app_path_underscored:
+                path = os.path.join(example_path, dirpath, config_name, target, "build")
+                if os.path.exists(path):
+                    return path
+                else:
+                    return None
+
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        path = self._try_get_binary_from_local_fs(app_path, config_name, target)
+        if path:
+            return path
+        else:
+            artifacts = Artifacts(self.idf_path, CIAssignExampleTest.ARTIFACT_INDEX_FILE,
+                                  app_path, config_name, target)
+            path = artifacts.download_artifacts()
+            if path:
+                return os.path.join(self.idf_path, path)
+            else:
+                raise OSError("Failed to find example binary")
+
+
+class LoadableElfExample(Example):
+    def __init__(self, app_path, app_files, config_name=None, target=None):
+        # add arg `app_files` for loadable elf example.
+        # Such examples only build elf files, so it doesn't generate flasher_args.json.
+        # So we can't get app files from config file. Test case should pass it to application.
+        super(IDFApp, self).__init__(app_path)
+        self.app_files = app_files
+        self.config_name = config_name
+        self.target = target
+        self.idf_path = self.get_sdk_path()
+        self.binary_path = self.get_binary_path(app_path, config_name, target)
+        self.elf_file = self._get_elf_file_path(self.binary_path)
+        assert os.path.exists(self.binary_path)
+
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        path = self._try_get_binary_from_local_fs(app_path, config_name, target)
+        if path:
+            return path
+        else:
+            artifacts = Artifacts(self.idf_path, CIAssignExampleTest.ARTIFACT_INDEX_FILE,
+                                  app_path, config_name, target)
+            path = artifacts.download_artifact_files(self.app_files)
+            if path:
+                return os.path.join(self.idf_path, path)
+            else:
+                raise OSError("Failed to find example binary")
+
+
+class UT(IDFApp):
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        if not config_name:
+            config_name = "default"
+
+        path = os.path.join(self.idf_path, app_path)
+        default_build_path = os.path.join(path, "build")
+        if os.path.exists(default_build_path):
+            return path
+
+        # first try to get from build folder of unit-test-app
+        path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
+        if os.path.exists(path):
+            # found, use bin in build path
+            return path
+
+        # ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
+        path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", config_name)
+        if os.path.exists(path):
+            return path
+
+        raise OSError("Failed to get unit-test-app binary path")
+
+
+class SSC(IDFApp):
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        # TODO: to implement SSC get binary path
+        return app_path
+
+
+class AT(IDFApp):
+    def get_binary_path(self, app_path, config_name=None, target=None):
+        # TODO: to implement AT get binary path
+        return app_path

+ 1 - 2
tools/tiny-test-fw/IDF/IDFDUT.py → tools/ci/python_packages/ttfw_idf/IDFDUT.py

@@ -30,8 +30,7 @@ except ImportError:
 
 
 from serial.tools import list_ports
 from serial.tools import list_ports
 
 
-import DUT
-import Utility
+from tiny_test_fw import DUT, Utility
 
 
 try:
 try:
     import esptool
     import esptool

+ 3 - 4
tools/tiny-test-fw/IDF/__init__.py → tools/ci/python_packages/ttfw_idf/__init__.py

@@ -14,10 +14,9 @@
 import os
 import os
 import re
 import re
 
 
-import TinyFW
-import Utility
-from IDF.IDFApp import IDFApp, Example, UT
-from IDF.IDFDUT import IDFDUT
+from tiny_test_fw import TinyFW, Utility
+from IDFApp import IDFApp, Example, LoadableElfExample, UT  # noqa: export all Apps for users
+from IDFDUT import IDFDUT, ESP32DUT, ESP32S2DUT, ESP8266DUT  # noqa: export DUTs for users
 
 
 
 
 def format_case_id(chip, case_name):
 def format_case_id(chip, case_name):

+ 0 - 0
examples/provisioning/softap_prov/utils/wifi_tools.py → tools/ci/python_packages/wifi_tools.py


+ 3 - 0
tools/ci/setup_python.sh

@@ -51,3 +51,6 @@ else
     echo 'No /opt/pyenv/activate exists and no Python interpreter is found!'
     echo 'No /opt/pyenv/activate exists and no Python interpreter is found!'
     exit 1
     exit 1
 fi
 fi
+
+# add esp-idf local package path to PYTHONPATH so it can be imported directly
+export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages:$PYTHONPATH"

+ 8 - 0
tools/cmake/git_submodules.cmake

@@ -11,6 +11,14 @@ if(NOT GIT_FOUND)
 else()
 else()
 
 
     function(git_submodule_check root_path)
     function(git_submodule_check root_path)
+        # for internal use:
+        # skip submodule check if running on Gitlab CI and job is configured as not clone submodules
+        if($ENV{IDF_SKIP_CHECK_SUBMODULES})
+            if($ENV{IDF_SKIP_CHECK_SUBMODULES} EQUAL 1)
+                message("skip submodule check on internal CI")
+                return()
+            endif()
+        endif()
 
 
         execute_process(
         execute_process(
             COMMAND ${GIT_EXECUTABLE} submodule status
             COMMAND ${GIT_EXECUTABLE} submodule status

+ 1 - 0
tools/esp_prov/__init__.py

@@ -0,0 +1 @@
+from esp_prov import *  # noqa: export esp_prov module to users

+ 0 - 56
tools/tiny-test-fw/CIAssignExampleTest.py

@@ -1,56 +0,0 @@
-# Copyright 2015-2017 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.
-
-"""
-Command line tool to assign example tests to CI test jobs.
-"""
-
-# TODO: Need to handle running examples on different chips
-import os
-import sys
-import re
-import argparse
-
-try:
-    from Utility.CIAssignTest import AssignTest
-except ImportError:
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path:
-        sys.path.insert(0, test_fw_path)
-    from Utility.CIAssignTest import AssignTest
-
-from Utility.CIAssignTest import Group
-
-
-class ExampleGroup(Group):
-    SORT_KEYS = CI_JOB_MATCH_KEYS = ["env_tag", "chip"]
-
-
-class CIExampleAssignTest(AssignTest):
-    CI_TEST_JOB_PATTERN = re.compile(r"^example_test_.+")
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser()
-    parser.add_argument("test_case",
-                        help="test case folder or file")
-    parser.add_argument("ci_config_file",
-                        help="gitlab ci config file")
-    parser.add_argument("output_path",
-                        help="output path of config files")
-    args = parser.parse_args()
-
-    assign_test = CIExampleAssignTest(args.test_case, args.ci_config_file, case_group=ExampleGroup)
-    assign_test.assign_cases()
-    assign_test.output_configs(args.output_path)

+ 0 - 273
tools/tiny-test-fw/IDF/IDFApp.py

@@ -1,273 +0,0 @@
-# Copyright 2015-2017 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.
-
-""" IDF Test Applications """
-import subprocess
-
-import os
-import json
-import App
-
-
-class IDFApp(App.BaseApp):
-    """
-    Implements common esp-idf application behavior.
-    idf applications should inherent from this class and overwrite method get_binary_path.
-    """
-
-    IDF_DOWNLOAD_CONFIG_FILE = "download.config"
-    IDF_FLASH_ARGS_FILE = "flasher_args.json"
-
-    def __init__(self, app_path, config_name=None, target=None):
-        super(IDFApp, self).__init__(app_path)
-        self.config_name = config_name
-        self.target = target
-        self.idf_path = self.get_sdk_path()
-        self.binary_path = self.get_binary_path(app_path, config_name)
-        self.elf_file = self._get_elf_file_path(self.binary_path)
-        assert os.path.exists(self.binary_path)
-        sdkconfig_dict = self.get_sdkconfig()
-        if "CONFIG_APP_BUILD_GENERATE_BINARIES" in sdkconfig_dict:
-            # There are no flashing targets available when no binaries where generated.
-            if self.IDF_DOWNLOAD_CONFIG_FILE not in os.listdir(self.binary_path):
-                if self.IDF_FLASH_ARGS_FILE not in os.listdir(self.binary_path):
-                    msg = ("Neither {} nor {} exists. "
-                           "Try to run 'make print_flash_cmd | tail -n 1 > {}/{}' "
-                           "or 'idf.py build' "
-                           "for resolving the issue."
-                           "").format(self.IDF_DOWNLOAD_CONFIG_FILE, self.IDF_FLASH_ARGS_FILE,
-                                      self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE)
-                    raise AssertionError(msg)
-
-            self.flash_files, self.flash_settings = self._parse_flash_download_config()
-            self.partition_table = self._parse_partition_table()
-
-    @classmethod
-    def get_sdk_path(cls):
-        idf_path = os.getenv("IDF_PATH")
-        assert idf_path
-        assert os.path.exists(idf_path)
-        return idf_path
-
-    def _get_sdkconfig_paths(self):
-        """
-        returns list of possible paths where sdkconfig could be found
-
-        Note: could be overwritten by a derived class to provide other locations or order
-        """
-        return [os.path.join(self.binary_path, "sdkconfig"), os.path.join(self.binary_path, "..", "sdkconfig")]
-
-    def get_sdkconfig(self):
-        """
-        reads sdkconfig and returns a dictionary with all configuredvariables
-
-        :param sdkconfig_file: location of sdkconfig
-        :raise: AssertionError: if sdkconfig file does not exist in defined paths
-        """
-        d = {}
-        sdkconfig_file = None
-        for i in self._get_sdkconfig_paths():
-            if os.path.exists(i):
-                sdkconfig_file = i
-                break
-        assert sdkconfig_file is not None
-        with open(sdkconfig_file) as f:
-            for line in f:
-                configs = line.split('=')
-                if len(configs) == 2:
-                    d[configs[0]] = configs[1].rstrip()
-        return d
-
-    def get_binary_path(self, app_path, config_name=None):
-        """
-        get binary path according to input app_path.
-
-        subclass must overwrite this method.
-
-        :param app_path: path of application
-        :param config_name: name of the application build config
-        :return: abs app binary path
-        """
-        pass
-
-    @staticmethod
-    def _get_elf_file_path(binary_path):
-        ret = ""
-        file_names = os.listdir(binary_path)
-        for fn in file_names:
-            if os.path.splitext(fn)[1] == ".elf":
-                ret = os.path.join(binary_path, fn)
-        return ret
-
-    def _parse_flash_download_config(self):
-        """
-        Parse flash download config from build metadata files
-
-        Sets self.flash_files, self.flash_settings
-
-        (Called from constructor)
-
-        Returns (flash_files, flash_settings)
-        """
-
-        if self.IDF_FLASH_ARGS_FILE in os.listdir(self.binary_path):
-            # CMake version using build metadata file
-            with open(os.path.join(self.binary_path, self.IDF_FLASH_ARGS_FILE), "r") as f:
-                args = json.load(f)
-                flash_files = [(offs,file) for (offs,file) in args["flash_files"].items() if offs != ""]
-                flash_settings = args["flash_settings"]
-        else:
-            # GNU Make version uses download.config arguments file
-            with open(os.path.join(self.binary_path, self.IDF_DOWNLOAD_CONFIG_FILE), "r") as f:
-                args = f.readlines()[-1].split(" ")
-                flash_files = []
-                flash_settings = {}
-                for idx in range(0, len(args), 2):  # process arguments in pairs
-                    if args[idx].startswith("--"):
-                        # strip the -- from the command line argument
-                        flash_settings[args[idx][2:]] = args[idx + 1]
-                    else:
-                        # offs, filename
-                        flash_files.append((args[idx], args[idx + 1]))
-
-        # The build metadata file does not currently have details, which files should be encrypted and which not.
-        # Assume that all files should be encrypted if flash encryption is enabled in development mode.
-        sdkconfig_dict = self.get_sdkconfig()
-        flash_settings["encrypt"] = "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT" in sdkconfig_dict
-
-        # make file offsets into integers, make paths absolute
-        flash_files = [(int(offs, 0), os.path.join(self.binary_path, path.strip())) for (offs, path) in flash_files]
-
-        return (flash_files, flash_settings)
-
-    def _parse_partition_table(self):
-        """
-        Parse partition table contents based on app binaries
-
-        Returns partition_table data
-
-        (Called from constructor)
-        """
-        partition_tool = os.path.join(self.idf_path,
-                                      "components",
-                                      "partition_table",
-                                      "gen_esp32part.py")
-        assert os.path.exists(partition_tool)
-
-        for (_, path) in self.flash_files:
-            if "partition" in path:
-                partition_file = os.path.join(self.binary_path, path)
-                break
-        else:
-            raise ValueError("No partition table found for IDF binary path: {}".format(self.binary_path))
-
-        process = subprocess.Popen(["python", partition_tool, partition_file],
-                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        raw_data = process.stdout.read()
-        if isinstance(raw_data, bytes):
-            raw_data = raw_data.decode()
-        partition_table = dict()
-
-        for line in raw_data.splitlines():
-            if line[0] != "#":
-                try:
-                    _name, _type, _subtype, _offset, _size, _flags = line.split(",")
-                    if _size[-1] == "K":
-                        _size = int(_size[:-1]) * 1024
-                    elif _size[-1] == "M":
-                        _size = int(_size[:-1]) * 1024 * 1024
-                    else:
-                        _size = int(_size)
-                except ValueError:
-                    continue
-                partition_table[_name] = {
-                    "type": _type,
-                    "subtype": _subtype,
-                    "offset": _offset,
-                    "size": _size,
-                    "flags": _flags
-                }
-
-        return partition_table
-
-
-class Example(IDFApp):
-    def _get_sdkconfig_paths(self):
-        """
-        overrides the parent method to provide exact path of sdkconfig for example tests
-        """
-        return [os.path.join(self.binary_path, "..", "sdkconfig")]
-
-    def get_binary_path(self, app_path, config_name=None):
-        # build folder of example path
-        path = os.path.join(self.idf_path, app_path, "build")
-        if os.path.exists(path):
-            return path
-
-        if not config_name:
-            config_name = "default"
-
-        # Search for CI build folders.
-        # Path format: $IDF_PATH/build_examples/app_path_with_underscores/config/target
-        # (see tools/ci/build_examples_cmake.sh)
-        # For example: $IDF_PATH/build_examples/examples_get-started_blink/default/esp32
-        app_path_underscored = app_path.replace(os.path.sep, "_")
-        example_path = os.path.join(self.idf_path, "build_examples")
-        for dirpath in os.listdir(example_path):
-            if os.path.basename(dirpath) == app_path_underscored:
-                path = os.path.join(example_path, dirpath, config_name, self.target, "build")
-                return path
-
-        raise OSError("Failed to find example binary")
-
-
-class UT(IDFApp):
-    def get_binary_path(self, app_path, config_name=None):
-        """
-        :param app_path: app path
-        :param config_name: config name
-        :return: binary path
-        """
-        if not config_name:
-            config_name = "default"
-
-        path = os.path.join(self.idf_path, app_path)
-        default_build_path = os.path.join(path, "build")
-        if os.path.exists(default_build_path):
-            return path
-
-        # first try to get from build folder of unit-test-app
-        path = os.path.join(self.idf_path, "tools", "unit-test-app", "build")
-        if os.path.exists(path):
-            # found, use bin in build path
-            return path
-
-        # ``make ut-build-all-configs`` or ``make ut-build-CONFIG`` will copy binary to output folder
-        path = os.path.join(self.idf_path, "tools", "unit-test-app", "output", config_name)
-        if os.path.exists(path):
-            return path
-
-        raise OSError("Failed to get unit-test-app binary path")
-
-
-class SSC(IDFApp):
-    def get_binary_path(self, app_path, config_name=None):
-        # TODO: to implement SSC get binary path
-        return app_path
-
-
-class AT(IDFApp):
-    def get_binary_path(self, app_path, config_name=None):
-        # TODO: to implement AT get binary path
-        return app_path

+ 13 - 34
tools/unit-test-app/unit_test.py

@@ -19,33 +19,12 @@ Test script for unit test case.
 """
 """
 
 
 import re
 import re
-import os
-import sys
 import time
 import time
 import argparse
 import argparse
 import threading
 import threading
 
 
-try:
-    import TinyFW
-except ImportError:
-    # if we want to run test case outside `tiny-test-fw` folder,
-    # we need to insert tiny-test-fw path into sys path
-    test_fw_path = os.getenv("TEST_FW_PATH")
-    if test_fw_path and test_fw_path not in sys.path:
-        sys.path.insert(0, test_fw_path)
-    else:
-        # or try the copy in IDF
-        idf_path = os.getenv("IDF_PATH")
-        tiny_test_path = idf_path + "/tools/tiny-test-fw"
-        if os.path.exists(tiny_test_path):
-            sys.path.insert(0, tiny_test_path)
-    import TinyFW
-
-import IDF
-import Utility
-import Env
-from DUT import ExpectTimeout
-from IDF.IDFApp import UT
+from tiny_test_fw import TinyFW, Utility, Env, DUT
+import ttfw_idf
 
 
 
 
 UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests."
 UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests."
@@ -180,7 +159,7 @@ def reset_dut(dut):
         try:
         try:
             dut.expect("0 Tests 0 Failures 0 Ignored", timeout=TEST_HISTORY_CHECK_TIMEOUT)
             dut.expect("0 Tests 0 Failures 0 Ignored", timeout=TEST_HISTORY_CHECK_TIMEOUT)
             break
             break
-        except ExpectTimeout:
+        except DUT.ExpectTimeout:
             pass
             pass
     else:
     else:
         raise AssertionError("Reset {} ({}) failed!".format(dut.name, dut.port))
         raise AssertionError("Reset {} ({}) failed!".format(dut.name, dut.port))
@@ -255,14 +234,14 @@ def run_one_normal_case(dut, one_case, junit_test_case):
                            (FINISH_PATTERN, handle_test_finish),
                            (FINISH_PATTERN, handle_test_finish),
                            (UT_APP_BOOT_UP_DONE, handle_reset_finish),
                            (UT_APP_BOOT_UP_DONE, handle_reset_finish),
                            timeout=one_case["timeout"])
                            timeout=one_case["timeout"])
-        except ExpectTimeout:
+        except DUT.ExpectTimeout:
             Utility.console_log("Timeout in expect", color="orange")
             Utility.console_log("Timeout in expect", color="orange")
             junit_test_case.add_failure_info("timeout")
             junit_test_case.add_failure_info("timeout")
             one_case_finish(False)
             one_case_finish(False)
             break
             break
 
 
 
 
-@IDF.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True)
+@ttfw_idf.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True)
 def run_unit_test_cases(env, extra_data):
 def run_unit_test_cases(env, extra_data):
     """
     """
     extra_data can be three types of value
     extra_data can be three types of value
@@ -401,7 +380,7 @@ class Handler(threading.Thread):
             time.sleep(1)
             time.sleep(1)
             self.dut.write("\"{}\"".format(self.parent_case_name))
             self.dut.write("\"{}\"".format(self.parent_case_name))
             self.dut.expect("Running " + self.parent_case_name + "...")
             self.dut.expect("Running " + self.parent_case_name + "...")
-        except ExpectTimeout:
+        except DUT.ExpectTimeout:
             Utility.console_log("No case detected!", color="orange")
             Utility.console_log("No case detected!", color="orange")
         while not self.finish and not self.force_stop.isSet():
         while not self.finish and not self.force_stop.isSet():
             try:
             try:
@@ -411,7 +390,7 @@ class Handler(threading.Thread):
                                     (self.SEND_SIGNAL_PATTERN, device_send_action),  # send signal pattern
                                     (self.SEND_SIGNAL_PATTERN, device_send_action),  # send signal pattern
                                     (self.FINISH_PATTERN, handle_device_test_finish),  # test finish pattern
                                     (self.FINISH_PATTERN, handle_device_test_finish),  # test finish pattern
                                     timeout=self.timeout)
                                     timeout=self.timeout)
-            except ExpectTimeout:
+            except DUT.ExpectTimeout:
                 Utility.console_log("Timeout in expect", color="orange")
                 Utility.console_log("Timeout in expect", color="orange")
                 one_device_case_finish(False)
                 one_device_case_finish(False)
                 break
                 break
@@ -471,7 +450,7 @@ def run_one_multiple_devices_case(duts, ut_config, env, one_case, app_bin, junit
     return result
     return result
 
 
 
 
-@IDF.idf_unit_test(env_tag="UT_T2_1", junit_report_by_case=True)
+@ttfw_idf.idf_unit_test(env_tag="UT_T2_1", junit_report_by_case=True)
 def run_multiple_devices_cases(env, extra_data):
 def run_multiple_devices_cases(env, extra_data):
     """
     """
      extra_data can be two types of value
      extra_data can be two types of value
@@ -618,7 +597,7 @@ def run_one_multiple_stage_case(dut, one_case, junit_test_case):
                                (FINISH_PATTERN, handle_test_finish),
                                (FINISH_PATTERN, handle_test_finish),
                                (UT_APP_BOOT_UP_DONE, handle_next_stage),
                                (UT_APP_BOOT_UP_DONE, handle_next_stage),
                                timeout=one_case["timeout"])
                                timeout=one_case["timeout"])
-            except ExpectTimeout:
+            except DUT.ExpectTimeout:
                 Utility.console_log("Timeout in expect", color="orange")
                 Utility.console_log("Timeout in expect", color="orange")
                 one_case_finish(False)
                 one_case_finish(False)
                 break
                 break
@@ -627,7 +606,7 @@ def run_one_multiple_stage_case(dut, one_case, junit_test_case):
             break
             break
 
 
 
 
-@IDF.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True)
+@ttfw_idf.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True)
 def run_multiple_stage_cases(env, extra_data):
 def run_multiple_stage_cases(env, extra_data):
     """
     """
     extra_data can be 2 types of value
     extra_data can be 2 types of value
@@ -734,7 +713,7 @@ def detect_update_unit_test_info(env, extra_data, app_bin):
             for _dic in extra_data:
             for _dic in extra_data:
                 if 'type' not in _dic:
                 if 'type' not in _dic:
                     raise ValueError("Unit test \"{}\" doesn't exist in the flashed device!".format(_dic.get('name')))
                     raise ValueError("Unit test \"{}\" doesn't exist in the flashed device!".format(_dic.get('name')))
-        except ExpectTimeout:
+        except DUT.ExpectTimeout:
             Utility.console_log("Timeout during getting the test list", color="red")
             Utility.console_log("Timeout during getting the test list", color="red")
         finally:
         finally:
             dut.close()
             dut.close()
@@ -789,8 +768,8 @@ if __name__ == '__main__':
     TinyFW.set_default_config(env_config_file=args.env_config_file)
     TinyFW.set_default_config(env_config_file=args.env_config_file)
 
 
     env_config = TinyFW.get_default_config()
     env_config = TinyFW.get_default_config()
-    env_config['app'] = UT
-    env_config['dut'] = IDF.IDFDUT
+    env_config['app'] = ttfw_idf.UT
+    env_config['dut'] = ttfw_idf.IDFDUT
     env_config['test_suite_name'] = 'unit_test_parsing'
     env_config['test_suite_name'] = 'unit_test_parsing'
     test_env = Env.Env(**env_config)
     test_env = Env.Env(**env_config)
     detect_update_unit_test_info(test_env, extra_data=list_of_dicts, app_bin=args.app_bin)
     detect_update_unit_test_info(test_env, extra_data=list_of_dicts, app_bin=args.app_bin)