1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-09-02 10:53:01 +02:00

Added pin unit tests and unit test framework.

This commit is contained in:
Stefan de Bruijn
2020-10-20 12:58:59 +02:00
parent f089ed8b79
commit 672566ebf6
26 changed files with 1458 additions and 17 deletions

5
.gitignore vendored
View File

@@ -16,7 +16,8 @@ Release/
*.vsarduino.h
__vm/
*.user
*.vcxproj
*.vcxproj.filters
Grbl_Esp32.vcxproj
Grbl_Esp32.vcxproj.filters
*.suo
Grbl_Esp32.ino.cpp
packages/

View File

@@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29306.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grbl_Esp32", "Grbl_Esp32.vcxproj", "{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests.vcxproj", "{33ECE513-60D1-4949-A4A9-C95D353C2CF0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -21,6 +23,14 @@ Global
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x64.Build.0 = Release|x64
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x86.ActiveCfg = Release|Win32
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x86.Build.0 = Release|Win32
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Debug|x64.ActiveCfg = Debug|x64
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Debug|x64.Build.0 = Debug|x64
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Debug|x86.ActiveCfg = Debug|Win32
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Debug|x86.Build.0 = Debug|Win32
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Release|x64.ActiveCfg = Release|x64
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Release|x64.Build.0 = Release|x64
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Release|x86.ActiveCfg = Release|Win32
{33ECE513-60D1-4949-A4A9-C95D353C2CF0}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -18,7 +18,8 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "src/Grbl.h"
#ifndef UNIT_TEST
# include "src/Grbl.h"
void setup() {
grbl_init();
@@ -27,3 +28,5 @@ void setup() {
void loop() {
run_once();
}
#endif

View File

@@ -1,5 +1,9 @@
#pragma once
#include "StackTrace/AssertionFailed.h"
class AssertionFailed;
#undef Assert
#define Stringify(x) #x
@@ -7,7 +11,7 @@
#define Assert(condition, ...) \
{ \
if (!(condition)) { \
const char* ch = #condition " (@line " Stringify2(__LINE__) ")"; \
throw ch; \
const char* ch = #condition " (@line " Stringify2(__LINE__) ")"; \
throw AssertionFailed::create(ch, ##__VA_ARGS__); \
} \
}

View File

@@ -110,11 +110,16 @@ static void reset_variables() {
}
void run_once() {
reset_variables();
// Start Grbl main loop. Processes program inputs and executes them.
// This can exit on a system abort condition, in which case run_once()
// is re-executed by an enclosing loop.
protocol_main_loop();
try {
reset_variables();
// Start Grbl main loop. Processes program inputs and executes them.
// This can exit on a system abort condition, in which case run_once()
// is re-executed by an enclosing loop.
protocol_main_loop();
} catch (AssertionFailed ex) {
// This means something is terribly broken:
grbl_sendf(CLIENT_ALL, "Critical error: %s", ex.stackTrace.c_str());
}
}
/*

View File

@@ -35,7 +35,7 @@ namespace Pins {
}
// If it's exclusive, we are not allowed to set it again:
if (_value != Undefined && this->has(Exclusive) && _value != t._value) {
if (_value != Undefined._value && this->has(Exclusive) && _value != t._value) {
return true;
}

View File

@@ -0,0 +1,83 @@
#include "AssertionFailed.h"
#include <cstdarg>
#include <cstring>
#ifdef ESP32
# ifdef UNIT_TEST
# include "debug_helpers.h"
# include "WString.h"
# include "stdio.h"
AssertionFailed AssertionFailed::create(const char* condition, const char* msg, ...) {
String st = condition;
st += ": ";
char tmp[255];
va_list arg;
va_start(arg, msg);
size_t len = vsnprintf(tmp, 255, msg, arg);
tmp[254] = 0;
st += tmp;
st += " at: ";
st += esp_backtrace_print(10);
return AssertionFailed(st);
}
# else
# include "stdio.h"
AssertionFailed AssertionFailed::create(const char* condition, const char* msg, ...) {
String st = "\r\nError ";
st += condition;
st += " failed: ";
char tmp[255];
va_list arg;
va_start(arg, msg);
size_t len = vsnprintf(tmp, 255, msg, arg);
tmp[254] = 0;
st += tmp;
return AssertionFailed(st);
}
# endif
#else
# include <iostream>
# include <string>
# include <sstream>
# include "WString.h"
extern void DumpStackTrace(std::ostringstream& builder);
String stackTrace;
std::exception AssertionFailed::create(const char* condition, const char* msg, ...) {
std::ostringstream oss;
oss << std::endl;
oss << "Error: " << std::endl;
char tmp[255];
va_list arg;
va_start(arg, msg);
size_t len = vsnprintf(tmp, 255, msg, arg);
tmp[254] = 0;
oss << tmp;
oss << " at ";
DumpStackTrace(oss);
// Store in a static temp:
static std::string info;
info = oss.str();
throw std::exception(info.c_str());
}
#endif

View File

@@ -0,0 +1,32 @@
#pragma once
#include "WString.h"
#ifdef ESP32
class AssertionFailed {
public:
String stackTrace;
AssertionFailed(String st) : stackTrace(st) {}
static AssertionFailed create(const char* condition) {
return create(condition, "Assertion failed");
}
static AssertionFailed create(const char* condition, const char* msg, ...);
};
#else
# include <exception>
class AssertionFailed {
public:
String stackTrace;
static std::exception create(const char* condition) {
return create(condition, "Assertion failed");
}
static std::exception create(const char* condition, const char* msg, ...);
};
#endif

View File

@@ -0,0 +1,92 @@
#ifdef ESP32
# ifdef UNIT_TEST
// Copyright 2015-2019 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.
# include <Arduino.h>
# include <WString.h>
# include "esp_types.h"
# include "esp_attr.h"
# include "esp_err.h"
# include "debug_helpers.h"
// #include "esp32/rom/ets_sys.h"
# include "soc/soc_memory_layout.h"
# include "soc/cpu.h"
static inline bool esp_stack_ptr_is_sane(uint32_t sp) {
return !(sp < 0x3ffae010UL || sp > 0x3ffffff0UL || ((sp & 0xf) != 0));
}
static inline uint32_t esp_cpu_process_stack_pc(uint32_t pc) {
if (pc & 0x80000000) {
//Top two bits of a0 (return address) specify window increment. Overwrite to map to address space.
pc = (pc & 0x3fffffff) | 0x40000000;
}
//Minus 3 to get PC of previous instruction (i.e. instruction executed before return address)
return pc - 3;
}
bool IRAM_ATTR esp_backtrace_get_next_frame(esp_backtrace_frame_t* frame) {
//Use frame(i-1)'s BS area located below frame(i)'s sp to get frame(i-1)'s sp and frame(i-2)'s pc
void* base_save = (void*)frame->sp; //Base save area consists of 4 words under SP
frame->pc = frame->next_pc;
frame->next_pc = *((uint32_t*)(((char*)base_save) - 16)); //If next_pc = 0, indicates frame(i-1) is the last frame on the stack
frame->sp = *((uint32_t*)(((char*)base_save) - 12));
//Return true if both sp and pc of frame(i-1) are sane, false otherwise
return (esp_stack_ptr_is_sane(frame->sp) && esp_ptr_executable((void*)esp_cpu_process_stack_pc(frame->pc)));
}
String IRAM_ATTR esp_backtrace_print(int depth) {
char buf[80];
//Check arguments
if (depth <= 0) {
return "";
}
//Initialize stk_frame with first frame of stack
esp_backtrace_frame_t stk_frame;
esp_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), &(stk_frame.next_pc));
//esp_cpu_get_backtrace_start(&stk_frame);
String s = "backtrace:";
snprintf(buf, 80, "0x%08X:0x%08X ", esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
s += buf;
//Check if first frame is valid
bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) && esp_ptr_executable((void*)esp_cpu_process_stack_pc(stk_frame.pc))) ? false :
true;
uint32_t i = (depth <= 0) ? INT32_MAX : depth;
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
if (!esp_backtrace_get_next_frame(&stk_frame)) { //Get previous stack frame
corrupted = true;
}
snprintf(buf, 80, "0x%08X:0x%08X ", esp_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
s += buf;
}
//Print backtrace termination marker
if (corrupted) {
s += " |<-CORRUPTED";
} else if (stk_frame.next_pc != 0) { //Backtrace continues
s += " |<-CONTINUES";
}
return s;
}
# endif
#endif

View File

@@ -0,0 +1,106 @@
#ifdef ESP32
# ifdef UNIT_TEST
// Copyright 2015-2019 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.
# pragma once
# ifdef __cplusplus
extern "C" {
# endif
# ifndef __ASSEMBLER__
# include <stdbool.h>
# include "esp_err.h"
# include "soc/soc.h"
# define ESP_WATCHPOINT_LOAD 0x40000000
# define ESP_WATCHPOINT_STORE 0x80000000
# define ESP_WATCHPOINT_ACCESS 0xC0000000
/*
* @brief Structure used for backtracing
*
* This structure stores the backtrace information of a particular stack frame
* (i.e. the PC and SP). This structure is used iteratively with the
* esp_cpu_get_next_backtrace_frame() function to traverse each frame within a
* single stack. The next_pc represents the PC of the current frame's caller, thus
* a next_pc of 0 indicates that the current frame is the last frame on the stack.
*
* @note Call esp_backtrace_get_start() to obtain initialization values for
* this structure
*/
typedef struct {
uint32_t pc; /* PC of the current frame */
uint32_t sp; /* SP of the current frame */
uint32_t next_pc; /* PC of the current frame's caller */
} esp_backtrace_frame_t;
/**
* Get the first frame of the current stack's backtrace
*
* Given the following function call flow (B -> A -> X -> esp_backtrace_get_start),
* this function will do the following.
* - Flush CPU registers and window frames onto the current stack
* - Return PC and SP of function A (i.e. start of the stack's backtrace)
* - Return PC of function B (i.e. next_pc)
*
* @note This function is implemented in assembly
*
* @param[out] pc PC of the first frame in the backtrace
* @param[out] sp SP of the first frame in the backtrace
* @param[out] next_pc PC of the first frame's caller
*/
extern void esp_backtrace_get_start(uint32_t* pc, uint32_t* sp, uint32_t* next_pc);
/**
* Get the next frame on a stack for backtracing
*
* Given a stack frame(i), this function will obtain the next stack frame(i-1)
* on the same call stack (i.e. the caller of frame(i)). This function is meant to be
* called iteratively when doing a backtrace.
*
* Entry Conditions: Frame structure containing valid SP and next_pc
* Exit Conditions:
* - Frame structure updated with SP and PC of frame(i-1). next_pc now points to frame(i-2).
* - If a next_pc of 0 is returned, it indicates that frame(i-1) is last frame on the stack
*
* @param[inout] frame Pointer to frame structure
*
* @return
* - True if the SP and PC of the next frame(i-1) are sane
* - False otherwise
*/
bool esp_backtrace_get_next_frame(esp_backtrace_frame_t* frame);
/**
* @brief Print the backtrace of the current stack
*
* @param depth The maximum number of stack frames to print (should be > 0)
*
* @return
* - ESP_OK Backtrace successfully printed to completion or to depth limit
* - ESP_FAIL Backtrace is corrupted
*/
String esp_backtrace_print(int depth);
# endif
# ifdef __cplusplus
}
# endif
# endif
#endif

View File

@@ -0,0 +1,63 @@
#ifdef ESP32
#ifdef UNIT_TEST
// Copyright 2015-2019 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.
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include <xtensa/hal.h>
/*
* esp_backtrace_get_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc)
*
* High Addr
* ..................
* | i-3 BS |
* | i-1 locals | Function B
* .................. i-1 SP
* | i-2 BS |
* | i locals | Function A (Start of backtrace)
* ------------------ i SP
* | i-1 BS |
* | i+1 locals | Backtracing function (e.g. esp_backtrace_print())
* ------------------ i+1 SP
* | i BS |
* | i+2 locals | esp_backtrace_get_start() <- This function
* ------------------ i+2 SP
* | i+1 BS |
* | i+3 locals | xthal_window_spill()
* ------------------ i+3 SP
* .................. Low Addr
*/
.section .iram1, "ax"
.align 4
.global esp_backtrace_get_start
.type esp_backtrace_get_start, @function
esp_backtrace_get_start:
entry a1, 32
call8 xthal_window_spill //Spill registers onto stack (excluding this function)
//a2, a3, a4 should be out arguments for i SP, i PC, i-1 PC respectively. Use a5 and a6 as scratch
l32e a5, sp, -16 //Get i PC, which is ret addres of i+1
s32i a5, a2, 0 //Store i PC to arg *pc
l32e a6, sp, -12 //Get i+1 SP. Used to access i BS
l32e a5, a6, -12 //Get i SP
s32i a5, a3, 0 //Store i SP to arg *sp
l32e a5, a6, -16 //Get i-1 PC, which is ret address of i
s32i a5, a4, 0 //Store i-1 PC to arg *next_pc
retw
#endif
#endif

View File

@@ -0,0 +1,41 @@
#include "../TestFramework.h"
#include <Arduino.h>
#ifdef ESP32
extern "C" int __digitalRead(uint8_t pin);
extern "C" void __pinMode(uint8_t pin, uint8_t mode);
extern "C" void __digitalWrite(uint8_t pin, uint8_t val);
namespace Pins {
Test(BasicGPIO, ReadGPIORaw) {
auto pin = 26;
// Enable driver, write high/low.
__pinMode(pin, OUTPUT);
__digitalWrite(pin, HIGH);
auto value = __digitalRead(pin);
Assert(value != 0);
__digitalWrite(pin, LOW);
value = __digitalRead(pin);
Assert(value == 0);
__digitalWrite(pin, HIGH);
value = __digitalRead(pin);
Assert(value != 0);
__digitalWrite(pin, LOW);
value = __digitalRead(pin);
Assert(value == 0);
// Disable driver, should read the last value (low).
__pinMode(pin, INPUT);
value = __digitalRead(pin);
Assert(value == 0);
}
}
#endif

View File

@@ -0,0 +1,26 @@
#include "../TestFramework.h"
#include <src/Pin.h>
namespace Pins {
Test(Error, Pins) {
// Error pins should throw whenever they are used.
Pin errorPin = Pin::ERROR;
AssertThrow(errorPin.write(true));
AssertThrow(errorPin.read());
errorPin.setAttr(Pin::Attr::None);
AssertThrow(errorPin.write(true));
AssertThrow(errorPin.read());
AssertThrow(errorPin.attachInterrupt([](void* arg) {}, CHANGE));
AssertThrow(errorPin.detachInterrupt());
Assert(errorPin.capabilities() == Pin::Capabilities::None, "Incorrect caps");
Assert(errorPin.name() == "ERROR_PIN");
}
}

View File

@@ -0,0 +1,250 @@
#include "../TestFramework.h"
#include <src/Pin.h>
#ifdef ESP32
extern "C" void __pinMode(uint8_t pin, uint8_t mode);
extern "C" int __digitalRead(uint8_t pin);
extern "C" void __digitalWrite(uint8_t pin, uint8_t val);
struct GPIONative {
inline static void initialize() {
for (int i = 16; i <= 17; ++i) {
__pinMode(i, OUTPUT);
__digitalWrite(i, LOW);
}
}
inline static void mode(int pin, uint8_t mode) { __pinMode(pin, mode); }
inline static void write(int pin, bool val) { __digitalWrite(pin, val ? HIGH : LOW); }
inline static bool read(int pin) { return __digitalRead(pin) != LOW; }
};
#else
# include <SoftwareGPIO.h>
struct GPIONative {
// We test GPIO pin 16 and 17, and GPIO 16 is wired directly to 17:
static void WriteVirtualCircuitHystesis(SoftwarePin* pins, int pin, bool value) {
switch (pin) {
case 16:
case 17:
pins[16].handlePadChange(value);
pins[17].handlePadChange(value);
break;
}
}
inline static void initialize() { SoftwareGPIO::instance().reset(WriteVirtualCircuitHystesis, false); }
inline static void mode(int pin, uint8_t mode) { SoftwareGPIO::instance().setMode(pin, mode); }
inline static void write(int pin, bool val) { SoftwareGPIO::instance().writeOutput(pin, val); }
inline static bool read(int pin) { return SoftwareGPIO::instance().read(pin); }
};
void digitalWrite(uint8_t pin, uint8_t val);
void pinMode(uint8_t pin, uint8_t mode);
int digitalRead(uint8_t pin);
#endif
namespace Pins {
Test(GPIO, BasicInputOutput1) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Pin gpio17 = Pin::create("gpio.17");
gpio16.setAttr(Pin::Attr::Output);
gpio17.setAttr(Pin::Attr::Input);
Assert(false == gpio16.read());
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
gpio16.on();
Assert(true == gpio16.read());
Assert(true == gpio17.read());
Assert(true == GPIONative::read(16));
Assert(true == GPIONative::read(17));
gpio16.off();
Assert(false == gpio16.read());
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
}
Test(GPIO, BasicInputOutput2) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Pin gpio17 = Pin::create("gpio.17");
gpio16.setAttr(Pin::Attr::Input);
gpio17.setAttr(Pin::Attr::Output);
Assert(false == gpio16.read());
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
gpio17.on();
Assert(true == gpio16.read());
Assert(true == gpio17.read());
Assert(true == GPIONative::read(16));
Assert(true == GPIONative::read(17));
gpio17.off();
Assert(false == gpio16.read());
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
}
void TestISR(int deltaRising, int deltaFalling, int mode) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Pin gpio17 = Pin::create("gpio.17");
gpio16.setAttr(Pin::Attr::Input | Pin::Attr::ISR);
gpio17.setAttr(Pin::Attr::Output);
int hitCount = 0;
int expected = 0;
gpio16.attachInterrupt(
[](void* arg) {
int* hc = static_cast<int*>(arg);
++(*hc);
},
mode,
&hitCount);
// Two ways to set I/O:
// 1. using on/off
// 2. external source (e.g. set softwareio pin value)
//
// We read as well, because that shouldn't modify the state.
//
// NOTE: Hysteresis tells us that we get changes a lot during a small
// window in time. Unfortunately, it's practically impossible to test
// because it bounces all over the place... TODO FIXME, some mechanism
// to cope with that.
for (int i = 0; i < 10; ++i) {
if (deltaRising) {
auto oldCount = hitCount;
gpio17.on();
delay(1);
auto newCount = hitCount;
Assert(oldCount < newCount, "Expected rise after set state");
} else {
gpio17.on();
}
if (deltaFalling) {
auto oldCount = hitCount;
gpio17.off();
delay(1);
auto newCount = hitCount;
Assert(oldCount < newCount, "Expected rise after set state");
} else {
gpio17.off();
}
}
// Detach interrupt. Regardless of what we do, it shouldn't change hitcount anymore.
gpio16.detachInterrupt();
auto oldCount = hitCount;
gpio17.on();
gpio17.off();
delay(1);
auto newCount = hitCount;
Assert(oldCount == newCount, "ISR hitcount error");
}
Test(GPIO, ISRRisingPin) { TestISR(1, 0, RISING); }
Test(GPIO, ISRFallingPin) { TestISR(0, 1, FALLING); }
Test(GPIO, ISRChangePin) { TestISR(1, 1, CHANGE); }
Test(GPIO, NativeForwardingInput) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Pin gpio17 = Pin::create("gpio.17");
pinMode(16, INPUT);
gpio17.setAttr(Pin::Attr::Output);
Assert(LOW == digitalRead(16));
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
gpio17.on();
Assert(HIGH == digitalRead(16));
Assert(true == gpio17.read());
Assert(true == GPIONative::read(16));
Assert(true == GPIONative::read(17));
gpio17.off();
Assert(LOW == digitalRead(16));
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
}
Test(GPIO, NativeForwardingOutput) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Pin gpio17 = Pin::create("gpio.17");
pinMode(16, OUTPUT);
gpio17.setAttr(Pin::Attr::Input);
digitalWrite(16, LOW);
Assert(LOW == digitalRead(16));
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
digitalWrite(16, HIGH);
Assert(HIGH == digitalRead(16));
Assert(true == gpio17.read());
Assert(true == GPIONative::read(16));
Assert(true == GPIONative::read(17));
digitalWrite(16, LOW);
Assert(LOW == digitalRead(16));
Assert(false == gpio17.read());
Assert(false == GPIONative::read(16));
Assert(false == GPIONative::read(17));
}
Test(GPIO, Name) {
GPIONative::initialize();
PinLookup::ResetAllPins();
Pin gpio16 = Pin::create("gpio.16");
Assert(gpio16.name().equals("GPIO.16"), "Name is %s", gpio16.name().c_str());
}
}

