mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
[cxx]: simple spi master class
* spi cxx unit test (CATCH-based, on host) * added portmacro.h to driver mocking * added simple testing app to write/read SPI, using an MPU9250
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
// Copyright 2015-2021 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "gpio_cxx.hpp"
|
||||
#include "driver/spi_master.h"
|
||||
#include "spi_cxx.hpp"
|
||||
extern "C" {
|
||||
#include "Mockgpio.h"
|
||||
#include "Mockspi_master.h"
|
||||
#include "Mockspi_common.h"
|
||||
}
|
||||
|
||||
static const idf::GPIONum VALID_GPIO(18);
|
||||
@@ -45,7 +47,7 @@ public:
|
||||
* Helper macro for setting up a test protect call for CMock.
|
||||
*
|
||||
* This macro should be used at the beginning of any test cases
|
||||
* which use generated CMock mock functions.
|
||||
* that uses generated CMock mock functions.
|
||||
* This is necessary because CMock uses longjmp which screws up C++ stacks and
|
||||
* also the CATCH mechanisms.
|
||||
*
|
||||
@@ -61,18 +63,236 @@ public:
|
||||
} \
|
||||
while (0)
|
||||
|
||||
struct GPIOFixture {
|
||||
GPIOFixture(idf::GPIONum gpio_num = idf::GPIONum(18), gpio_mode_t mode = GPIO_MODE_OUTPUT) : num(gpio_num)
|
||||
struct CMockFixture {
|
||||
CMockFixture()
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), ESP_OK); gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), mode, ESP_OK);
|
||||
}
|
||||
|
||||
~GPIOFixture()
|
||||
~CMockFixture()
|
||||
{
|
||||
// Verify that all expected methods have been called.
|
||||
Mockgpio_Verify();
|
||||
Mockspi_master_Verify();
|
||||
Mockspi_common_Verify();
|
||||
}
|
||||
};
|
||||
|
||||
struct GPIOFixture : public CMockFixture {
|
||||
GPIOFixture(idf::GPIONum gpio_num = idf::GPIONum(18), gpio_mode_t mode = GPIO_MODE_OUTPUT)
|
||||
: CMockFixture(), num(gpio_num)
|
||||
{
|
||||
gpio_reset_pin_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), ESP_OK);
|
||||
gpio_set_direction_ExpectAndReturn(static_cast<gpio_num_t>(num.get_num()), mode, ESP_OK);
|
||||
}
|
||||
|
||||
idf::GPIONum num;
|
||||
};
|
||||
|
||||
struct SPIFix;
|
||||
struct SPIDevFix;
|
||||
struct SPITransactionDescriptorFix;
|
||||
struct SPITransactionTimeoutFix;
|
||||
struct SPITransactionFix;
|
||||
|
||||
static SPIFix *g_fixture;
|
||||
static SPIDevFix *g_dev_fixture;
|
||||
static SPITransactionDescriptorFix *g_trans_desc_fixture;
|
||||
static SPITransactionTimeoutFix *g_trans_timeout_fixture;
|
||||
static SPITransactionFix *g_trans_fixture;
|
||||
|
||||
struct SPIFix : public CMockFixture {
|
||||
SPIFix(spi_host_device_t host_id = spi_host_device_t(1),
|
||||
uint32_t mosi = 1,
|
||||
uint32_t miso = 2,
|
||||
uint32_t sclk = 3) : CMockFixture(), bus_config() {
|
||||
bus_config.mosi_io_num = mosi;
|
||||
bus_config.miso_io_num = miso;
|
||||
bus_config.sclk_io_num = sclk;
|
||||
bus_config.quadwp_io_num = -1;
|
||||
bus_config.quadhd_io_num = -1;
|
||||
|
||||
spi_bus_initialize_ExpectWithArrayAndReturn(host_id, &bus_config, 1, spi_common_dma_t::SPI_DMA_CH_AUTO, ESP_OK);
|
||||
spi_bus_free_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
|
||||
g_fixture = this;
|
||||
}
|
||||
|
||||
~SPIFix() {
|
||||
g_fixture = nullptr;
|
||||
}
|
||||
|
||||
spi_bus_config_t bus_config;
|
||||
};
|
||||
|
||||
struct QSPIFix : public SPIFix {
|
||||
QSPIFix(spi_host_device_t host_id = spi_host_device_t(1),
|
||||
uint32_t mosi = 1,
|
||||
uint32_t miso = 2,
|
||||
uint32_t sclk = 3,
|
||||
uint32_t wp = 4,
|
||||
uint32_t hd = 5) : SPIFix(host_id, mosi, miso, sclk)
|
||||
{
|
||||
bus_config.quadwp_io_num = wp;
|
||||
bus_config.quadhd_io_num = hd;
|
||||
}
|
||||
};
|
||||
|
||||
enum class CreateAnd {
|
||||
FAIL,
|
||||
SUCCEED,
|
||||
IGNORE
|
||||
};
|
||||
|
||||
struct SPIDevFix {
|
||||
SPIDevFix(CreateAnd flags)
|
||||
: dev_handle(reinterpret_cast<spi_device_handle_t>(47)),
|
||||
dev_config()
|
||||
{
|
||||
dev_config.spics_io_num = 4;
|
||||
if (flags == CreateAnd::FAIL) {
|
||||
spi_bus_add_device_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
} else if (flags == CreateAnd::IGNORE) {
|
||||
spi_bus_add_device_IgnoreAndReturn(ESP_OK);
|
||||
spi_bus_remove_device_IgnoreAndReturn(ESP_OK);
|
||||
} else {
|
||||
spi_bus_add_device_AddCallback(add_dev_cb);
|
||||
spi_bus_add_device_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
spi_bus_remove_device_ExpectAndReturn(dev_handle, ESP_OK);
|
||||
}
|
||||
|
||||
g_dev_fixture = this;
|
||||
}
|
||||
|
||||
~SPIDevFix()
|
||||
{
|
||||
spi_bus_add_device_AddCallback(nullptr);
|
||||
g_dev_fixture = nullptr;
|
||||
}
|
||||
|
||||
spi_device_handle_t dev_handle;
|
||||
spi_device_interface_config_t dev_config;
|
||||
|
||||
static esp_err_t add_dev_cb(spi_host_device_t host_id,
|
||||
const spi_device_interface_config_t* dev_config,
|
||||
spi_device_handle_t* handle,
|
||||
int cmock_num_calls)
|
||||
{
|
||||
SPIDevFix *fix = static_cast<SPIDevFix*>(g_dev_fixture);
|
||||
*handle = fix->dev_handle;
|
||||
fix->dev_config = *dev_config;
|
||||
return ESP_OK;
|
||||
}
|
||||
};
|
||||
|
||||
struct SPITransactionFix {
|
||||
SPITransactionFix(esp_err_t get_trans_return = ESP_OK) : get_transaction_return(get_trans_return)
|
||||
{
|
||||
spi_device_queue_trans_AddCallback(queue_trans_cb);
|
||||
spi_device_get_trans_result_AddCallback(get_trans_result_cb);
|
||||
|
||||
spi_device_queue_trans_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
spi_device_get_trans_result_ExpectAnyArgsAndReturn(get_trans_return);
|
||||
|
||||
g_trans_fixture = this;
|
||||
}
|
||||
|
||||
~SPITransactionFix()
|
||||
{
|
||||
spi_device_get_trans_result_AddCallback(nullptr);
|
||||
spi_device_queue_trans_AddCallback(nullptr);
|
||||
g_trans_fixture = nullptr;
|
||||
}
|
||||
|
||||
static esp_err_t queue_trans_cb(spi_device_handle_t handle,
|
||||
spi_transaction_t* trans_desc,
|
||||
TickType_t ticks_to_wait,
|
||||
int cmock_num_calls)
|
||||
{
|
||||
SPITransactionFix *fix = static_cast<SPITransactionFix*> (g_trans_fixture);
|
||||
fix->orig_trans = trans_desc;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t get_trans_result_cb(spi_device_handle_t handle,
|
||||
spi_transaction_t** trans_desc,
|
||||
TickType_t ticks_to_wait,
|
||||
int cmock_num_calls)
|
||||
{
|
||||
SPITransactionFix *fix = static_cast<SPITransactionFix*> (g_trans_fixture);
|
||||
|
||||
*trans_desc = fix->orig_trans;
|
||||
|
||||
return fix->get_transaction_return;
|
||||
}
|
||||
|
||||
esp_err_t get_transaction_return;
|
||||
spi_transaction_t *orig_trans;
|
||||
};
|
||||
|
||||
struct SPITransactionDescriptorFix {
|
||||
SPITransactionDescriptorFix(size_t size = 1, bool ignore_handle = false, TickType_t wait_time = portMAX_DELAY)
|
||||
: size(size), handle(reinterpret_cast<spi_device_handle_t>(0x01020304))
|
||||
{
|
||||
spi_device_queue_trans_AddCallback(queue_trans_cb);
|
||||
spi_device_get_trans_result_AddCallback(get_trans_result_cb);
|
||||
|
||||
spi_device_acquire_bus_ExpectAndReturn(handle, portMAX_DELAY, ESP_OK);
|
||||
if (ignore_handle) {
|
||||
spi_device_acquire_bus_IgnoreArg_device();
|
||||
}
|
||||
spi_device_queue_trans_ExpectAndReturn(handle, nullptr, 0, ESP_OK);
|
||||
spi_device_queue_trans_IgnoreArg_trans_desc();
|
||||
if (ignore_handle) {
|
||||
spi_device_queue_trans_IgnoreArg_handle();
|
||||
}
|
||||
|
||||
spi_device_get_trans_result_ExpectAndReturn(handle, nullptr, wait_time, ESP_OK);
|
||||
spi_device_get_trans_result_IgnoreArg_trans_desc();
|
||||
if (ignore_handle) {
|
||||
spi_device_get_trans_result_IgnoreArg_handle();
|
||||
}
|
||||
spi_device_release_bus_ExpectAnyArgs();
|
||||
|
||||
g_trans_desc_fixture = this;
|
||||
}
|
||||
|
||||
~SPITransactionDescriptorFix()
|
||||
{
|
||||
spi_device_get_trans_result_AddCallback(nullptr);
|
||||
spi_device_queue_trans_AddCallback(nullptr);
|
||||
g_trans_desc_fixture = nullptr;
|
||||
}
|
||||
|
||||
static esp_err_t queue_trans_cb(spi_device_handle_t handle,
|
||||
spi_transaction_t* trans_desc,
|
||||
TickType_t ticks_to_wait,
|
||||
int cmock_num_calls)
|
||||
{
|
||||
SPITransactionDescriptorFix *fix = static_cast<SPITransactionDescriptorFix*> (g_trans_desc_fixture);
|
||||
fix->orig_trans = trans_desc;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t get_trans_result_cb(spi_device_handle_t handle,
|
||||
spi_transaction_t** trans_desc,
|
||||
TickType_t ticks_to_wait,
|
||||
int cmock_num_calls)
|
||||
{
|
||||
SPITransactionDescriptorFix *fix = static_cast<SPITransactionDescriptorFix*> (g_trans_desc_fixture);
|
||||
|
||||
for (int i = 0; i < fix->size; i++) {
|
||||
static_cast<uint8_t*>(fix->orig_trans->rx_buffer)[i] = fix->rx_data[i];
|
||||
}
|
||||
|
||||
*trans_desc = fix->orig_trans;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
spi_transaction_t *orig_trans;
|
||||
spi_device_handle_t handle;
|
||||
std::vector<uint8_t> tx_data;
|
||||
std::vector<uint8_t> rx_data;
|
||||
};
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
#include "unity.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "gpio_cxx.hpp"
|
||||
#include "test_fixtures.hpp"
|
||||
|
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
|
||||
idf_build_set_property(COMPILE_DEFINITIONS "-DNO_DEBUG_STORAGE" APPEND)
|
||||
|
||||
# Overriding components which should be mocked
|
||||
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}/tools/mocks/esp_timer/")
|
||||
|
||||
# Including experimental component here because it's outside IDF's main component directory
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/")
|
||||
|
||||
project(test_spi_cxx_host)
|
@@ -0,0 +1,8 @@
|
||||
| Supported Targets | Linux |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Build
|
||||
`idf.py build` (sdkconfig.defaults sets the linux target by default)
|
||||
|
||||
# Run
|
||||
`build/host_spi_cxx_test.elf`
|
@@ -0,0 +1,9 @@
|
||||
idf_component_get_property(cpp_component experimental_cpp_component COMPONENT_DIR)
|
||||
|
||||
idf_component_register(SRCS "spi_cxx_test.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"${cpp_component}/host_test/fixtures"
|
||||
"${cpp_component}/private_include"
|
||||
$ENV{IDF_PATH}/tools/catch
|
||||
REQUIRES cmock driver experimental_cpp_component)
|
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0
|
||||
*
|
||||
* This test 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 "unity.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "spi_host_cxx.hpp"
|
||||
#include "spi_host_private_cxx.hpp"
|
||||
#include "system_cxx.hpp"
|
||||
#include "test_fixtures.hpp"
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
// TODO: IDF-2693, function definition just to satisfy linker, mock esp_common instead
|
||||
const char *esp_err_to_name(esp_err_t code) {
|
||||
return "host_test error";
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
using namespace idf;
|
||||
|
||||
TEST_CASE("SPITransferSize basic construction")
|
||||
{
|
||||
SPITransferSize transfer_size_0(0);
|
||||
CHECK(0 == transfer_size_0.get_value());
|
||||
SPITransferSize transfer_size_1(47);
|
||||
CHECK(47 == transfer_size_1.get_value());
|
||||
SPITransferSize transfer_size_default = SPITransferSize::default_size();
|
||||
CHECK(0 == transfer_size_default.get_value());
|
||||
}
|
||||
|
||||
TEST_CASE("SPI gpio numbers work correctly")
|
||||
{
|
||||
GPIONum gpio_num_0(19);
|
||||
MOSI mosi_0(18);
|
||||
MOSI mosi_1(gpio_num_0.get_num());
|
||||
MOSI mosi_2(mosi_0);
|
||||
CHECK(mosi_0 != mosi_1);
|
||||
CHECK(mosi_2 == mosi_0);
|
||||
CHECK(mosi_2.get_num() == 18u);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI_DMAConfig valid")
|
||||
{
|
||||
CHECK(SPI_DMAConfig::AUTO().get_num() == spi_common_dma_t::SPI_DMA_CH_AUTO);
|
||||
CHECK(SPI_DMAConfig::DISABLED().get_num() == spi_common_dma_t::SPI_DMA_DISABLED);
|
||||
}
|
||||
|
||||
TEST_CASE("SPINum invalid argument")
|
||||
{
|
||||
CHECK_THROWS_AS(SPINum(-1), SPIException&);
|
||||
uint32_t host_raw = spi_host_device_t::SPI_HOST_MAX;
|
||||
CHECK_THROWS_AS(SPINum host(host_raw), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("Master init failure")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
spi_bus_initialize_ExpectAnyArgsAndReturn(ESP_FAIL);
|
||||
|
||||
CHECK_THROWS_AS(SPIMaster master(SPINum(SPI2_HOST), MOSI(1), MISO(2), SCLK(3)), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("Master invalid state")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
spi_bus_initialize_ExpectAnyArgsAndReturn(ESP_ERR_INVALID_STATE);
|
||||
|
||||
CHECK_THROWS_AS(SPIMaster master(SPINum(SPI2_HOST), MOSI(1), MISO(2), SCLK(3)), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("build master")
|
||||
{
|
||||
SPIFix fix;
|
||||
|
||||
SPIMaster master(SPINum(SPI2_HOST),
|
||||
MOSI(fix.bus_config.mosi_io_num),
|
||||
MISO(fix.bus_config.miso_io_num),
|
||||
SCLK(fix.bus_config.sclk_io_num));
|
||||
}
|
||||
|
||||
TEST_CASE("build QSPI master")
|
||||
{
|
||||
QSPIFix fix;
|
||||
|
||||
SPIMaster master(SPINum(SPI2_HOST),
|
||||
MOSI(fix.bus_config.mosi_io_num),
|
||||
MISO(fix.bus_config.miso_io_num),
|
||||
SCLK(fix.bus_config.sclk_io_num),
|
||||
QSPIWP(fix.bus_config.quadwp_io_num),
|
||||
QSPIHD(fix.bus_config.quadhd_io_num));
|
||||
}
|
||||
|
||||
TEST_CASE("Master build device")
|
||||
{
|
||||
SPIFix fix;
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
|
||||
SPIMaster master(SPINum(SPI2_HOST),
|
||||
MOSI(fix.bus_config.mosi_io_num),
|
||||
MISO(fix.bus_config.miso_io_num),
|
||||
SCLK(fix.bus_config.sclk_io_num));
|
||||
|
||||
master.create_dev(CS(4), Frequency::MHz(1));
|
||||
}
|
||||
|
||||
TEST_CASE("SPIDeviceHandle throws on driver error")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::FAIL);
|
||||
CHECK_THROWS_AS(SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10)), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPIDeviceHandle succeed")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
}
|
||||
|
||||
TEST_CASE("SPIDevice succeed")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction empty data throws")
|
||||
{
|
||||
CHECK_THROWS_AS(SPITransactionDescriptor transaction({}, reinterpret_cast<SPIDeviceHandle*>(4747)), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction device handle nullptr throws")
|
||||
{
|
||||
CHECK_THROWS_AS(SPITransactionDescriptor transaction({47}, nullptr), SPIException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction not started wait_for")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47}, &handle);
|
||||
|
||||
CHECK_THROWS_AS(transaction.wait_for(std::chrono::milliseconds(47)), SPITransferException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction not started wait")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47}, &handle);
|
||||
|
||||
CHECK_THROWS_AS(transaction.wait(), SPITransferException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction not started get")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47}, &handle);
|
||||
|
||||
CHECK_THROWS_AS(transaction.get(), SPITransferException&);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction wait_for timeout")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionFix transaction_fix(ESP_ERR_TIMEOUT);
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
spi_device_acquire_bus_IgnoreAndReturn(ESP_OK);
|
||||
spi_device_release_bus_Ignore();
|
||||
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47}, &handle);
|
||||
transaction.start();
|
||||
|
||||
CHECK(transaction.wait_for(std::chrono::milliseconds(47)) == false);
|
||||
|
||||
// We need to finish the transaction, otherwise it goes out of scope without finishing and cleaning up the
|
||||
// allocated transaction descriptor.
|
||||
transaction_fix.get_transaction_return = ESP_OK;
|
||||
spi_device_get_trans_result_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
transaction.wait();
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction one byte")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix fix(1, true);
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
fix.rx_data = {0xA6};
|
||||
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47}, &handle);
|
||||
|
||||
transaction.start();
|
||||
auto out_data = transaction.get();
|
||||
|
||||
CHECK(1 * 8 == fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) fix.orig_trans->tx_buffer)[0]);
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction two byte")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix fix(2, true);
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
fix.rx_data = {0xA6, 0xA7};
|
||||
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
SPITransactionDescriptor transaction({47, 48}, &handle);
|
||||
transaction.start();
|
||||
auto out_data = transaction.get();
|
||||
|
||||
CHECK(fix.size * 8 == fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) fix.orig_trans->tx_buffer)[0]);
|
||||
CHECK(48 == ((uint8_t*) fix.orig_trans->tx_buffer)[1]);
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
REQUIRE(out_data.size() == 2);
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
CHECK(0xA7 == out_data[1]);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction future")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
auto result = dev.transfer({47});
|
||||
vector<uint8_t> out_data = result.get();
|
||||
|
||||
CHECK(1 * 8 == trans_fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) trans_fix.orig_trans->tx_buffer)[0]);
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
CHECK(out_data.size() == 1);
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction with pre_callback")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
bool pre_cb_called = false;
|
||||
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
auto result = dev.transfer({47}, [&] (void *user) { pre_cb_called = true; });
|
||||
vector<uint8_t> out_data = result.get();
|
||||
|
||||
SPITransactionDescriptor *transaction = reinterpret_cast<SPITransactionDescriptor*>(trans_fix.orig_trans->user);
|
||||
dev_fix.dev_config.pre_cb(trans_fix.orig_trans);
|
||||
CHECK(true == pre_cb_called);
|
||||
CHECK(1 * 8 == trans_fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) trans_fix.orig_trans->tx_buffer)[0]);
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
CHECK(out_data.size() == 1);
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction with post_callback")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
bool post_cb_called = false;
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
|
||||
auto result = dev.transfer({47}, [&] (void *user) { }, [&] (void *user) { post_cb_called = true; });
|
||||
vector<uint8_t> out_data = result.get();
|
||||
|
||||
dev_fix.dev_config.post_cb(trans_fix.orig_trans);
|
||||
CHECK(true == post_cb_called);
|
||||
CHECK(1 * 8 == trans_fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) trans_fix.orig_trans->tx_buffer)[0]);
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
CHECK(out_data.size() == 1);
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction data routed to pre callback")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
bool pre_cb_called = false;
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
|
||||
auto result = dev.transfer({47},
|
||||
[&] (void *user) { *(static_cast<bool*>(user)) = true; },
|
||||
[&] (void *user) { },
|
||||
&pre_cb_called);
|
||||
result.get();
|
||||
|
||||
dev_fix.dev_config.pre_cb(trans_fix.orig_trans);
|
||||
CHECK(true == pre_cb_called);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("SPI transaction data routed to post callback")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
bool post_cb_called = false;
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
|
||||
auto result = dev.transfer({47},
|
||||
[&] (void *user) { },
|
||||
[&] (void *user) { *(static_cast<bool*>(user)) = true; },
|
||||
&post_cb_called);
|
||||
result.get();
|
||||
|
||||
dev_fix.dev_config.post_cb(trans_fix.orig_trans);
|
||||
CHECK(true == post_cb_called);
|
||||
}
|
||||
|
||||
TEST_CASE("SPI two transactions")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::SUCCEED);
|
||||
bool pre_cb_called = false;
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
std::function<void(void *)> pre_callback = [&] (void *user) {
|
||||
pre_cb_called = true;
|
||||
};
|
||||
|
||||
auto result = dev.transfer({47}, pre_callback);
|
||||
vector<uint8_t> out_data = result.get();
|
||||
|
||||
dev_fix.dev_config.pre_cb(trans_fix.orig_trans);
|
||||
CHECK(true == pre_cb_called);
|
||||
|
||||
CHECK(1 * 8 == trans_fix.orig_trans->length);
|
||||
CHECK(47 == ((uint8_t*) trans_fix.orig_trans->tx_buffer)[0]);
|
||||
|
||||
REQUIRE(out_data.begin() != out_data.end());
|
||||
CHECK(out_data.size() == 1);
|
||||
CHECK(0xA6 == out_data[0]);
|
||||
|
||||
// preparing the second transfer
|
||||
pre_cb_called = false;
|
||||
spi_device_acquire_bus_ExpectAndReturn(trans_fix.handle, portMAX_DELAY, ESP_OK);
|
||||
spi_device_acquire_bus_IgnoreArg_device();
|
||||
spi_device_queue_trans_ExpectAndReturn(trans_fix.handle, nullptr, 0, ESP_OK);
|
||||
spi_device_queue_trans_IgnoreArg_trans_desc();
|
||||
spi_device_queue_trans_IgnoreArg_handle();
|
||||
spi_device_get_trans_result_ExpectAndReturn(trans_fix.handle, nullptr, portMAX_DELAY, ESP_OK);
|
||||
spi_device_get_trans_result_IgnoreArg_trans_desc();
|
||||
spi_device_get_trans_result_IgnoreArg_handle();
|
||||
spi_device_release_bus_Ignore();
|
||||
|
||||
|
||||
result = dev.transfer({47}, pre_callback);
|
||||
result.get();
|
||||
|
||||
dev_fix.dev_config.pre_cb(trans_fix.orig_trans);
|
||||
CHECK(true == pre_cb_called);
|
||||
}
|
||||
|
||||
TEST_CASE("SPIFuture invalid after default construction")
|
||||
{
|
||||
SPIFuture future;
|
||||
CHECK(false == future.valid());
|
||||
}
|
||||
|
||||
TEST_CASE("SPIFuture valid")
|
||||
{
|
||||
CMOCK_SETUP();
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
shared_ptr<SPITransactionDescriptor> trans(new SPITransactionDescriptor(std::vector<uint8_t>(47), &handle));
|
||||
SPIFuture future(trans);
|
||||
|
||||
CHECK(true == future.valid());
|
||||
}
|
||||
|
||||
TEST_CASE("SPIFuture wait_for timeout")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionFix transaction_fix(ESP_ERR_TIMEOUT);
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
spi_device_acquire_bus_IgnoreAndReturn(ESP_OK);
|
||||
spi_device_release_bus_Ignore();
|
||||
|
||||
SPIDeviceHandle handle(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
shared_ptr<SPITransactionDescriptor> transaction(new SPITransactionDescriptor(std::vector<uint8_t>(47), &handle));
|
||||
SPIFuture future(transaction);
|
||||
transaction->start();
|
||||
|
||||
CHECK(future.wait_for(std::chrono::milliseconds(47)) == std::future_status::timeout);
|
||||
|
||||
// We need to finish the transaction, otherwise it goes out of scope without finishing and cleaning up the
|
||||
// allocated transaction descriptor.
|
||||
transaction_fix.get_transaction_return = ESP_OK;
|
||||
spi_device_get_trans_result_ExpectAnyArgsAndReturn(ESP_OK);
|
||||
|
||||
future.wait();
|
||||
}
|
||||
|
||||
TEST_CASE("SPIFuture wait_for on SPIFuture")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true, 20);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
auto result = dev.transfer({47});
|
||||
|
||||
CHECK(result.wait_for(std::chrono::milliseconds(20)) == std::future_status::ready);
|
||||
}
|
||||
|
||||
TEST_CASE("SPIFuture wait on SPIFuture")
|
||||
{
|
||||
CMockFixture cmock_fix;
|
||||
SPITransactionDescriptorFix trans_fix(1, true);
|
||||
trans_fix.rx_data = {0xA6};
|
||||
SPIDevFix dev_fix(CreateAnd::IGNORE);
|
||||
|
||||
SPIDevice dev(SPINum(SPI2_HOST), CS(4), Frequency::MHz(1), QueueSize(10));
|
||||
auto result = dev.transfer({47});
|
||||
|
||||
result.wait();
|
||||
|
||||
vector<uint8_t> out_data = result.get();
|
||||
CHECK(out_data.size() == 1);
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_CXX_EXCEPTIONS=y
|
@@ -0,0 +1,34 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
|
||||
idf_component_set_property(driver USE_MOCK 1)
|
||||
|
||||
# Overriding components which should be mocked
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/driver/")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/")
|
||||
|
||||
# Including experimental component here because it's outside IDF's main component directory
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component/")
|
||||
project(test_system_cxx_host)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage.info"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build"
|
||||
COMMAND lcov --capture --directory . --output-file coverage.info
|
||||
COMMENT "Create coverage report"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage_report/"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/build/coverage.info"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build"
|
||||
COMMAND genhtml coverage.info --output-directory coverage_report/
|
||||
COMMENT "Turn coverage report into html-based visualization"
|
||||
)
|
||||
|
||||
add_custom_target(coverage
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/build"
|
||||
DEPENDS "coverage_report/"
|
||||
)
|
@@ -0,0 +1,8 @@
|
||||
| Supported Targets | Linux |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# Build
|
||||
`idf.py build` (sdkconfig.defaults sets the linux target by default)
|
||||
|
||||
# Run
|
||||
`build/system_cxx_host_test.elf`
|
@@ -0,0 +1,12 @@
|
||||
idf_component_get_property(cpp_component experimental_cpp_component COMPONENT_DIR)
|
||||
|
||||
idf_component_register(SRCS "system_cxx_test.cpp"
|
||||
"${cpp_component}/esp_exception.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
"${cpp_component}/include"
|
||||
$ENV{IDF_PATH}/tools/catch
|
||||
REQUIRES driver)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage)
|
||||
target_link_libraries(${COMPONENT_LIB} --coverage)
|
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0
|
||||
*
|
||||
* This test 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 "catch.hpp"
|
||||
#include "system_cxx.hpp"
|
||||
|
||||
// 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;
|
||||
|
||||
TEST_CASE("Frequency invalid")
|
||||
{
|
||||
CHECK_THROWS_AS(Frequency(0), ESPException&);
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency constructors correct")
|
||||
{
|
||||
Frequency f0(440);
|
||||
CHECK(440 == f0.get_value());
|
||||
Frequency f1 = Frequency::Hz(440);
|
||||
CHECK(440 == f1.get_value());
|
||||
Frequency f2 = Frequency::KHz(440);
|
||||
CHECK(440000 == f2.get_value());
|
||||
Frequency f3 = Frequency::MHz(440);
|
||||
CHECK(440000000 == f3.get_value());
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op ==")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(440);
|
||||
CHECK(f1 == f0);
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op !=")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(441);
|
||||
CHECK(f1 != f0);
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op >")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(441);
|
||||
Frequency f2(440);
|
||||
CHECK(f1 > f0);
|
||||
CHECK(!(f0 > f1));
|
||||
CHECK(!(f0 > f2));
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op <")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(441);
|
||||
Frequency f2(440);
|
||||
CHECK(f0 < f1);
|
||||
CHECK(!(f1 < f0));
|
||||
CHECK(!(f0 < f2));
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op >=")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(441);
|
||||
Frequency f2(440);
|
||||
CHECK (f1 >= f0);
|
||||
CHECK(!(f0 >= f1));
|
||||
CHECK (f0 >= f2);
|
||||
}
|
||||
|
||||
TEST_CASE("Frequency op <=")
|
||||
{
|
||||
Frequency f0(440);
|
||||
Frequency f1(441);
|
||||
Frequency f2(440);
|
||||
CHECK (f0 <= f1);
|
||||
CHECK(!(f1 <= f0));
|
||||
CHECK (f0 <= f2);
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_CXX_EXCEPTIONS=y
|
Reference in New Issue
Block a user