diff --git a/pkg/Kconfig b/pkg/Kconfig index d5b1778165..31fd88cd76 100644 --- a/pkg/Kconfig +++ b/pkg/Kconfig @@ -15,6 +15,7 @@ rsource "driver_atwinc15x0/Kconfig" rsource "driver_bme680/Kconfig" rsource "driver_sx126x/Kconfig" rsource "emlearn/Kconfig" +rsource "fff/Kconfig" rsource "gemmlowp/Kconfig" rsource "hacl/Kconfig" rsource "heatshrink/Kconfig" diff --git a/pkg/fff/Kconfig b/pkg/fff/Kconfig new file mode 100644 index 0000000000..b6bc905b3a --- /dev/null +++ b/pkg/fff/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2021 Jens Wetterich +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config PACKAGE_FFF + bool "Fake functions framework" + depends on TEST_KCONFIG + help + A header only fake functions framework for unit testing. diff --git a/pkg/fff/Makefile b/pkg/fff/Makefile new file mode 100644 index 0000000000..69db577b2b --- /dev/null +++ b/pkg/fff/Makefile @@ -0,0 +1,9 @@ +PKG_NAME=fff +PKG_URL=https://github.com/meekrosoft/fff +PKG_VERSION=7e09f07e5b262b1cc826189dc5057379e40ce886 # v1.1 +PKG_LICENSE=MIT + +include $(RIOTBASE)/pkg/pkg.mk + +all: + @: diff --git a/pkg/fff/Makefile.include b/pkg/fff/Makefile.include new file mode 100644 index 0000000000..5f9a0f6442 --- /dev/null +++ b/pkg/fff/Makefile.include @@ -0,0 +1,5 @@ +INCLUDES += -I$(PKGDIRBASE)/fff + +# There's nothing to build in this package, it's used as a header only library. +# So it's declared as a pseudo-module +PSEUDOMODULES += fff diff --git a/pkg/fff/doc.txt b/pkg/fff/doc.txt new file mode 100644 index 0000000000..96187c391a --- /dev/null +++ b/pkg/fff/doc.txt @@ -0,0 +1,7 @@ +/** + * @defgroup pkg_fff Fake Functions Framework + * @ingroup pkg + * @brief Fake Functions Framework (fff) + * + * @see https://github.com/meekrosoft/fff + */ diff --git a/sys/include/embUnit.h b/sys/include/embUnit.h index 6a17f1bcd5..43764abaaf 100644 --- a/sys/include/embUnit.h +++ b/sys/include/embUnit.h @@ -14,6 +14,7 @@ * @see https://sourceforge.net/projects/embunit * * @note Please refer to https://github.com/RIOT-OS/RIOT/wiki/Testing-RIOT + * @note If mocking would be helpful for your unit test, you can also have a look at @ref pkg_fff. * * @author Martine Lenders */ diff --git a/tests/pkg_fff/Makefile b/tests/pkg_fff/Makefile new file mode 100644 index 0000000000..fca286b0a9 --- /dev/null +++ b/tests/pkg_fff/Makefile @@ -0,0 +1,5 @@ +include ../Makefile.tests_common +USEPKG += fff +# If periph_i2c is pulled in the real implementation conflicts with the mock +FEATURES_BLACKLIST += periph_i2c +include $(RIOTBASE)/Makefile.include diff --git a/tests/pkg_fff/Makefile.ci b/tests/pkg_fff/Makefile.ci new file mode 100644 index 0000000000..7139b819a6 --- /dev/null +++ b/tests/pkg_fff/Makefile.ci @@ -0,0 +1,2 @@ +BOARD_INSUFFICIENT_MEMORY := \ + nucleo-l011k4 diff --git a/tests/pkg_fff/app.config.test b/tests/pkg_fff/app.config.test new file mode 100644 index 0000000000..a922209180 --- /dev/null +++ b/tests/pkg_fff/app.config.test @@ -0,0 +1 @@ +CONFIG_PACKAGE_FFF=y diff --git a/tests/pkg_fff/main.c b/tests/pkg_fff/main.c new file mode 100644 index 0000000000..fad4a9aca0 --- /dev/null +++ b/tests/pkg_fff/main.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 Jens Wetterich + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * @file + * @brief Unit test to test the usage of @ref pkg_fff module + * @author Jens Wetterich + */ + +#define FFF_ARG_HISTORY_LEN 1u +#define FFF_CALL_HISTORY_LEN 1u +#include "fff.h" +#include "periph/i2c.h" +#include +DEFINE_FFF_GLOBALS +FAKE_VALUE_FUNC(int, i2c_read_bytes, i2c_t, uint16_t, void *, size_t, uint8_t) +FAKE_VALUE_FUNC(int, i2c_acquire, i2c_t) +FAKE_VOID_FUNC(i2c_release, i2c_t) + +int test_i2c_basic(void *buffer, size_t len); +int read_fake_impl(i2c_t dev, uint16_t addr, void *data, size_t size, uint8_t flags); + +const unsigned int fake_read_len = 6; +const char *fake_read_data = "hello"; +const i2c_t device = 0; +const uint16_t address = 4; +const uint8_t flags = 0; + +int test_i2c_basic(void *buffer, size_t len) +{ + int acquire_return_val; + int read_return_val; + int failure = 0; + + acquire_return_val = i2c_acquire(device); + if (acquire_return_val != 0) { + failure = 1; + } + read_return_val = i2c_read_bytes(device, address, buffer, len, flags); + if (read_return_val != 0) { + failure = 1; + } + i2c_release(device); + return -failure; +} + +int read_fake_impl(i2c_t dev, uint16_t addr, void *data, size_t size, uint8_t fl) +{ + (void)dev; + (void)addr; + (void)size; + (void)fl; + memcpy(data, fake_read_data, fake_read_len); + return 0; +} + +int main(void) +{ + uint8_t buffer[fake_read_len]; + int basic_test_return_val; + int failure = 0; + + puts("Testing fff"); + /* Set fake implementation / return values of the mocks */ + i2c_read_bytes_fake.custom_fake = read_fake_impl; + i2c_acquire_fake.return_val = 0; + /* Run function under test */ + basic_test_return_val = test_i2c_basic(buffer, fake_read_len); + /* Assert correct interaction of the function under test with the mocked API */ + if (basic_test_return_val != 0) { + failure = 1; + puts("The return value of the read_bytes mock didn't match the expected value"); + } + if (i2c_read_bytes_fake.arg0_val != device) { + failure = 1; + puts("The device parameter to the read_bytes mock didn't match the expected value"); + } + if (i2c_read_bytes_fake.arg1_val != address) { + failure = 1; + puts("The address parameter to the read_bytes mock didn't match the expected value"); + } + if (memcmp(buffer, fake_read_data, fake_read_len) != 0) { + failure = 1; + puts("The read_bytes mock didn't write the expected data into the buffer"); + } + if (i2c_read_bytes_fake.arg3_val != fake_read_len) { + failure = 1; + puts("The len parameter to the read_bytes mock didn't match the expected value"); + } + if (i2c_read_bytes_fake.arg4_val != flags) { + failure = 1; + puts("The flags parameter to the read_bytes mock didn't match the expected value"); + } + if (i2c_read_bytes_fake.call_count != 1) { + failure = 1; + puts("The call count for the read_bytes mock didn't match the expected value"); + } + if (i2c_acquire_fake.call_count != 1) { + failure = 1; + puts("The call count for the acquire mock didn't match the expected value"); + } + if (i2c_release_fake.call_count != 1) { + failure = 1; + puts("The call count for the release mock didn't match the expected value"); + } + printf("FFF test completed: %s\n", failure ? "FAILURE" : "SUCCESS"); + return 0; +} +/** @} */ diff --git a/tests/pkg_fff/tests/01-run.py b/tests/pkg_fff/tests/01-run.py new file mode 100755 index 0000000000..f20bfb623f --- /dev/null +++ b/tests/pkg_fff/tests/01-run.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021 Jens Wetterich +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact("FFF test completed: SUCCESS") + print("All tests successful") + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=1, echo=True, traceback=True))