Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add build profile for embedded applications #130

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions contrib/embedded/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CGreen can now be built (and even work) in the embedded environments including
microcontrollers (aka MCU)

Building is very easy (for STM32 MCUs):
cmake -DSTM32_CHIP=stm32f103c8 \
-DCMAKE_TOOLCHAIN_FILE=/path/to/stm32-cmake/cmake/gcc_stm32.cmake \
-DCMAKE_MODULE_PATH=/path/to/stm32-cmake/cmake \
-DTOOLCHAIN_PREFIX=/path/to/arm_gcc_with_newlib \
/path/to/cgreen/ && make

That wont build all of CGreen but libcgreen.a is enough.

The Proof-Of-Concept can be found here:
https://github.com/ildar/stm32-tests/tree/xUnit
or: https://github.com/ildar/stm32-tests/commit/29707fd67ce1418fa385371e09888d0ba84809aa
32 changes: 32 additions & 0 deletions include/cgreen/internal/circular_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef CIRCULAR_BUFFER_HEADER
#define CIRCULAR_BUFFER_HEADER

#ifndef CB_TYPE
#define CB_TYPE int
#endif

struct CircularBuffer_ {
CB_TYPE* buffer;
CB_TYPE* buffer_end;
CB_TYPE* head;
CB_TYPE* tail;
};

typedef struct CircularBuffer_ CircularBuffer;

#ifdef __cplusplus
namespace cgreen {
extern "C" {
#endif

CircularBuffer* create_circular_buffer(int len);
void destroy_circular_buffer(CircularBuffer* cb);
int write_to_circular_buffer(CircularBuffer* cb, CB_TYPE data);
int read_from_circular_buffer(CircularBuffer* cb, CB_TYPE* pdata);

#ifdef __cplusplus
}
}
#endif

#endif
11 changes: 11 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ set(cgreen_SRCS
breadcrumb.c
cgreen_time.c
cgreen_value.c
circular_buffer.c
constraint.c
constraint_syntax_helpers.c
cute_reporter.c
Expand Down Expand Up @@ -68,7 +69,17 @@ else(UNIX)
win32_runner_platform.c
)
else(WIN32)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Generic") # embedded systems, like MCUs
set (CGREEN_WITH_STATIC_LIBRARY ON)
# list (REMOVE_ITEM cgreen_SRCS cdash_reporter.c)
LIST(APPEND cgreen_SRCS
circular_buffer_messaging.c
generic_runner_platform.c
)
LIST(REMOVE_ITEM cgreen_SRCS messaging.c)
else() #Generic
message(FATAL_ERROR "cgreen currently only works on UNIX, Cygwin, MacOSX and Windows. Please file a bug.")
endif() #Generic
endif(WIN32)
endif(UNIX)
SET_SOURCE_FILES_PROPERTIES(${cgreen_SRCS} PROPERTIES LANGUAGE C)
Expand Down
45 changes: 45 additions & 0 deletions src/circular_buffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <cgreen/internal/circular_buffer.h>
#include <stdlib.h>

CircularBuffer* create_circular_buffer(int len) {
CircularBuffer* result;

result = malloc(sizeof(CircularBuffer));
if(!result)
return NULL;
result->buffer = malloc((len+1)*sizeof(CB_TYPE));
if(!result) {
free(result);
return NULL;
}
result->buffer_end = result->buffer + (len+1);
result->head = result->tail = result->buffer;
return result;
}

void destroy_circular_buffer(CircularBuffer* cb) {
free(cb->buffer);
cb->buffer = NULL;
free(cb);
}

int write_to_circular_buffer(CircularBuffer* cb, CB_TYPE data) {
CB_TYPE* next = (cb->head+1 != cb->buffer_end) ? cb->head+1 : cb->buffer;
if(next != cb->tail) {
*(cb->head) = data;
cb->head = next;
return 1;
} else
return 0;
}

int read_from_circular_buffer(CircularBuffer* cb, CB_TYPE* pdata) {
if(cb->tail == cb->head)
return 0;
*pdata = *(cb->tail++);
if(cb->tail == cb-> buffer_end)
cb->tail = cb-> buffer;
return 1;
}

/* vim: set ts=4 sw=4 et cindent: */
38 changes: 38 additions & 0 deletions src/circular_buffer_messaging.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <cgreen/messaging.h>
#include <cgreen/internal/circular_buffer.h>
#include <stdlib.h>

#define QUEUE_LENGTH 200
static int queue_count = 0;
static int* queue_tags = NULL;
static CircularBuffer** queues = NULL;

int start_cgreen_messaging(int tag) {
const int queue = queue_count;
queue_tags = realloc(queue_tags, (queue_count+1)*sizeof(int));
queues = realloc(queues, (queue_count+1)*sizeof(CircularBuffer*));
#warning no alloc result checks
queue_tags[queue] = tag;
queues[queue] = create_circular_buffer(QUEUE_LENGTH);
#warning no alloc result checks

queue_count++;
return queue_count - 1;
}

void send_cgreen_message(int messaging, int result) {
write_to_circular_buffer(queues[messaging], result);
#warning FIXME what if the circular buffer is full?
// write_to_circular_buffer(queues[messaging], queue_tags[messaging]);
}

int receive_cgreen_message(int messaging) {
int tag, result;
if (!read_from_circular_buffer(queues[messaging], &result)) {
return 0;
}
// read_from_circular_buffer(queues[messaging], &tag);
return result;
}

/* vim: set ts=4 sw=4 et cindent: */
57 changes: 57 additions & 0 deletions src/generic_runner_platform.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#ifndef __ANDROID__
#include "config.h"
#endif // #ifdef __ANDROID__
#include "runner.h"
#include "cgreen/internal/runner_platform.h"
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <cgreen/internal/cgreen_time.h>

