Skip to content

Commit

Permalink
TODO test execution starter
Browse files Browse the repository at this point in the history
TODO
- Split into serialWrapper (first) and test execution (second) commits
- Make Ethan a co-author of the seriaLWrapper commit:
https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors
  • Loading branch information
jrvollmer committed Oct 28, 2023
1 parent af55494 commit bcfd5db
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 1 deletion.
29 changes: 28 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,48 @@ execute_process(COMMAND ./parseConfigs.py
set(MBED_PATH ${CMAKE_CURRENT_SOURCE_DIR}/mbed-os CACHE INTERNAL "")
set(MBED_CONFIG_PATH ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "")
set(APP_TARGET embedded-mbed)
set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests)
set(HELPERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/helpers)
set(TEST_SOURCES
${TESTS_DIR}/serialTest.cpp
)
set(TEST_HEADERS
${TESTS_DIR}/test.h
)
set(HELPER_SOURCES
${HELPERS_DIR}/serialWrapper.cpp
)
set(HELPER_HEADERS
${HELPERS_DIR}/serialWrapper.h
)


include(${MBED_PATH}/tools/cmake/app.cmake)

project(${APP_TARGET})

add_subdirectory(${MBED_PATH})

add_executable(${APP_TARGET} testRunner.cpp)
add_executable(${APP_TARGET}
testRunner.cpp
${TEST_SOURCES}
${TEST_HEADERS}
${HELPER_SOURCES}
${HELPER_HEADERS}
)

# Precompile the definitions header. The generated header will be force included in all source files, so they don't need `#include "definitions.h"`
target_precompile_headers(${APP_TARGET}
PRIVATE
definitions.h
)

target_include_directories(${APP_TARGET}
PRIVATE
${TESTS_DIR}
${HELPERS_DIR}
)

