Merge branch 'feature/esp_timer_mock' into 'master'

[esp_timer]: created mock override component

See merge request espressif/esp-idf!14526
This commit is contained in:
Jakob Hasse
2021-08-13 11:01:53 +00:00
11 changed files with 291 additions and 84 deletions

View File

@@ -356,6 +356,13 @@ test_esp_event:
- idf.py build
- build/test_esp_event_host.elf
test_esp_timer_cxx:
extends: .host_test_template
script:
- cd ${IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/host_test/esp_timer
- idf.py build
- build/test_esp_timer_cxx_host.elf
test_eh_frame_parser:
extends: .host_test_template
script:

View File

@@ -1,9 +1,16 @@
idf_component_register(SRCS
"esp_exception.cpp"
"i2c_cxx.cpp"
"gpio_cxx.cpp"
"esp_event_api.cpp"
"esp_event_cxx.cpp"
"esp_timer_cxx.cpp"
idf_build_get_property(target IDF_TARGET)
set(srcs "esp_timer_cxx.cpp" "esp_exception.cpp" "gpio_cxx.cpp")
set(requires "esp_timer" "driver")
if(NOT ${target} STREQUAL "linux")
list(APPEND srcs
"i2c_cxx.cpp"
"esp_event_api.cpp"
"esp_event_cxx.cpp")
list(APPEND requires "esp_event")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include"
REQUIRES driver esp_event esp_timer)
REQUIRES ${requires})

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/esp_timer/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/driver/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/")
project(test_esp_timer_cxx_host)

View File

@@ -0,0 +1,36 @@
| Supported Targets | Linux |
| ----------------- | ----- |
# C++ ESPTimer test on Linux target
This unit test tests basic functionality of the `ESPTimer` class. The test does not use mocks. Instead, it runs the whole implementation of the component on the Linux host. The test framework is CATCH.
## Requirements
* A Linux system
* The usual IDF requirements for Linux system, as described in the [Getting Started Guides](../../../../docs/en/get-started/index.rst).
* The host's gcc/g++
This application has been tested on Ubuntu 20.04 with `gcc` version *9.3.0*.
## Build
First, make sure that the target is set to Linux. Run `idf.py --preview set-target linux` if you are not sure. Then do a normal IDF build: `idf.py build`.
## Run
IDF monitor doesn't work yet for Linux. You have to run the app manually:
```bash
build/test_esp_timer_cxx_host.elf
```
## Example Output
Ideally, all tests pass, which is indicated by "All tests passed" in the last line:
```bash
$ build/test_esp_timer_cxx_host.elf
===============================================================================
All tests passed (9 assertions in 11 test cases)
```

View File

@@ -0,0 +1,5 @@
idf_component_register(SRCS "esp_timer_test.cpp"
INCLUDE_DIRS
"."
$ENV{IDF_PATH}/tools/catch
REQUIRES cmock esp_timer experimental_cpp_component)

View File