#ifdef __ANDROID__
#include "cgreen/internal/android_headers/androidcompat.h"
#endif // #ifdef __ANDROID__

// FIXME: check for GCC

void run_test_in_its_own_process(TestSuite *suite, CgreenTest *test, TestReporter *reporter) {
uint32_t test_starting_milliseconds = cgreen_time_get_current_milliseconds();

(*reporter->start_test)(reporter, test->name);
if (test->skip) {
send_reporter_skipped_notification(reporter);
(*reporter->finish_test)(reporter, test->filename, test->line, NULL, 0);
} else {
run_the_test_code(suite, test, reporter);
send_reporter_completion_notification(reporter);
uint32_t test_duration = cgreen_time_duration_in_milliseconds(test_starting_milliseconds,
cgreen_time_get_current_milliseconds());

(*reporter->finish_test)(reporter, test->filename, test->line, NULL, test_duration);
}
}

void run_specified_test_if_child(TestSuite *suite, TestReporter *reporter){
//not needed for systems that support fork()
(void)suite;
(void)reporter;
}

uint32_t cgreen_time_get_current_milliseconds() {
// FIXME: fake implementation
static ms=0;
return ++ms;
}

void die_in(unsigned int seconds) {
// FIXME: fake implementation
(void)seconds;
}

/* vim: set ts=4 sw=4 et cindent: */
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set(c_tests_library_SRCS
breadcrumb_tests.c
cdash_reporter_tests.c
cdash_reporter_tests.c
circular_buffer_tests.c
constraint_tests.c
cute_reporter_tests.c
environment_variables_tests.c
Expand Down
2 changes: 2 additions & 0 deletions tests/all_c_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using namespace cgreen;
TestSuite *assertion_tests();
TestSuite *breadcrumb_tests();
TestSuite *cdash_reporter_tests();
TestSuite *circular_buffer_tests();
TestSuite *collector_tests();
TestSuite *constraint_tests();
#ifdef __cplusplus
Expand All @@ -36,6 +37,7 @@ int main(int argc, char **argv) {
add_suite(suite, assertion_tests());
add_suite(suite, breadcrumb_tests());
add_suite(suite, cdash_reporter_tests());
add_suite(suite, circular_buffer_tests());
add_suite(suite, constraint_tests());
#ifdef __cplusplus
add_suite(suite, cpp_assertion_tests());
Expand Down
91 changes: 91 additions & 0 deletions tests/circular_buffer_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include <cgreen/cgreen.h>
#include <cgreen/internal/circular_buffer.h>

#include <stdlib.h>

#ifdef __cplusplus
using namespace cgreen;
#endif

Describe(CircularBuffer);

static CircularBuffer* instance;
static const int length=100;

BeforeEach(CircularBuffer) {
instance = create_circular_buffer(length);
}
AfterEach(CircularBuffer) {
destroy_circular_buffer(instance);
}

Ensure(CircularBuffer, created_and_correctly_initialized) {
assert_that(instance, is_non_null);
assert_that(instance->buffer, is_non_null);
assert_that(instance->buffer_end >= (instance->buffer + length));
assert_that(instance->head, is_equal_to( instance->buffer ));
assert_that(instance->tail, is_equal_to( instance->head ));
}

Ensure(CircularBuffer, accepts_data) {
for(int i=0; i<length; i++)
assert_that(write_to_circular_buffer(instance, i), is_equal_to(1));
assert_that(write_to_circular_buffer(instance, 100), is_equal_to(0)); // full
}

Ensure(CircularBuffer, gives_data_back) {
int c;
for(int i=0; i<50; i++)
write_to_circular_buffer(instance, i*2);
for(int i=0; i<50; i++) {
assert_that(read_from_circular_buffer(instance, &c), is_equal_to(1));
assert_that(c, is_equal_to(i*2));
}
assert_that(read_from_circular_buffer(instance, &c), is_equal_to(0));
}

Ensure(CircularBuffer, is_really_circular) {
int c;
for(int i=0; i<16; i++) {
for(int j=0; j<(length/2+3); j++)
assert_that(write_to_circular_buffer(instance, i*j), is_equal_to(1));
for(int j=0; j<(length/2+3); j++) {
assert_that(read_from_circular_buffer(instance, &c), is_equal_to(1));
assert_that(c, is_equal_to(i*j));
}
assert_that(read_from_circular_buffer(instance, &c), is_equal_to(0));
}
}

Ensure(CircularBuffer, withstands_stress_test) {
int msgs_in_buffer = 0, rx = 0, tx = 0;
int c;
for(int i=0; i<length; i++) {
do
for(int j=0; j<random()/(RAND_MAX/(length-msgs_in_buffer)); j++,msgs_in_buffer++)
assert_that(write_to_circular_buffer(instance, tx++), is_equal_to(1));
while(msgs_in_buffer == 0);

do
for(int j=0; j<random()/(RAND_MAX/msgs_in_buffer); j++,msgs_in_buffer--) {
assert_that(read_from_circular_buffer(instance, &c), is_equal_to(1));
assert_that(c, is_equal_to(rx++));
}
while(msgs_in_buffer == length);
}
}


TestSuite *circular_buffer_tests() {
TestSuite *suite = create_test_suite();

add_test_with_context(suite, CircularBuffer, created_and_correctly_initialized);
add_test_with_context(suite, CircularBuffer, accepts_data);
add_test_with_context(suite, CircularBuffer, gives_data_back);
add_test_with_context(suite, CircularBuffer, is_really_circular);
add_test_with_context(suite, CircularBuffer, withstands_stress_test);

return suite;
}

/* vim: set ts=4 sw=4 et cindent: */
1 change: 1 addition & 0 deletions tests/circular_buffer_tests.cpp