diff --git a/.github/workflows/modem__build-host-tests.yml b/.github/workflows/modem__build-host-tests.yml index a1c52011994..af8deaa3d66 100644 --- a/.github/workflows/modem__build-host-tests.yml +++ b/.github/workflows/modem__build-host-tests.yml @@ -109,7 +109,6 @@ jobs: - name: Build and Test shell: bash run: | - apt-get update apt-get update && apt-get install -y gcc-8 g++-8 python3-pip apt-get install -y rsync update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 800 --slave /usr/bin/g++ g++ /usr/bin/g++-8 @@ -129,9 +128,9 @@ jobs: cd $GITHUB_WORKSPACE/${{ env.COMP_DIR }} gcov-8 `find . -name "esp_modem*gcda" -printf '%h\n' | head -n 1`/* gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x esp_modem_coverage.xml - mkdir docs_gcovr - cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html docs_gcovr - cp -rf docs_gcovr $GITHUB_WORKSPACE + mkdir modem_coverage_report + cp $GITHUB_WORKSPACE/${{ env.COMP_DIR }}/index.html modem_coverage_report + cp -rf modem_coverage_report $GITHUB_WORKSPACE - name: Code Coverage Summary Report uses: irongut/CodeCoverageSummary@v1.3.0 with: @@ -151,13 +150,7 @@ jobs: uses: actions/upload-artifact@v3 if: always() with: - name: docs_gcovr + name: modem_coverage_report path: | - ${{ env.COMP_DIR }}/docs_gcovr + ${{ env.COMP_DIR }}/modem_coverage_report if-no-files-found: error - - name: Deploy code coverage results - if: github.ref == 'refs/heads/master' - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs_gcovr diff --git a/.github/workflows/publish-coverage-report.yml b/.github/workflows/publish-coverage-report.yml new file mode 100644 index 00000000000..f58ff4fc86c --- /dev/null +++ b/.github/workflows/publish-coverage-report.yml @@ -0,0 +1,42 @@ +name: Publish coverage report to Github Pages + +on: + workflow_run: + workflows: ["websocket: build/host-tests", "esp-modem: build/host-tests"] + types: + - completed + +jobs: + publish_github_pages: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Download Websocket Artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow: websocket__build-host-tests.yml + workflow_conclusion: success + name: websocket_example_coverage_report + path: websocket_example_coverage_report_artifact + - name: Download Modem Artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow: modem__build-host-tests.yml + workflow_conclusion: success + name: modem_coverage_report + path: modem_coverage_report_artifact + - name: Merge HTML files + run: | + echo "" > index.html + cat modem_coverage_report_artifact/index.html >> index.html + cat websocket_example_coverage_report_artifact/index.html >> index.html + echo "" >> index.html + mkdir coverage_report + mv index.html coverage_report + + - name: Deploy generated docs + uses: JamesIves/github-pages-deploy-action@4.1.5 + with: + branch: gh-pages + folder: coverage_report diff --git a/.github/workflows/run-host-tests.yml b/.github/workflows/run-host-tests.yml new file mode 100644 index 00000000000..d2211320cf7 --- /dev/null +++ b/.github/workflows/run-host-tests.yml @@ -0,0 +1,86 @@ +name: Run on host + +on: + workflow_call: + inputs: + idf_version: + required: true + type: string + app_name: + type: string + required: true + app_path: + type: string + required: true + component_path: + type: string + required: true + upload_artifacts: + type: boolean + required: true + +jobs: + build: + name: Build App + runs-on: ubuntu-20.04 + permissions: + contents: write + container: espressif/idf:${{inputs.idf_version}} + steps: + - name: Checkout esp-protocols + uses: actions/checkout@v3 + with: + path: esp-protocols + - name: Build ${{ inputs.app_name }} with IDF-${{ inputs.idf_version }} + shell: bash + run: | + . ${IDF_PATH}/export.sh + cd $GITHUB_WORKSPACE/${{inputs.app_path}} + rm -rf sdkconfig sdkconfig.defaults build + cp sdkconfig.ci.linux sdkconfig.defaults + idf.py build + ./build/${{inputs.app_name}}.elf + - name: Build with Coverage Enabled + shell: bash + run: | + . ${IDF_PATH}/export.sh + cd $GITHUB_WORKSPACE/${{inputs.app_path}} + rm -rf build sdkconfig sdkconfig.defaults + cp sdkconfig.ci.coverage sdkconfig.defaults + idf.py fullclean + idf.py build + ./build/${{inputs.app_name}}.elf + - name: Run Coverage + shell: bash + run: | + apt-get update && apt-get install -y gcc-8 g++-8 python3-pip rsync + python -m pip install gcovr + cd $GITHUB_WORKSPACE/${{inputs.component_path}} + gcov `find . -name "*gcda"` + gcovr --gcov-ignore-parse-errors -g -k -r . --html index.html -x ${{inputs.app_name}}_coverage.xml + mkdir ${{inputs.app_name}}_coverage_report + touch ${{inputs.app_name}}_coverage_report/.nojekyll + cp index.html ${{inputs.app_name}}_coverage_report + cp -rf ${{inputs.app_name}}_coverage_report ${{inputs.app_name}}_coverage.xml $GITHUB_WORKSPACE + - name: Code Coverage Summary Report + uses: irongut/CodeCoverageSummary@v1.3.0 + with: + filename: esp-protocols/**/${{inputs.app_name}}_coverage.xml + badge: true + fail_below_min: false + format: markdown + hide_branch_rate: false + hide_complexity: false + indicators: true + output: both + thresholds: '60 80' + - name: Write to Job Summary + run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY + - name: Upload files to artifacts for run-target job + uses: actions/upload-artifact@v3 + if: ${{inputs.upload_artifacts}} + with: + name: ${{inputs.app_name}}_coverage_report + path: | + ${{inputs.component_path}}/${{inputs.app_name}}_coverage_report + if-no-files-found: error diff --git a/.github/workflows/websocket__build-host-tests.yml b/.github/workflows/websocket__build-host-tests.yml new file mode 100644 index 00000000000..7a1e3bfc4ff --- /dev/null +++ b/.github/workflows/websocket__build-host-tests.yml @@ -0,0 +1,20 @@ +name: "websocket: build/host-tests" + +on: + push: +# branches: +# - master + pull_request: + types: [opened, synchronize, reopened, labeled] + + +jobs: + host_test_websocket: + if: contains(github.event.pull_request.labels.*.name, 'websocket') || github.event_name == 'push' + uses: "./.github/workflows/run-host-tests.yml" + with: + idf_version: "latest" + app_name: "websocket_example" + app_path: "esp-protocols/components/esp_websocket_client/examples" + component_path: "esp-protocols/components/esp_websocket_client" + upload_artifacts: true diff --git a/.github/workflows/websocket__build-target-test.yml b/.github/workflows/websocket__build-target-test.yml index b86c76a03d9..feb991941b2 100644 --- a/.github/workflows/websocket__build-target-test.yml +++ b/.github/workflows/websocket__build-target-test.yml @@ -76,6 +76,7 @@ jobs: unzip ci/artifacts.zip -d ci for dir in `ls -d ci/build_*`; do rm -rf build sdkconfig.defaults + cp sdkconfig.ci.esp32 sdkconfig.defaults mv $dir build python -m pytest --log-cli-level DEBUG --junit-xml=./results_${{ matrix.test.app }}_${{ matrix.idf_target }}_${{ matrix.idf_ver }}_${dir#"ci/build_"}.xml --target=${{ matrix.idf_target }} done diff --git a/components/esp_websocket_client/CMakeLists.txt b/components/esp_websocket_client/CMakeLists.txt index ba53db6fa69..36b0766d4c3 100644 --- a/components/esp_websocket_client/CMakeLists.txt +++ b/components/esp_websocket_client/CMakeLists.txt @@ -1,3 +1,5 @@ +idf_build_get_property(target IDF_TARGET) + if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION) message(STATUS "Websocket transport is disabled so the esp_websocket_client component will not be built") # note: the component is still included in the build so it can become visible again in config @@ -6,7 +8,14 @@ if(NOT CONFIG_WS_TRANSPORT AND NOT CMAKE_BUILD_EARLY_EXPANSION) return() endif() -idf_component_register(SRCS "esp_websocket_client.c" +if(${IDF_TARGET} STREQUAL "linux") + idf_component_register(SRCS "esp_websocket_client.c" + INCLUDE_DIRS "include" + REQUIRES esp-tls tcp_transport http_parser esp_event nvs_flash esp_stubs json + PRIV_REQUIRES esp_timer) +else() + idf_component_register(SRCS "esp_websocket_client.c" INCLUDE_DIRS "include" REQUIRES lwip esp-tls tcp_transport http_parser PRIV_REQUIRES esp_timer esp_event) +endif() diff --git a/components/esp_websocket_client/esp_websocket_client.c b/components/esp_websocket_client/esp_websocket_client.c index 46bf51d77e5..f13ec802d03 100644 --- a/components/esp_websocket_client/esp_websocket_client.c +++ b/components/esp_websocket_client/esp_websocket_client.c @@ -20,6 +20,12 @@ #include "esp_timer.h" #include "esp_tls_crypto.h" +#if defined(CONFIG_IDF_TARGET_LINUX) +#include +#include +#include "esp_system.h" +#endif + static const char *TAG = "websocket_client"; #define WEBSOCKET_TCP_DEFAULT_PORT (80) diff --git a/components/esp_websocket_client/examples/CMakeLists.txt b/components/esp_websocket_client/examples/CMakeLists.txt index 6394830fb64..71deec94992 100644 --- a/components/esp_websocket_client/examples/CMakeLists.txt +++ b/components/esp_websocket_client/examples/CMakeLists.txt @@ -2,9 +2,24 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_get_property(target IDF_TARGET) + set(EXTRA_COMPONENT_DIRS "..") # This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. list(APPEND EXTRA_COMPONENT_DIRS "../../../common_components/protocol_examples_common") -include($ENV{IDF_PATH}/tools/cmake/project.cmake) +if(${IDF_TARGET} STREQUAL "linux") + set(common_component_dir ../../../common_components) + set(EXTRA_COMPONENT_DIRS + ".." + "${common_component_dir}/linux_compat/esp_timer" + "${common_component_dir}/linux_compat" + "${common_component_dir}/linux_compat/freertos") + + list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/linux_stubs/esp_stubs) + set(COMPONENTS esp_websocket_client main) +endif() + project(websocket_example) diff --git a/components/esp_websocket_client/examples/main/CMakeLists.txt b/components/esp_websocket_client/examples/main/CMakeLists.txt index bff26f10886..e06513e5466 100644 --- a/components/esp_websocket_client/examples/main/CMakeLists.txt +++ b/components/esp_websocket_client/examples/main/CMakeLists.txt @@ -1,2 +1,13 @@ idf_component_register(SRCS "websocket_example.c" + REQUIRES esp_websocket_client protocol_examples_common INCLUDE_DIRS ".") + +if(CONFIG_GCOV_ENABLED) + target_compile_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage) + target_link_options(${COMPONENT_LIB} PUBLIC --coverage -fprofile-arcs -ftest-coverage) + + idf_component_get_property(esp_websocket_client esp_websocket_client COMPONENT_LIB) + + target_compile_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage) + target_link_options(${esp_websocket_client} PUBLIC --coverage -fprofile-arcs -ftest-coverage) +endif() diff --git a/components/esp_websocket_client/examples/main/Kconfig.projbuild b/components/esp_websocket_client/examples/main/Kconfig.projbuild index d146488e3cf..49ad49effdd 100644 --- a/components/esp_websocket_client/examples/main/Kconfig.projbuild +++ b/components/esp_websocket_client/examples/main/Kconfig.projbuild @@ -20,4 +20,11 @@ menu "Example Configuration" help URL of websocket endpoint this example connects to and sends echo + if CONFIG_IDF_TARGET = "linux" + config GCOV_ENABLED + bool "Coverage analyzer" + default n + help + Enables coverage analyzing for host tests. + endif endmenu diff --git a/components/esp_websocket_client/examples/main/websocket_example.c b/components/esp_websocket_client/examples/main/websocket_example.c index 4e44f46c974..7b02b0275f8 100644 --- a/components/esp_websocket_client/examples/main/websocket_example.c +++ b/components/esp_websocket_client/examples/main/websocket_example.c @@ -14,7 +14,8 @@ #include -#include "esp_wifi.h" +// #include "esp_wifi.h" +#include "esp_netif.h" #include "esp_system.h" #include "nvs_flash.h" #include "esp_event.h" @@ -34,8 +35,8 @@ static const char *TAG = "websocket"; -static TimerHandle_t shutdown_signal_timer; -static SemaphoreHandle_t shutdown_sema; +//static TimerHandle_t shutdown_signal_timer; +//static SemaphoreHandle_t shutdown_sema; static void log_error_if_nonzero(const char *message, int error_code) { @@ -44,11 +45,11 @@ static void log_error_if_nonzero(const char *message, int error_code) } } -static void shutdown_signaler(TimerHandle_t xTimer) -{ - ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC); - xSemaphoreGive(shutdown_sema); -} +//static void shutdown_signaler(TimerHandle_t xTimer) +//{ +// ESP_LOGI(TAG, "No data received for %d seconds, signaling shutdown", NO_DATA_TIMEOUT_SEC); +// xSemaphoreGive(shutdown_sema); +//} #if CONFIG_WEBSOCKET_URI_FROM_STDIN static void get_string(char *line, size_t size) @@ -108,7 +109,7 @@ static void websocket_event_handler(void *handler_args, esp_event_base_t base, i ESP_LOGW(TAG, "Total payload length=%d, data_len=%d, current payload offset=%d\r\n", data->payload_len, data->data_len, data->payload_offset); - xTimerReset(shutdown_signal_timer, portMAX_DELAY); + // xTimerReset(shutdown_signal_timer, portMAX_DELAY); break; case WEBSOCKET_EVENT_ERROR: ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR"); @@ -126,9 +127,9 @@ static void websocket_app_start(void) { esp_websocket_client_config_t websocket_cfg = {}; - shutdown_signal_timer = xTimerCreate("Websocket shutdown timer", NO_DATA_TIMEOUT_SEC * 1000 / portTICK_PERIOD_MS, - pdFALSE, NULL, shutdown_signaler); - shutdown_sema = xSemaphoreCreateBinary(); + //shutdown_signal_timer = xTimerCreate("Websocket shutdown timer", NO_DATA_TIMEOUT_SEC * 1000 / portTICK_PERIOD_MS, + // pdFALSE, NULL, shutdown_signaler); + //shutdown_sema = xSemaphoreCreateBinary(); #if CONFIG_WEBSOCKET_URI_FROM_STDIN char line[128]; @@ -149,7 +150,7 @@ static void websocket_app_start(void) esp_websocket_register_events(client, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)client); esp_websocket_client_start(client); - xTimerStart(shutdown_signal_timer, portMAX_DELAY); + // xTimerStart(shutdown_signal_timer, portMAX_DELAY); char data[32]; int i = 0; while (i < 5) { @@ -161,13 +162,13 @@ static void websocket_app_start(void) vTaskDelay(1000 / portTICK_PERIOD_MS); } - xSemaphoreTake(shutdown_sema, portMAX_DELAY); - esp_websocket_client_close(client, portMAX_DELAY); + // xSemaphoreTake(shutdown_sema, portMAX_DELAY); + // esp_websocket_client_close(client, portMAX_DELAY); ESP_LOGI(TAG, "Websocket Stopped"); esp_websocket_client_destroy(client); } -void app_main(void) +int main(void) { ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size()); @@ -188,4 +189,5 @@ void app_main(void) ESP_ERROR_CHECK(example_connect()); websocket_app_start(); + return 0; } diff --git a/components/esp_websocket_client/examples/sdkconfig.ci.coverage b/components/esp_websocket_client/examples/sdkconfig.ci.coverage new file mode 100644 index 00000000000..7c35acfd21c --- /dev/null +++ b/components/esp_websocket_client/examples/sdkconfig.ci.coverage @@ -0,0 +1,8 @@ +CONFIG_GCOV_ENABLED=y +CONFIG_IDF_TARGET="linux" +CONFIG_IDF_TARGET_LINUX=y +CONFIG_ESP_EVENT_POST_FROM_ISR=n +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n +CONFIG_WEBSOCKET_URI="ws://echo.websocket.events" +CONFIG_WEBSOCKET_URI_FROM_STRING=y +CONFIG_WEBSOCKET_URI_FROM_STDIN=n diff --git a/components/esp_websocket_client/examples/sdkconfig.ci b/components/esp_websocket_client/examples/sdkconfig.ci.esp32 similarity index 87% rename from components/esp_websocket_client/examples/sdkconfig.ci rename to components/esp_websocket_client/examples/sdkconfig.ci.esp32 index 9c2dea374e3..d1654398349 100644 --- a/components/esp_websocket_client/examples/sdkconfig.ci +++ b/components/esp_websocket_client/examples/sdkconfig.ci.esp32 @@ -1,3 +1,5 @@ +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_TARGET_LINUX=n CONFIG_WEBSOCKET_URI_FROM_STDIN=y CONFIG_WEBSOCKET_URI_FROM_STRING=n CONFIG_EXAMPLE_CONNECT_ETHERNET=y diff --git a/components/esp_websocket_client/examples/sdkconfig.ci.linux b/components/esp_websocket_client/examples/sdkconfig.ci.linux new file mode 100644 index 00000000000..dfb14cb63c1 --- /dev/null +++ b/components/esp_websocket_client/examples/sdkconfig.ci.linux @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_IDF_TARGET_LINUX=y +CONFIG_ESP_EVENT_POST_FROM_ISR=n +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=n +CONFIG_WEBSOCKET_URI="ws://echo.websocket.events" +CONFIG_WEBSOCKET_URI_FROM_STRING=y +CONFIG_WEBSOCKET_URI_FROM_STDIN=n +CONFIG_EXAMPLE_CONNECT_WIFI=n