@@ -0,0 +1,196 @@
/* ESP Timer C++ unit tests
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#define CATCH_CONFIG_MAIN
#include <stdio.h>
#include <stdexcept>
#include "esp_err.h"
#include "esp_timer_cxx.hpp"
#include "catch.hpp"
extern "C" {
#include "Mockesp_timer.h"
}
// TODO: IDF-2693, function definition just to satisfy linker, mock esp_common instead
const char *esp_err_to_name(esp_err_t code) {
return "test";
}
using namespace std;
using namespace idf;
using namespace idf::esp_timer;
struct FixtureException : std::exception {
const char *what() const noexcept override {
return "CMock failed";
}
};
struct TimerCreationFixture {
TimerCreationFixture(bool expect_stop = false) : out_handle(reinterpret_cast<esp_timer_handle_t>(1))
{
if (!TEST_PROTECT()) {
throw FixtureException();
}
esp_timer_create_ExpectAnyArgsAndReturn(ESP_OK);
esp_timer_create_ReturnThruPtr_out_handle(&out_handle);
if (expect_stop) {
esp_timer_stop_ExpectAndReturn(out_handle, ESP_OK); // implementation may always call stop
} else {
esp_timer_stop_IgnoreAndReturn(ESP_OK); // implementation may always call stop
}
esp_timer_delete_ExpectAndReturn(out_handle, ESP_OK);
}
virtual ~TimerCreationFixture()
{
Mockesp_timer_Verify();
}
esp_timer_handle_t out_handle;
};
static void (*trigger_timer_callback)(void *data) = nullptr;
esp_err_t cmock_timer_create_callback(const esp_timer_create_args_t* create_args, esp_timer_handle_t* out_handle, int cmock_num_calls)
{
trigger_timer_callback = create_args->callback;
return ESP_OK;
}
struct TimerCallbackFixture : public TimerCreationFixture {
TimerCallbackFixture(bool expect_stop = false) : TimerCreationFixture(expect_stop)
{
esp_timer_create_AddCallback(cmock_timer_create_callback);
}
~TimerCallbackFixture()
{
trigger_timer_callback = nullptr;
}
};
TEST_CASE("get_time works")
{
esp_timer_get_time_ExpectAndReturn(static_cast<uint64_t>(0xfeeddeadbeef));
CHECK(get_time() == std::chrono::microseconds(0xfeeddeadbeef));
}
TEST_CASE("get_next_alarm works")
{
esp_timer_get_next_alarm_ExpectAndReturn(static_cast<uint64_t>(47u));
CHECK(get_next_alarm() == std::chrono::microseconds(47u));
}
TEST_CASE("ESPTimer null function")
{
CHECK_THROWS_AS(ESPTimer(nullptr), ESPException&);
}
TEST_CASE("ESPTimer empty std::function")
{
function<void()> nothing;
CHECK_THROWS_AS(ESPTimer(nothing, "test"), ESPException&);
}
TEST_CASE("ESPTimer initializes and deletes itself")
{
TimerCreationFixture fix;
function<void()> timer_cb = [&]() { };
ESPTimer(timer_cb, "test");
}
TEST_CASE("ESPTimer start throws on invalid state failure")
{
TimerCreationFixture fix;
esp_timer_start_once_ExpectAndReturn(fix.out_handle, 5000, ESP_ERR_INVALID_STATE);
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
CHECK_THROWS_AS(timer.start(chrono::microseconds(5000)), ESPException&);
}
TEST_CASE("ESPTimer start periodically throws on invalid state failure")
{
TimerCreationFixture fix;
esp_timer_start_periodic_ExpectAndReturn(fix.out_handle, 5000, ESP_ERR_INVALID_STATE);
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
CHECK_THROWS_AS(timer.start_periodic(chrono::microseconds(5000)), ESPException&);
}
TEST_CASE("ESPTimer stopp throws on invaid state failure")
{
TimerCreationFixture fix;
// Overriding stop part of the fixture
esp_timer_stop_StopIgnore();
esp_timer_stop_IgnoreAndReturn(ESP_ERR_INVALID_STATE);
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
CHECK_THROWS_AS(timer.stop(), ESPException&);
}
TEST_CASE("ESPTimer stops in destructor")
{
TimerCreationFixture fix(true);
esp_timer_start_once_ExpectAndReturn(fix.out_handle, 5000, ESP_OK);
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
timer.start(chrono::microseconds(5000));
}
TEST_CASE("ESPTimer stops correctly")
{
TimerCreationFixture fix(true);
esp_timer_start_once_ExpectAndReturn(fix.out_handle, 5000, ESP_OK);
// Additional stop needed because stop is called in ESPTimer::stop and ~ESPTimer.
esp_timer_stop_ExpectAndReturn(fix.out_handle, ESP_OK);
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
timer.start(chrono::microseconds(5000));
timer.stop();
}
TEST_CASE("ESPTimer callback works")
{
TimerCallbackFixture fix;
int flag = 0;
function<void()> timer_cb = [&]() { flag = 47; };
ESPTimer timer(timer_cb);
trigger_timer_callback(&timer);
REQUIRE(trigger_timer_callback != nullptr);
CHECK(flag == 47);
}

View File

@@ -0,0 +1,3 @@
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
CONFIG_IDF_TARGET="linux"
CONFIG_CXX_EXCEPTIONS=y

View File

@@ -18,6 +18,7 @@
#include <chrono>
#include <functional>
#include <string>
#include "esp_exception.hpp"
#include "esp_timer.h"

View File

@@ -44,82 +44,6 @@ struct RefClock {
}
};
TEST_CASE("ESPTimer null function", "[ESPTimer]")
{
TEST_THROW(ESPTimer(nullptr), ESPException);
}
TEST_CASE("ESPTimer empty std::function", "[ESPTimer]")
{
function<void()> nothing;
TEST_THROW(ESPTimer(nothing, "test"), ESPException);
}
TEST_CASE("ESPTimer starting twice throws", "[ESPTimer]")
{
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
timer.start(chrono::microseconds(5000));
TEST_THROW(timer.start(chrono::microseconds(5000)), ESPException);
}
TEST_CASE("ESPTimer periodically starting twice throws", "[ESPTimer]")
{
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
timer.start_periodic(chrono::microseconds(5000));
TEST_THROW(timer.start_periodic(chrono::microseconds(5000)), ESPException);
}
TEST_CASE("ESPTimer stopping non-started timer throws", "[ESPTimer]")
{
function<void()> timer_cb = [&]() { };
ESPTimer timer(timer_cb);
TEST_THROW(timer.stop(), ESPException);
}
TEST_CASE("ESPTimer calls callback", "[ESPTimer]")
{
bool called = false;
function<void()> timer_cb = [&]() {
called = true;
};
ESPTimer timer(timer_cb);
timer.start(chrono::microseconds(5000));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ASSERT(called);
}
TEST_CASE("ESPTimer periodically calls callback", "[ESPTimer]")
{
size_t called = 0;
function<void()> timer_cb = [&]() {
called++;
};
ESPTimer timer(timer_cb);
timer.start_periodic(chrono::microseconds(2000));
vTaskDelay(10 / portTICK_PERIOD_MS);
TEST_ASSERT(called >= 4u);
}
TEST_CASE("ESPTimer produces correct delay", "[ESPTimer]")
{
int64_t t_end;

View File

@@ -0,0 +1,9 @@
# NOTE: This kind of mocking currently works on Linux targets only.
# On Espressif chips, too many dependencies are missing at the moment.
message(STATUS "building ESP TIMER MOCKS")
idf_component_get_property(original_esp_timer_dir esp_timer COMPONENT_OVERRIDEN_DIR)
idf_component_mock(INCLUDE_DIRS "${original_esp_timer_dir}/include"
REQUIRES esp_common
MOCK_HEADER_FILES ${original_esp_timer_dir}/include/esp_timer.h)

View File

@@ -0,0 +1,9 @@
:cmock:
:plugins:
- expect
- expect_any_args
- return_thru_ptr
- array
- ignore
- ignore_arg
- callback