mirror of
https://github.com/esp8266/Arduino.git
synced 2025-07-24 19:42:27 +03:00
Previously device tests included information such as access point SSID/password at compile time. This made it difficult to compile test binaries once and then send them to multiple test runners for execution. This change adds a command to the test library to set environment variable on the target device: “setenv key value”. C library setenv/getenv facility is used to store variables. Test runner, tests, and makefile are updated to use this functionality.
224 lines
5.1 KiB
C++
224 lines
5.1 KiB
C++
#ifndef BSTEST_H
|
|
#define BSTEST_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <functional>
|
|
#include <stdexcept>
|
|
#include "BSProtocol.h"
|
|
|
|
#if defined(ARDUINO)
|
|
#include "BSArduino.h"
|
|
#else
|
|
#include "BSStdio.h"
|
|
#endif
|
|
|
|
#ifndef BS_LINE_BUF_SIZE
|
|
#define BS_LINE_BUF_SIZE 80
|
|
#endif
|
|
|
|
namespace bs
|
|
{
|
|
typedef void(*test_case_func_t)();
|
|
|
|
class TestCase
|
|
{
|
|
public:
|
|
TestCase(TestCase* prev, test_case_func_t func, const char* file, size_t line, const char* name, const char* desc)
|
|
: m_func(func), m_file(file), m_line(line), m_name(name), m_desc(desc)
|
|
{
|
|
if (prev) {
|
|
prev->m_next = this;
|
|
}
|
|
}
|
|
|
|
void run() const
|
|
{
|
|
(*m_func)();
|
|
}
|
|
|
|
TestCase* next() const
|
|
{
|
|
return m_next;
|
|
}
|
|
|
|
const char* file() const
|
|
{
|
|
return m_file;
|
|
}
|
|
|
|
size_t line() const
|
|
{
|
|
return m_line;
|
|
}
|
|
|
|
const char* name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
const char* desc() const
|
|
{
|
|
return (m_desc)?m_desc:"";
|
|
}
|
|
|
|
protected:
|
|
TestCase* m_next = nullptr;
|
|
test_case_func_t m_func;
|
|
const char* m_file;
|
|
size_t m_line;
|
|
const char* m_name;
|
|
const char* m_desc;
|
|
};
|
|
|
|
struct Registry {
|
|
void add(test_case_func_t func, const char* file, size_t line, const char* name, const char* desc)
|
|
{
|
|
TestCase* tc = new TestCase(m_last, func, file, line, name, desc);
|
|
if (!m_first) {
|
|
m_first = tc;
|
|
}
|
|
m_last = tc;
|
|
}
|
|
TestCase* m_first = nullptr;
|
|
TestCase* m_last = nullptr;
|
|
};
|
|
|
|
struct Env {
|
|
std::function<void(void)> m_check_pass;
|
|
std::function<void(size_t)> m_check_fail;
|
|
std::function<void(size_t)> m_fail;
|
|
Registry m_registry;
|
|
};
|
|
|
|
extern Env g_env;
|
|
|
|
template<typename IO>
|
|
class Runner
|
|
{
|
|
typedef Runner<IO> Tself;
|
|
public:
|
|
Runner(IO& io) : m_io(io)
|
|
{
|
|
g_env.m_check_pass = std::bind(&Tself::check_pass, this);
|
|
g_env.m_check_fail = std::bind(&Tself::check_fail, this, std::placeholders::_1);
|
|
g_env.m_fail = std::bind(&Tself::fail, this, std::placeholders::_1);
|
|
}
|
|
|
|
~Runner()
|
|
{
|
|
g_env.m_check_pass = 0;
|
|
g_env.m_check_fail = 0;
|
|
g_env.m_fail = 0;
|
|
}
|
|
|
|
void run()
|
|
{
|
|
do {
|
|
} while(do_menu());
|
|
}
|
|
|
|
void check_pass()
|
|
{
|
|
++m_check_pass_count;
|
|
}
|
|
|
|
void check_fail(size_t line)
|
|
{
|
|
++m_check_fail_count;
|
|
protocol::output_check_failure(m_io, line);
|
|
}
|
|
|
|
void fail(size_t line)
|
|
{
|
|
protocol::output_test_end(m_io, false, m_check_pass_count + m_check_fail_count, m_check_fail_count, line);
|
|
bs::fatal();
|
|
}
|
|
|
|
protected:
|
|
bool do_menu()
|
|
{
|
|
protocol::output_menu_begin(m_io);
|
|
int id = 1;
|
|
for (TestCase* tc = g_env.m_registry.m_first; tc; tc = tc->next(), ++id) {
|
|
protocol::output_menu_item(m_io, id, tc->name(), tc->desc());
|
|
}
|
|
protocol::output_menu_end(m_io);
|
|
while(true) {
|
|
int id;
|
|
char line_buf[BS_LINE_BUF_SIZE];
|
|
if (!protocol::input_handle(m_io, line_buf, sizeof(line_buf), id)) {
|
|
continue;
|
|
}
|
|
if (id < 0) {
|
|
return true;
|
|
}
|
|
TestCase* tc = g_env.m_registry.m_first;
|
|
for (int i = 0; i != id - 1 && tc; ++i, tc = tc->next());
|
|
if (!tc) {
|
|
bs::fatal();
|
|
}
|
|
m_check_pass_count = 0;
|
|
m_check_fail_count = 0;
|
|
protocol::output_test_start(m_io, tc->file(), tc->line(), tc->name(), tc->desc());
|
|
tc->run();
|
|
bool success = m_check_fail_count == 0;
|
|
protocol::output_test_end(m_io, success, m_check_pass_count + m_check_fail_count, m_check_fail_count);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
IO& m_io;
|
|
size_t m_check_pass_count;
|
|
size_t m_check_fail_count;
|
|
};
|
|
|
|
class AutoReg
|
|
{
|
|
public:
|
|
AutoReg(test_case_func_t func, const char* file, size_t line, const char* name, const char* desc = nullptr)
|
|
{
|
|
g_env.m_registry.add(func, file, line, name, desc);
|
|
}
|
|
};
|
|
|
|
inline void check(bool condition, size_t line)
|
|
{
|
|
if (!condition) {
|
|
g_env.m_check_fail(line);
|
|
} else {
|
|
g_env.m_check_pass();
|
|
}
|
|
}
|
|
|
|
inline void require(bool condition, size_t line)
|
|
{
|
|
if (!condition) {
|
|
g_env.m_check_fail(line);
|
|
g_env.m_fail(line);
|
|
} else {
|
|
g_env.m_check_pass();
|
|
}
|
|
}
|
|
|
|
} // ::bs
|
|
|
|
#define BS_NAME_LINE2( name, line ) name##line
|
|
#define BS_NAME_LINE( name, line ) BS_NAME_LINE2( name, line )
|
|
#define BS_UNIQUE_NAME( name ) BS_NAME_LINE( name, __LINE__ )
|
|
|
|
#define TEST_CASE( ... ) \
|
|
static void BS_UNIQUE_NAME( TEST_FUNC__ )(); \
|
|
namespace{ bs::AutoReg BS_UNIQUE_NAME( test_autoreg__ )( &BS_UNIQUE_NAME( TEST_FUNC__ ), __FILE__, __LINE__, __VA_ARGS__ ); }\
|
|
static void BS_UNIQUE_NAME( TEST_FUNC__ )()
|
|
|
|
#define CHECK(condition) bs::check((condition), __LINE__)
|
|
#define REQUIRE(condition) bs::require((condition), __LINE__)
|
|
#define FAIL() bs::g_env.m_fail(__LINE__)
|
|
|
|
#define BS_ENV_DECLARE() namespace bs { Env g_env; }
|
|
#define BS_RUN(...) do { bs::IOHelper helper = bs::IOHelper(__VA_ARGS__); bs::Runner<bs::IOHelper> runner(helper); runner.run(); } while(0);
|
|
|
|
#endif //BSTEST_H
|