improve output of C tests

This commit is contained in:
Dustin J. Mitchell 2022-02-13 21:02:18 +00:00
parent 41a578ab2b
commit ca904d6288
5 changed files with 59 additions and 5 deletions

View file

@ -18,6 +18,11 @@ fn build_libtaskchampion(suites: &[&'static str]) {
build.object(libtaskchampion); build.object(libtaskchampion);
build.include("../lib"); build.include("../lib");
build.include("src/bindings_tests/unity"); build.include("src/bindings_tests/unity");
build.define("UNITY_OUTPUT_CHAR", "test_output");
build.define(
"UNITY_OUTPUT_CHAR_HEADER_DECLARATION",
"test_output(char c)",
);
build.file("src/bindings_tests/unity/unity.c"); build.file("src/bindings_tests/unity/unity.c");
let mut files = vec!["src/bindings_tests/test.c".to_string()]; let mut files = vec!["src/bindings_tests/test.c".to_string()];

View file

@ -1,14 +1,28 @@
use std::fs;
extern "C" {
// set up to send test output to TEST-OUTPUT
fn setup_output();
// close the output file
fn finish_output();
}
// Each suite is represented by a <name>_tests C function in <name>.c. // Each suite is represented by a <name>_tests C function in <name>.c.
// All of these C files are built into a library that is linked to the crate -- but not to test // All of these C files are built into a library that is linked to the crate -- but not to test
// crates. So, this macro produces a "glue function" that calls the C function, and that can be // crates. So, this macro produces a "glue function" that calls the C function, and that can be
// called from test crates. // called from test crates.
macro_rules! suite( macro_rules! suite(
{ $s:ident } => { { $s:ident } => {
pub fn $s() -> i32 { pub fn $s() -> (i32, String) {
extern "C" { extern "C" {
fn $s() -> i32; fn $s() -> i32;
} }
unsafe { $s() } unsafe { setup_output() };
let res = unsafe { $s() };
unsafe { finish_output() };
let output = fs::read_to_string("TEST-OUTPUT")
.unwrap_or_else(|e| format!("could not open TEST-OUTPUT: {}", e));
(res, output)
} }
}; };
); );

View file

@ -1,8 +1,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <string.h> #include <string.h>
#include "unity.h"
#include "taskchampion.h" #include "taskchampion.h"
#include "unity.h"
// creating an in-memory replica does not crash // creating an in-memory replica does not crash
static void test_replica_creation(void) { static void test_replica_creation(void) {

View file

@ -1,6 +1,30 @@
#include <stdio.h>
#include "unity.h" #include "unity.h"
// these functions are shared between all test "suites" // these functions are shared between all test "suites"
// and cannot be customized per-suite. // and cannot be customized per-suite.
void setUp(void) { } void setUp(void) { }
void tearDown(void) { } void tearDown(void) { }
static FILE *output = NULL;
// Set up for test_output, writing output to "TEST-OUTPUT" in the
// current directory. The Rust test harness reads this file to get
// the output and display it only on failure. This is called by
// the Rust test harness
void setup_output(void) {
output = fopen("TEST-OUTPUT", "w");
}
// Close the output file. Called by the Rust test harness.
void finish_output(void) {
fclose(output);
output = NULL;
}
// this replaces UNITY_OUTPUT_CHAR, and writes output to
// TEST-OUTPUT in the current directory; the Rust test harness
// will read this data if the test fails.
void test_output(char c) {
fputc(c, output);
}

View file

@ -1,5 +1,6 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Mutex; use std::sync::Mutex;
use tempfile::TempDir;
lazy_static! { lazy_static! {
// the C library running the tests is not reentrant, so we use a mutex to ensure that only one // the C library running the tests is not reentrant, so we use a mutex to ensure that only one
@ -11,8 +12,18 @@ macro_rules! suite(
{ $s:ident } => { { $s:ident } => {
#[test] #[test]
fn $s() { fn $s() {
let _guard = MUTEX.lock().unwrap(); let tmp_dir = TempDir::new().expect("TempDir failed");
assert_eq!(integration_tests::bindings_tests::$s(), 0); let (res, output) = {
let _guard = MUTEX.lock().unwrap();
// run the tests in the temp dir (NOTE: this must be inside
// the mutex guard!)
std::env::set_current_dir(tmp_dir.as_ref()).unwrap();
integration_tests::bindings_tests::$s()
};
println!("{}", output);
if res != 0 {
assert!(false, "test failed");
}
} }
}; };
); );