View File

@@ -0,0 +1,30 @@
#include "../TestFramework.h"
#include <src/Pin.h>
namespace Pins {
Test(Undefined, Pins) {
// Unassigned pins are not doing much...
Pin unassigned = Pin::UNDEFINED;
Assert(Pin::UNDEFINED == unassigned, "Undefined has wrong pin id");
{
unassigned.write(true);
auto result = unassigned.read();
Assert(0 == result, "Result value incorrect");
}
{
unassigned.write(false);
auto result = unassigned.read();
Assert(0 == result, "Result value incorrect");
}
AssertThrow(unassigned.attachInterrupt([](void* arg) {}, CHANGE));
AssertThrow(unassigned.detachInterrupt());
Assert(unassigned.capabilities() == Pin::Capabilities::None);
Assert(unassigned.name().equals("UNDEFINED_PIN"));
}
}

View File

@@ -0,0 +1,101 @@
#include "TestFactory.h"
#include <src/Assert.h>
#include <WString.h>
#ifdef ESP32
#include "unity.h"
#include <cstdio>
void TestFactory::runAll() {
int index = 0;
auto current = first;
const char* prev = nullptr;
while (current) {
++index;
auto curTestName = current->unitTestName();
auto curTestCase = current->unitTestCase();
char fullName[80];
snprintf(fullName, 80, "%s:%s", curTestName, curTestCase);
auto function = current->getFunction();
UnityDefaultTestRun(function, fullName, index);
current = current->next;
}
}
#else
# include <iostream>
# include <cstring>
# include <cstdio>
# if defined _WIN32 || defined _WIN64
# define WIN32_LEAN_AND_MEAN
# include <Windows.h>
void setColor(int colorIndex) { // 10 = green, 12 = red, 7 = gray, 15 = white
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// you can loop k higher to see more color choices
// pick the colorattribute k you want
SetConsoleTextAttribute(hConsole, colorIndex);
}
# else
void setColor(int colorIndex) {}
# endif
void TestFactory::runAll() {
const int Indent = 80;
char spaces[Indent];
memset(spaces, ' ', Indent - 1);
spaces[Indent - 1] = '\0';
auto current = first;
const char* prev = nullptr;
while (current) {
auto curTest = current->unitTestName();
setColor(15);
if (prev == nullptr || !strcmp(prev, curTest)) {
printf("- Test: %s\r\n", curTest);
prev = curTest;
}
setColor(7);
printf(" - Case: %s", current->unitTestCase());
int len = int(strlen(current->unitTestCase()));
if (len >= (Indent - 5)) {
len = (Indent - 5);
}
printf(spaces + len); // pad.
try {
setColor(10);
current->run();
printf("Passed.\r\n");
} catch (AssertionFailed& ex) {
setColor(12);
printf("FAILED!\r\n");
printf(ex.stackTrace.c_str());
printf("\r\n");
} catch (...) {
setColor(12);
printf("FAILED!\r\n");
// We don't know where unfortunately...
}
current = current->next;
setColor(7);
}
printf("\r\nDone.\r\n");
}
#endif

