From 4314c78ca005ad059e0d833ed7a6f659b39469f0 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Mon, 28 Nov 2022 14:35:02 +0100 Subject: [PATCH] feat(esp_modem): Added target test --- .github/workflows/target-test.yml | 65 ++++++++++++++- ci/clean_build_artifacts.sh | 7 ++ ci/requirements.txt | 1 + .../pppos_client/pytest_pppos_client.py | 24 ++++++ .../examples/pppos_client/sdkconfig.ci.sim800 | 13 +++ .../esp_modem/test/target/main/CMakeLists.txt | 2 + .../test/target/main/Kconfig.projbuild | 50 ++++++----- .../esp_modem/test/target/main/NetworkDCE.cpp | 3 + .../esp_modem/test/target/pytest_pppd.py | 83 +++++++++++++++++++ .../esp_modem/test/target/sdkconfig.defaults | 1 + 10 files changed, 229 insertions(+), 20 deletions(-) create mode 100755 ci/clean_build_artifacts.sh create mode 100644 components/esp_modem/examples/pppos_client/pytest_pppos_client.py create mode 100644 components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800 create mode 100644 components/esp_modem/test/target/pytest_pppd.py diff --git a/.github/workflows/target-test.yml b/.github/workflows/target-test.yml index 72149400f..060f48c67 100644 --- a/.github/workflows/target-test.yml +++ b/.github/workflows/target-test.yml @@ -6,7 +6,7 @@ jobs: build_esp_modem: strategy: matrix: - idf_ver: ["latest", "release-v4.1", "release-v4.2", "release-v4.3", "release-v4.4"] + idf_ver: ["latest", "release-v4.1", "release-v4.2", "release-v4.3", "release-v4.4", "release-v5.0"] example: ["pppos_client", "modem_console", "ap_to_pppos", "simple_cmux_client"] idf_target: ["esp32"] exclude: @@ -310,3 +310,66 @@ jobs: with: name: examples_results_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.example }} path: components/asio/examples/${{ matrix.example }}/*.xml + + build_esp_modem_tests: + strategy: + matrix: + idf_ver: ["latest"] + idf_target: ["esp32c3"] + test: [ { app: pppd, path: test/target }, { app: sim800, path: examples/pppos_client } ] + runs-on: ubuntu-20.04 + container: espressif/idf:${{ matrix.idf_ver }} + env: + TEST_DIR: components/esp_modem/${{ matrix.test.path }} + steps: + - name: Checkout esp-protocols + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Build esp-modem target tests with IDF-${{ matrix.idf_ver }} for ${{ matrix.idf_target }} + env: + IDF_TARGET: ${{ matrix.idf_target }} + SDKCONFIG: sdkconfig.ci.${{ matrix.test.app }} + shell: bash + working-directory: ${{ env.TEST_DIR }} + run: | + . ${IDF_PATH}/export.sh + rm -rf sdkconfig build + [ -f ${SDKCONFIG} ] && cp ${SDKCONFIG} sdkconfig.defaults + idf.py set-target ${{ matrix.idf_target }} + idf.py build + $GITHUB_WORKSPACE/ci/clean_build_artifacts.sh ${GITHUB_WORKSPACE}/${TEST_DIR}/build + ls build + - uses: actions/upload-artifact@v2 + with: + name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }} + path: ${{ env.TEST_DIR }}/build + if-no-files-found: error + + run_esp_modem_tests: + strategy: + matrix: + idf_ver: ["latest"] + idf_target: ["esp32c3"] + test: [ { app: pppd, path: test/target }, { app: sim800, path: examples/pppos_client } ] + name: Run esp_modem Test on target + needs: build_esp_modem_tests + runs-on: + - self-hosted + - BrnoRPI-GH006 + env: + TEST_DIR: components/esp_modem/${{ matrix.test.path }} + # Skip running on forks since it won't have access to secrets + if: github.repository == 'espressif/esp-protocols' + steps: + - name: Clear repository + run: sudo rm -fr $GITHUB_WORKSPACE && mkdir $GITHUB_WORKSPACE + - uses: actions/checkout@v3 + - uses: actions/download-artifact@v2 + with: + name: modem_target_bin_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${{ matrix.test.app }} + path: ${{ env.TEST_DIR }}/build + - name: Run Example Test on target + working-directory: ${{ env.TEST_DIR }} + run: | + python -m pytest --log-cli-level DEBUG --target=${{ matrix.idf_target }} diff --git a/ci/clean_build_artifacts.sh b/ci/clean_build_artifacts.sh new file mode 100755 index 000000000..362fe4d84 --- /dev/null +++ b/ci/clean_build_artifacts.sh @@ -0,0 +1,7 @@ +# Remove everything, but +# - elf/bin files in the build dir +# - partition-table and bootloader binaries +# - flasher args +# - sdkconfigs (header and json) +# (Ignoring the command failure as it refuses to delete nonempty dirs) +find $1 ! -regex ".*/build/[^/]+.\(bin\|elf\)" -a ! -regex ".*\(bootloader\|partition-table\).bin" -a ! -name "flasher_args.json" -a ! -regex ".*/build/config/sdkconfig.\(h\|json\)" -delete || true diff --git a/ci/requirements.txt b/ci/requirements.txt index 60d3b2604..e8ecae73b 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -5,3 +5,4 @@ SimpleWebSocketServer dpkt pytest idf_build_apps +netifaces diff --git a/components/esp_modem/examples/pppos_client/pytest_pppos_client.py b/components/esp_modem/examples/pppos_client/pytest_pppos_client.py new file mode 100644 index 000000000..74a45ad2e --- /dev/null +++ b/components/esp_modem/examples/pppos_client/pytest_pppos_client.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +from __future__ import print_function, unicode_literals + + +def test_pppos_connect(dut): + """ + steps: + 1. initializes connection with SIM800 + 2. checks we get an IP + 3. checks for the MQTT events + 4. checks that the client cleanly disconnects + """ + # Check the sequence of connecting, publishing, disconnecting + dut.expect('Modem Connect to PPP Server') + # Check for MQTT connection and the data event + dut.expect('MQTT_EVENT_CONNECTED') + dut.expect('MQTT_EVENT_DATA') + dut.expect('TOPIC=/topic/esp-pppos') + dut.expect('DATA=esp32-pppos') + # Check that we have disconnected + dut.expect('User interrupted event') + # And can use commands again + dut.expect('IMSI=[0-9]+') diff --git a/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800 b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800 new file mode 100644 index 000000000..98ab7b4be --- /dev/null +++ b/components/esp_modem/examples/pppos_client/sdkconfig.ci.sim800 @@ -0,0 +1,13 @@ +# Override some defaults to enable PPP +CONFIG_LWIP_PPP_SUPPORT=y +CONFIG_LWIP_PPP_NOTIFY_PHASE_SUPPORT=y +CONFIG_LWIP_PPP_PAP_SUPPORT=y +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096 +CONFIG_LWIP_PPP_ENABLE_IPV6=n +CONFIG_EXAMPLE_MODEM_UART_TX_PIN=4 +CONFIG_EXAMPLE_MODEM_UART_RX_PIN=5 +CONFIG_EXAMPLE_MODEM_DEVICE_SIM800=y +CONFIG_EXAMPLE_MODEM_DEVICE_BG96=n +CONFIG_EXAMPLE_MODEM_PPP_APN="lpwa.vodafone.com" +CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y +CONFIG_ESP32_PANIC_PRINT_HALT=y diff --git a/components/esp_modem/test/target/main/CMakeLists.txt b/components/esp_modem/test/target/main/CMakeLists.txt index 37807fcf4..12a1ae849 100644 --- a/components/esp_modem/test/target/main/CMakeLists.txt +++ b/components/esp_modem/test/target/main/CMakeLists.txt @@ -8,3 +8,5 @@ set_target_properties(${COMPONENT_LIB} PROPERTIES CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS ON ) + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/components/esp_modem/test/target/main/Kconfig.projbuild b/components/esp_modem/test/target/main/Kconfig.projbuild index 19e9f86e0..8b32e0252 100644 --- a/components/esp_modem/test/target/main/Kconfig.projbuild +++ b/components/esp_modem/test/target/main/Kconfig.projbuild @@ -1,26 +1,38 @@ -menu "Example Configuration" +menu "Test App Configuration" - config ESP_WIFI_SSID - string "WiFi SSID" - default "myssid" + config TEST_APP_PPP_SERVER_IP + string "IP address of PPP server" + default "10.0.0.1" help - SSID (network name) for the example to connect to. + IP address of PPP server. Note: this is also the address + where the TCP server is started to test the connection - config ESP_WIFI_PASSWORD - string "WiFi Password" - default "mypassword" + config TEST_APP_PPP_CLIENT_IP + string "IP address of PPP client" + default "10.0.0.2" help - WiFi password (WPA or WPA2) for the example to use. - config ESP_WIFI_CHANNEL - int "WiFi Channel" - range 1 13 - default 1 - help - WiFi channel (network channel) for the example to use. + IP address that PPP server assigns to PPP client. - config ESP_MAX_STA_CONN - int "Maximal STA connections" - default 4 + config TEST_APP_UART_TX_PIN + int "TXD Pin Number" + default 6 + range 0 31 help - Max number of the STA connects to AP. + Pin number of UART TX. + + config TEST_APP_UART_RX_PIN + int "RXD Pin Number" + default 7 + range 0 31 + help + Pin number of UART RX. + + config TEST_APP_TCP_PORT + int "Port of test" + range 0 65535 + default 2222 + help + The remote port to which the client will connects to + once the PPP connection established + endmenu diff --git a/components/esp_modem/test/target/main/NetworkDCE.cpp b/components/esp_modem/test/target/main/NetworkDCE.cpp index a321644d3..d48d90e46 100644 --- a/components/esp_modem/test/target/main/NetworkDCE.cpp +++ b/components/esp_modem/test/target/main/NetworkDCE.cpp @@ -60,6 +60,8 @@ public: { // configure esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG(); + dte_config.uart_config.tx_io_num = CONFIG_TEST_APP_UART_TX_PIN; + dte_config.uart_config.rx_io_num = CONFIG_TEST_APP_UART_RX_PIN; esp_modem_dce_config dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG(""); // create DTE and minimal network DCE @@ -96,6 +98,7 @@ esp_err_t modem_init_network(esp_netif_t *netif) esp_err_t modem_start_network() { NetModule::start(); + return ESP_OK; } void modem_stop_network() diff --git a/components/esp_modem/test/target/pytest_pppd.py b/components/esp_modem/test/target/pytest_pppd.py new file mode 100644 index 000000000..707936a54 --- /dev/null +++ b/components/esp_modem/test/target/pytest_pppd.py @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +from __future__ import print_function, unicode_literals + +import subprocess +import time +from threading import Event, Thread + +import netifaces + + +def run_server(server_stop, port, server_ip, client_ip): + print('Starting PPP server on port: {}'.format(port)) + try: + arg_list = [ + 'sudo', 'pppd', port, '115200', + '{}:{}'.format(server_ip, client_ip), 'modem', 'local', 'noauth', + 'debug', 'nocrtscts', 'nodetach', '+ipv6' + ] + p = subprocess.Popen(arg_list, stdout=subprocess.PIPE, bufsize=1) + while not server_stop.is_set(): + if p.poll() is not None: + if p.poll() == 16: + print('[PPPD:] Terminated: hang-up received') + break + else: + raise ValueError( + 'ENV_TEST_FAILURE: PPP terminated unexpectedly with {}' + .format(p.poll())) + line = p.stdout.readline() + if line: + print('[PPPD:]{}'.format(line.rstrip())) + time.sleep(0.1) + except Exception as e: + print(e) + raise ValueError('ENV_TEST_FAILURE: Error running PPP server') + finally: + p.terminate() + print('PPP server stopped') + + +def test_examples_protocol_pppos_connect(dut): + """ + steps: + 1. starts PPP server + 2. get DUT as PPP client to connect to the server + 3. run unit test FW + """ + + # Look for test case symbolic names + try: + server_ip = dut.app.sdkconfig.get('TEST_APP_PPP_SERVER_IP') + client_ip = dut.app.sdkconfig.get('TEST_APP_PPP_CLIENT_IP') + except Exception: + print( + 'ENV_TEST_FAILURE: Some mandatory configuration not found in sdkconfig' + ) + raise + + # the PPP test env uses two ttyUSB's: one for ESP32 board, another one for ppp server + # use the other port for PPP server than the DUT/ESP + port = '/dev/ttyUSB0' if dut.serial.port == '/dev/ttyUSB1' else '/dev/ttyUSB1' + # Start the PPP server + server_stop = Event() + t = Thread(target=run_server, + args=(server_stop, port, server_ip, client_ip)) + t.start() + try: + ppp_server_timeout = time.time() + 30 + while 'ppp0' not in netifaces.interfaces(): + print( + "PPP server haven't yet setup its netif, list of active netifs:{}" + .format(netifaces.interfaces())) + time.sleep(0.5) + if time.time() > ppp_server_timeout: + raise ValueError( + 'ENV_TEST_FAILURE: PPP server failed to setup ppp0 interface within timeout' + ) + + dut.expect('All tests passed') + finally: + server_stop.set() + t.join() diff --git a/components/esp_modem/test/target/sdkconfig.defaults b/components/esp_modem/test/target/sdkconfig.defaults index aa6763982..4af92e67d 100644 --- a/components/esp_modem/test/target/sdkconfig.defaults +++ b/components/esp_modem/test/target/sdkconfig.defaults @@ -1,3 +1,4 @@ CONFIG_COMPILER_CXX_EXCEPTIONS=y CONFIG_CXX_EXCEPTIONS=y CONFIG_PPP_SUPPORT=y +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096