target_sources(${APP_TARGET}
PRIVATE
testRunner.cpp
Expand Down
80 changes: 80 additions & 0 deletions helpers/serialWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "serialWrapper.h"


/**
* Reads in a line of text, excluding newline
* Writes a null-terminated string into the provided destination buffer
*
* Parameters:
* dest: Pointer to destination buffer
* buffSize: Size of destination buffer
* * NOTE: Although the newline is not included in the final buffer
* contents, you must ensure that buffSize accounts for both
* the newline and null-terminator at the end of the buffer
* timeout: Time (in milliseconds) to wait for a line to be read
* * If timeout is negative, this will not time out
* * Default timeout is 10000ms (10s)
*
* Returns:
* size_t: Length of string written to buffer or -1 on error or timeout
**/
size_t SerialWrapper::readLine(char* dest, size_t buffSize, int timeout) {
// Length of string stored in character buffer
size_t strlen = 0;
// Stores value returned by BufferedSerial.read()
int err = 0;
// Calling printf within loop is too slow
bool overflow = false;
// Whether or not there is a timeout
bool canTimeout = timeout >= 0;

while(1) {
// Check for buffer overflow before read
/*TODO if(strlen >= BUFFER_SIZE) {
overflow = true;
break;
}*/

// Read in from serial
if((err = this->read(dest + strlen, buffSize)) > 0) {
strlen += err;
// TODO Check for buffer overflow after read
if(strlen >= buffSize) {
overflow = true;
break;
}
// Convert to string
dest[strlen] = '\0';

// Check for newline
char* newline; // Location of newline
char n[2] {'\n', '\0'}; // Newline character string TODO Any particular reason 0xd was used instead of '\n'?
if((newline = strstr(dest, n)) != NULL) {
// Change \n to \0
*newline = '\0';
strlen = newline - dest;
break;
}
} else if(err == 0) {
// Nothing read
if(canTimeout) {
if(timeout <= 0) {
// Time out
return -1;
}
ThisThread::sleep_for(100ms);
timeout -= 100;
}
} else {
// Error reading from buffer
return -1;
}
}

if(overflow) {
// Buffer overflow
return -1;
}

return strlen;
}
19 changes: 19 additions & 0 deletions helpers/serialWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef __SERIAL_WRAPPER_H__
#define __SERIAL_WRAPPER_H__

#include "mbed.h"
#include <string.h>

// TODO REMOVE: #define BUFFER_SIZE 16

class SerialWrapper : public BufferedSerial {
public:
SerialWrapper(PinName tx, PinName rx, int baud=MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE): BufferedSerial(tx, rx, baud) {};
size_t readLine(char* dest, size_t buffSize, int timeout=10000);

// TODO private:
// BufferedSerial *serial;

};

#endif // __SERIAL_WRAPPER_H__
11 changes: 11 additions & 0 deletions testRunner.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
#include "mbed.h"
#include "serialWrapper.h"
#include "serialTest.cpp"

// Serial interface for BTF communication
SerialWrapper pc(USBTX, USBRX);


int main()
{
// Test BTF communication before running other tests
SerialTest serialTest = SerialTest();
if(serialTest.execute()) {
// Run tests
}

while(1) {
wait_us(1000000);
}
Expand Down
58 changes: 58 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Tests
This is where all test classes will be defined.

## Creating a New Test Class
All custom test classes must inherit the base `Test` class, as it defines a standard test structure that is required for communicating with the [BTF](https://github.com/badgerloop-software/BTF) test runner. Additionally, `test.h` provides a declaration of `pc`, a `SerialWrapper` object for communicating with the BTF test runner. The following is an example custom test class with implementation notes:
```cpp
#ifndef __CUSTOM_TEST__
#define __CUSTOM_TEST__

#include "test.h"
#include <string>

using namespace std;


class CustomTest : public Test {
public:
// REQUIRED: Pass the name of the test to the base class' constructor
// The test name should be formatted as shown below, using the keys from tests.yml
// NOTE: If multiple tests have the same setup() and teardown(), you could use
// `CustomTest(const string name): Test(name) {` to run different tests in runTest()
// based on the name and save on redefining setup() and teardown()
CustomTest(): Test("<driver>_<test_name>") {
// Add initialization here
};
private:
// OPTIONAL: Test setup. If not defined, there is no setup
// This is called before starting the test in the BTF
void setup() {
// Add custom setup here
}

// REQUIRED: Main test content
// Returns true if test passes, false otherwise
bool runTest() {
char buf[5];
// You can use printf() and the pc object to communicate with the BTF
printf("foo\n"); // pc.write() also works
pc.readLine(buff, sizeof(buff));
if(strcmp(buff, "bar") == 0) {
// TODO REMOVE Be sure to let the BTF know if the test passes or fails!
// printf("PASS\n");
return true;
}
// TODO REMOVE Be sure to let the BTF know if the test passes or fails!
// printf("FAIL\n");
return false;
}

// OPTIONAL: Test teardown. If not defined, there is no teardown
// This is called after ending the test in the BTF
void teardown() {
// Add custom teardown here
}
};

#endif // __CUSTOM_TEST__
```
17 changes: 17 additions & 0 deletions tests/serialTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef __SERIAL_TEST__
#define __SERIAL_TEST__

#include "test.h"


class SerialTest : public Test {
public:
SerialTest(): Test("serial_basic") {};
private:
bool runTest() {
// Simply pass, as signalStart() and signalEnd() will indicate success or failure
return true;
}
};

#endif // __SERIAL_TEST__
118 changes: 118 additions & 0 deletions tests/test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#ifndef __TEST_H__
#define __TEST_H__

#include "mbed.h"
#include "serialWrapper.h"
#include <string>

using namespace std;

// Serial interface for BTF communication
extern SerialWrapper pc;


class Test {
public:
Test(const string name) {
this->testName = name;
}

// Execute the test, including any setup and teardown
bool execute() {
bool ret;
setup();
if(signalStart()) {
ret = runTest();
// Even if the test passed, modify the return value to indicate serial
// communication status. This is particularly useful in verifying the
// success of the initial serial test
ret = signalEnd(ret);
} else {
ret = false;
}
teardown();

return ret;
}
protected:
// Name of the test, as defined in tests.yml
string testName;
private:
// Setup for a given test. Does nothing by default
virtual void setup() {};
// Main test content
virtual bool runTest() = 0;
// Teardown for a given test. Does nothing by default
virtual void teardown() {};

// Signal to the BTF test runner to start the test
bool signalStart() {
// REMOVE bool started = false;
// REMOVE int attempts = 0;
char buff[7];

// Send start message to BTF test runner
printf("START TEST %s\n", testName.c_str());

// Read response from BTF test runner
// REMOVE Need this? memset(buff, '\0', sizeof(buff));
size_t nread = pc.readLine(buff, sizeof(buff));

// The response should be "READY"
return (nread == 5) && (strcmp(buff, "READY") == 0);
/* REMOVE
do {
// TODO Change to readLine with timeout
if(pc.readable()) {
pc.read(buff, sizeof(buff));
started = strncmp(buff, "READY", 5) == 0;
} else {
ThisThread::sleep_for(100);
attempts++;
}
} while(!started && (attempts < 100));
return started;*/
}

// Signal to the BTF test runner to end the test
bool signalEnd(bool result) {
// REMOVE bool ended = false;
// REMOVE int attempts = 0;
char buff[6];

// Send end message to BTF test runner
if(result) {
printf("PASS\n");
} else {
printf("FAIL\n");
}

// Read response from BTF test runner
// REMOVE Need this? memset(buff, '\0', sizeof(buff));
size_t nread = pc.readLine(buff, sizeof(buff));

// The response should be 'DONE'. If so, preserve the test result
return result && (nread == 4) && (strcmp(buff, "DONE") == 0);

/* REMOVE
do {
// TODO Change to readLine with timeout
if(pc.readable()) {
pc.read(buff, sizeof(buff));
ended = strncmp(buff, "DONE", 4) == 0;
} else {
ThisThread::sleep_for(100);
attempts++;
}
} while(!ended && (attempts < 100));
return ended;*/
}
};

#endif // __TEST_H__

0 comments on commit bcfd5db

Please sign in to comment.