View File

@@ -0,0 +1,43 @@
#pragma once
#include <src/Assert.h>
struct TestBase {
TestBase() : next(nullptr) {}
TestBase* next;
virtual const char* unitTestCase() const = 0;
virtual const char* unitTestName() const = 0;
#ifdef ESP32
typedef void (*TestFunction)();
virtual TestFunction getFunction() = 0;
#else
#endif
virtual void run() = 0;
};
class TestFactory {
TestBase* first;
TestBase* last;
TestFactory() : first(nullptr), last(nullptr) {}
TestFactory(const TestFactory& o) = default;
public:
static TestFactory& instance() {
static TestFactory instance_;
return instance_;
}
void registerTest(TestBase* test) {
if (last == nullptr) {
first = last = test;
} else {
last->next = test;
last = test;
}
}
void runAll();
};

View File

@@ -0,0 +1,133 @@
#pragma once
#ifdef ESP32
# include <Arduino.h>
# include "unity.h"
# include <src/Assert.h>
# include "TestFactory.h"
# define TEST_CLASS_NAME(testCase, testName) testCase##_##testName##_Test
# define TEST_INST_NAME(testCase, testName) testCase##_##testName##_Test_Instance
// Defines a single unit test. Basically creates a small test class.
# define Test(testCase, testName) \
struct TEST_CLASS_NAME(testCase, testName) : TestBase { \
TEST_CLASS_NAME(testCase, testName)() { TestFactory::instance().registerTest(this); } \
\
const char* unitTestCase() const override { return #testCase; } \
const char* unitTestName() const override { return #testName; } \
\
static void runDetail(); \
static void runWrap() { \
try { \
runDetail(); \
} catch (AssertionFailed ex) { TEST_FAIL_MESSAGE(ex.stackTrace.c_str()); } catch (...) { \
TEST_FAIL_MESSAGE("Failed for unknown reason."); \
} \
} \
void run() override { runWrap(); } \
\
TestFunction getFunction() override { return runWrap; } \
}; \
\
TEST_CLASS_NAME(testCase, testName) TEST_INST_NAME(testCase, testName); \
\
void TEST_CLASS_NAME(testCase, testName)::runDetail()
# define NativeTest(testCase, testName) TEST_INST_NAME(testCase, testName)
# define PlatformTest(testCase, testName) Test(testCase, testName)
inline void PrintSerial(const char* format, ...) {
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
size_t len = vsnprintf(NULL, 0, format, arg);
auto tmp = new char[len + 1];
va_end(copy);
len = vsnprintf(tmp, len + 1, format, arg);
Serial.println(tmp);
va_end(arg);
delete[] tmp;
}
# define Debug(fmt, ...) PrintSerial(fmt, __VA_ARGS__);
# define AssertThrow(statement) \
try { \
statement; \
Assert(false, "Expected statement to throw."); \
} catch (...) {}
#elif defined _WIN32 || defined _WIN64
# include <src/Assert.h>
// Use 'Assert(...)' please.
# define GTEST_DONT_DEFINE_TEST 1
# define GTEST_DONT_DEFINE_ASSERT_EQ 1
# define GTEST_DONT_DEFINE_ASSERT_NE 1
# define GTEST_DONT_DEFINE_ASSERT_LT 1
# define GTEST_DONT_DEFINE_ASSERT_LE 1
# define GTEST_DONT_DEFINE_ASSERT_GE 1
# define GTEST_DONT_DEFINE_ASSERT_GT 1
# define GTEST_DONT_DEFINE_FAIL 1
# define GTEST_DONT_DEFINE_SUCCEED 1
# include "gtest/gtest.h"
# undef EXPECT_THROW
# undef EXPECT_NO_THROW
# undef EXPECT_ANY_THROW
# undef ASSERT_THROW
# undef ASSERT_NO_THROW
# undef ASSERT_ANY_THROW
# define Test(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)
# define NativeTest(testCase, testName) Test(testCase, testName)
# define PlatformTest(testCase, testName) TEST_INST_NAME(testCase, testName)
# define Debug(fmt, ...) printf(fmt, __VA_ARGS__); printf("\r\n");
# define AssertThrow(statement) GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
#else
# include <src/Assert.h>
# include "TestFactory.h"
# define TEST_CLASS_NAME(testCase, testName) testCase##_##testName##_Test
# define TEST_INST_NAME(testCase, testName) testCase##_##testName##_Test_Instance
// Defines a single unit test. Basically creates a small test class.
# define Test(testCase, testName) \
struct TEST_CLASS_NAME(testCase, testName) : TestBase { \
TEST_CLASS_NAME(testCase, testName)() { TestFactory::instance().registerTest(this); } \
\
const char* unitTestCase() const override { return #testCase; } \
const char* unitTestName() const override { return #testName; } \
void run() override; \
}; \
\
TEST_CLASS_NAME(testCase, testName) TEST_INST_NAME(testCase, testName); \
\
void TEST_CLASS_NAME(testCase, testName)::run()
# define NativeTest(testCase, testName) Test(testCase, testName)
# define PlatformTest(testCase, testName) TEST_INST_NAME(testCase, testName)
# define Debug(fmt, ...) printf(fmt, __VA_ARGS__);
# define AssertThrow(statement) \
try { \
statement; \
Assert(false, "Expected statement to throw."); \
} catch (...) {}
#endif

View File

@@ -0,0 +1,21 @@
#include "TestFramework.h"
/* Normally you don't want these:
Test(PassingTest, TestFrameworkTest) {
Assert(1 == 1);
}
Test(FailingTest1, TestFrameworkTest) {
Assert(1 != 1);
}
Test(FailingTest2, TestFrameworkTest) {
Assert(1 != 1, "Oops");
}
Test(FailingTest3, TestFrameworkTest) {
throw "oops";
}
*/

View File

@@ -0,0 +1,41 @@
# Google test code
These unit tests are designed to test a small portion of the GRBL_ESP32
code, directly from your desktop PC. This is not a complete test of
GRBL_ESP32, but a starting point from which we can move on. Testing and
debugging on a desktop machine is obviously much more convenient than
it is on an ESP32 with a multitude of different configurations, not to
mention the fact that you can use a large variety of tools such as
code coverage, profiling, and so forth.
Code here is split into two parts:
1. A subset of the GRBL code is compiled. Over time, this will become more.
2. Unit tests are executed on this code.
## Prerequisites
Google test framework.
## Folders and how this works
Support libraries are implemented that sort-of mimick the Arduino API where
appropriate. This functionality might be extended in the future, and is by
no means intended to be or a complete or even a "working" version; it's
designed to be _testable_.
Generally speaking that means that most features are simply not available.
Things like GPIO just put stuff in a buffer, things like pins can be logged
for analysis and so forth.
The "Support" folder is the main thing that gives this mimicking ability,
so that the code in the Grbl_Esp32 folder is able to compile. For example,
when including `<Arduino.h>`, in fact `Support/Arduino.h` is included.
The include folders that have to be passed to the x86/x64 compiler are:
- X86TestSupport
- ..\Grbl_Esp32
## Test code
Google tests can be found in the `Tests` folder.

View File

@@ -0,0 +1,29 @@
#ifdef ESP32
# include "TestFactory.h"
# include <Arduino.h>
# include <HardwareSerial.h>
# include "unity.h"
void test_blank() {
int i = 5;
TEST_ASSERT_EQUAL(i, 5);
}
void setup() {
delay(500); // Let's give it some time first, in case it triggers a reboot.
UNITY_BEGIN();
// calls to tests will go here
// RUN_TEST(test_blank);
// Run all tests:
TestFactory::instance().runAll();
UNITY_END(); // stop unit testing
}
void loop() {}
#endif

159
UnitTests.vcxproj Normal file
View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{33ece513-60d1-4949-a4a9-c95d353c2cf0}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<None Include="Grbl_Esp32\src\StackTrace\debug_helpers_asm.S" />
<None Include="Grbl_Esp32\test\UnitTests.md" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Grbl_Esp32\src\Assert.h" />
<ClInclude Include="Grbl_Esp32\src\Pin.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\ErrorPinDetail.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\GPIOPinDetail.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\I2SPinDetail.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\PinAttributes.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\PinCapabilities.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\PinDetail.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\PinLookup.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\PinOptionsParser.h" />
<ClInclude Include="Grbl_Esp32\src\Pins\VoidPinDetail.h" />
<ClInclude Include="Grbl_Esp32\src\StackTrace\AssertionFailed.h" />
<ClInclude Include="Grbl_Esp32\src\StackTrace\debug_helpers.h" />
<ClInclude Include="Grbl_Esp32\test\TestFactory.h" />
<ClInclude Include="Grbl_Esp32\test\TestFramework.h" />
<ClInclude Include="X86TestSupport\Arduino.h" />
<ClInclude Include="X86TestSupport\SoftwareGPIO.h" />
<ClInclude Include="X86TestSupport\WString.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Grbl_Esp32\src\Pin.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\ErrorPinDetail.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\GPIOPinDetail.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\I2SPinDetail.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\PinAttributes.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\PinCapabilities.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\PinDetail.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\PinLookup.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\PinOptionsParser.cpp" />
<ClCompile Include="Grbl_Esp32\src\Pins\VoidPinDetail.cpp" />
<ClCompile Include="Grbl_Esp32\src\StackTrace\AssertionFailed.cpp" />
<ClCompile Include="Grbl_Esp32\src\StackTrace\debug_helpers.cpp" />
<ClCompile Include="Grbl_Esp32\test\Pins\BasicGPIO.cpp" />
<ClCompile Include="Grbl_Esp32\test\Pins\Error.cpp" />
<ClCompile Include="Grbl_Esp32\test\Pins\GPIO.cpp" />
<ClCompile Include="Grbl_Esp32\test\Pins\Undefined.cpp" />
<ClCompile Include="Grbl_Esp32\test\TestFactory.cpp" />
<ClCompile Include="Grbl_Esp32\test\TestFrameworkTest.cpp" />
<ClCompile Include="Grbl_Esp32\test\test_main.cpp" />
<ClCompile Include="X86TestSupport\Arduino.cpp" />
<ClCompile Include="X86TestSupport\ExceptionHelper.cpp" />
<ClCompile Include="X86TestSupport\WString.cpp" />
</ItemGroup>
<ItemDefinitionGroup />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets" Condition="Exists('packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" />
</ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)X86TestSupport;$(MSBuildThisFileDirectory)GRBL_Esp32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
</ItemDefinitionGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.1.8.1\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn.targets'))" />
</Target>
</Project>

156
UnitTests.vcxproj.filters Normal file
View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
<UniqueIdentifier>{d251c8e1-710b-4299-bf32-211cbc23e1d2}</UniqueIdentifier>
</Filter>
<Filter Include="test">
<UniqueIdentifier>{0a063b22-59ea-4f6c-a573-7e4aef718f3b}</UniqueIdentifier>
</Filter>
<Filter Include="test\Pins">
<UniqueIdentifier>{cc13daa8-4fd4-4995-a724-77db03c229af}</UniqueIdentifier>
</Filter>
<Filter Include="src\Pins">
<UniqueIdentifier>{2259029a-8770-45f8-889a-f9c25becf189}</UniqueIdentifier>
</Filter>
<Filter Include="src\StackTrace">
<UniqueIdentifier>{a068458b-6148-4377-9d86-41337ee3d689}</UniqueIdentifier>
</Filter>
<Filter Include="X86TestSupport">
<UniqueIdentifier>{a166a529-634f-48dc-b368-6c03f6f0a36f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Grbl_Esp32\test\TestFramework.h">
<Filter>test</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\test\TestFactory.h">
<Filter>test</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pin.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Assert.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\PinAttributes.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\PinCapabilities.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\PinDetail.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\PinLookup.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\PinOptionsParser.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\VoidPinDetail.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\ErrorPinDetail.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\GPIOPinDetail.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\Pins\I2SPinDetail.h">
<Filter>src\Pins</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\StackTrace\AssertionFailed.h">
<Filter>src\StackTrace</Filter>
</ClInclude>
<ClInclude Include="Grbl_Esp32\src\StackTrace\debug_helpers.h">
<Filter>src\StackTrace</Filter>
</ClInclude>
<ClInclude Include="X86TestSupport\SoftwareGPIO.h">
<Filter>X86TestSupport</Filter>
</ClInclude>
<ClInclude Include="X86TestSupport\WString.h">
<Filter>X86TestSupport</Filter>
</ClInclude>
<ClInclude Include="X86TestSupport\Arduino.h">
<Filter>X86TestSupport</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Grbl_Esp32\test\TestFrameworkTest.cpp">
<Filter>test</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\test_main.cpp">
<Filter>test</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\TestFactory.cpp">
<Filter>test</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\Pins\Error.cpp">
<Filter>test\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\Pins\GPIO.cpp">
<Filter>test\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\Pins\Undefined.cpp">
<Filter>test\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\test\Pins\BasicGPIO.cpp">
<Filter>test\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pin.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\PinAttributes.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\PinCapabilities.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\PinDetail.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\PinLookup.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\PinOptionsParser.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\VoidPinDetail.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\ErrorPinDetail.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\GPIOPinDetail.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\Pins\I2SPinDetail.cpp">
<Filter>src\Pins</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\StackTrace\AssertionFailed.cpp">
<Filter>src\StackTrace</Filter>
</ClCompile>
<ClCompile Include="Grbl_Esp32\src\StackTrace\debug_helpers.cpp">
<Filter>src\StackTrace</Filter>
</ClCompile>
<ClCompile Include="X86TestSupport\ExceptionHelper.cpp">
<Filter>X86TestSupport</Filter>
</ClCompile>
<ClCompile Include="X86TestSupport\WString.cpp">
<Filter>X86TestSupport</Filter>
</ClCompile>
<ClCompile Include="X86TestSupport\Arduino.cpp">
<Filter>X86TestSupport</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Grbl_Esp32\test\UnitTests.md">
<Filter>test</Filter>
</None>
<None Include="Grbl_Esp32\src\StackTrace\debug_helpers_asm.S">
<Filter>src\StackTrace</Filter>
</None>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -37,7 +37,7 @@ def FilterFromPath(path):
class Vcxproj:
# configuration, platform
ConfigurationFmt = '\n'.join([
' <ProjectConfiguration Include="Grbl_Esp32\{0}|{1}">',
' <ProjectConfiguration Include="{0}|{1}">',
' <Configuration>{0}</Configuration>',
' <Platform>{1}</Platform>',
' </ProjectConfiguration>'])
@@ -138,7 +138,12 @@ class Generator:
if filters != '':
self.Folders.add(filters)
def AddFile(self, path):
def AddFile(self, path):
if path.find('/test/') >= 0:
return
elif path.find('\\test\\') >= 0:
return
(root, ext) = os.path.splitext(path)
if ext in HEADER_EXT:
self.Headers.add(path)
@@ -148,7 +153,7 @@ class Generator:
self.Others.add(path)
else:
return
self.AddFolder(path)
def Walk(self, path):

4
packages.config Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-dyn" version="1.8.1.3" targetFramework="native" />
</packages>

View File

@@ -1,6 +1,7 @@
[platformio]
src_dir=Grbl_Esp32
lib_dir=libraries
test_dir=Grbl_Esp32/test
data_dir=Grbl_Esp32/data
default_envs=release
;extra_configs=debug.ini
@@ -28,8 +29,6 @@ build_flags =
-DCORE_DEBUG_LEVEL=0
-Wno-unused-variable
-Wno-unused-function
;-DDEBUG_REPORT_HEAP_SIZE
;-DDEBUG_REPORT_STACK_FREE
[env]
lib_deps =
@@ -51,9 +50,13 @@ board_build.flash_mode = qio
build_flags = ${common.build_flags}
src_filter =
+<*.h> +<*.s> +<*.S> +<*.cpp> +<*.c> +<*.ino> +<src/>
-<.git/> -<data/> -<test/> -<tests/>
-<.git/> -<data/> -<test/> -<tests/> -<Custom/>
[env:release]
[env:debug]
build_type = debug
[env:test]
build_type = debug
test_build_project_src = true