diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 000000000..35049cbcb --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[alias] +xtask = "run --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index b63a0a2de..0f5d620c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,10 +592,29 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.0.68" +name = "cbindgen" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" dependencies = [ "jobserver", ] @@ -1501,6 +1520,24 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "integration-tests" +version = "0.4.1" +dependencies = [ + "actix-rt", + "actix-web", + "anyhow", + "cc", + "env_logger 0.8.4", + "lazy_static", + "log", + "pretty_assertions", + "taskchampion", + "taskchampion-lib", + "taskchampion-sync-server", + "tempfile", +] + [[package]] name = "iovec" version = "0.1.4" @@ -1598,9 +1635,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.97" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" [[package]] name = "libgit2-sys" @@ -2460,21 +2497,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "replica-server-tests" -version = "0.4.1" -dependencies = [ - "actix-rt", - "actix-web", - "anyhow", - "env_logger 0.8.4", - "log", - "pretty_assertions", - "taskchampion", - "taskchampion-sync-server", - "tempfile", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -3008,6 +3030,18 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "taskchampion-lib" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "libc", + "pretty_assertions", + "taskchampion", + "uuid", +] + [[package]] name = "taskchampion-sync-server" version = "0.4.1" @@ -3762,6 +3796,14 @@ dependencies = [ "time 0.1.43", ] +[[package]] +name = "xtask" +version = "0.4.1" +dependencies = [ + "anyhow", + "cbindgen", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index eef6d1ce7..701f3839e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,7 @@ members = [ "taskchampion", "cli", "sync-server", - "replica-server-tests" + "lib", + "integration-tests", + "xtask", ] diff --git a/README.md b/README.md index 10e119460..3622cc50e 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,32 @@ But, if you just want to get some practice with Rust, we'd be happy to have you. ## Structure -There are four crates here: +There are five crates here: * [taskchampion](./taskchampion) - the core of the tool * [taskchampion-cli](./cli) - the command-line binary * [taskchampion-sync-server](./sync-server) - the server against which `task sync` operates - * [replica-server-tests](./replica-server-tests) - integration tests covering both _taskchampion-cli_ and _taskchampion-sync-server_ + * [taskchampion-lib](./lib) - glue code to use _taskchampion_ from C + * [integration-tests](./integration-tests) - integration tests covering _taskchampion-cli_, _taskchampion-sync-server_, and _taskchampion-lib_. + +## Code Generation + +The _taskchampion_lib_ crate uses a bit of code generation to create the `lib/taskchampion.h` header file. +To regenerate this file, run `cargo xtask codegen`. + +## C libraries + +The `taskchampion-lib` crate generates libraries suitable for use from C (or any C-compatible language). + +The necessary bits are: + +* a shared object in `target/$PROFILE/deps` (e.g., `target/debug/deps/libtaskchampion.so`) +* a static library in `target/$PROFILE` (e.g., `target/debug/libtaskchampion.a`) +* a header file, `lib/taskchampion.h`. + +Downstream consumers may use either the static or dynamic library, as they prefer. + +NOTE: on Windows, the "BCrypt" library must be included when linking to taskchampion. ## Documentation Generation diff --git a/integration-tests/.gitignore b/integration-tests/.gitignore new file mode 100644 index 000000000..140a35ffe --- /dev/null +++ b/integration-tests/.gitignore @@ -0,0 +1,2 @@ +test-db +test-sync-server diff --git a/replica-server-tests/Cargo.toml b/integration-tests/Cargo.toml similarity index 51% rename from replica-server-tests/Cargo.toml rename to integration-tests/Cargo.toml index efe369b3c..1d57b1a7d 100644 --- a/replica-server-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,15 +1,14 @@ [package] -name = "replica-server-tests" +name = "integration-tests" version = "0.4.1" authors = ["Dustin J. Mitchell "] edition = "2018" publish = false +build = "build.rs" -[dependencies.taskchampion-sync-server] -path = "../sync-server" - -[dependencies.taskchampion] -path = "../taskchampion" +[dependencies] +taskchampion = { path = "../taskchampion" } +taskchampion-sync-server = { path = "../sync-server" } [dev-dependencies] anyhow = "1.0" @@ -19,3 +18,8 @@ tempfile = "3" pretty_assertions = "1" log = "^0.4.14" env_logger = "^0.8.3" +lazy_static = "1" + +[build-dependencies] +cc = "1.0.73" +taskchampion-lib = { path = "../lib" } diff --git a/integration-tests/README.md b/integration-tests/README.md new file mode 100644 index 000000000..cbec3515a --- /dev/null +++ b/integration-tests/README.md @@ -0,0 +1,30 @@ +# Integration Tests for Taskchampion + +## "Regular" Tests + +Some of the tests in `tests/` are just regular integration tests. +Nothing exciting to see. + +## Bindings Tests + +The bindings tests are a bit more interesting, since they are written in C. +They are composed of a collection of "suites", each in one C file in `integration-tests/src/bindings_tests/`. +Each suite contains a number of tests (using [Unity](http://www.throwtheswitch.org/unity)) and an exported function named after the suite that returns an exit status (1 = failure). + +The build script (`integration-tests/build.rs`) builds these files into a library that is linked with the `integration_tests` library crate. +This crate contains a `bindings_tests` module with a pub function for each suite. + +Finally, the `integration-tests/tests/bindings.rs` test file calls each of those functions in a separate test case. + +### Adding Tests + +To add a test, select a suite and add a new test-case function. +Add a `RUN_TEST` invocation for your new function to the `.._tests` function at the bottom. +Keep the `RUN_TEST`s in the same order as the functions they call. + +### Adding Suites + +To add a suite, + +1. Add a new C file in `integration-tests/src/bindings_tests/`, based off of one of the others. +1. Add a the suite name to `suites` in `integration-tests/build.rs`. diff --git a/integration-tests/build.rs b/integration-tests/build.rs new file mode 100644 index 000000000..90165f6d0 --- /dev/null +++ b/integration-tests/build.rs @@ -0,0 +1,73 @@ +use std::env; +use std::fs; +use std::path::Path; + +/// Link to the libtaskchampion library produced by the `taskchampion-lib` crate. This is done as +/// a build dependency, rather than a cargo dependency, so that the symbols are available to +/// bindings-tests. +fn link_libtaskchampion() { + // This crate has taskchampion-lib in its build-dependencies, so libtaskchampion.so should be + // built already. + // + // Shared libraries (crate-type=cdylib) appear to be placed in target/$PROFILE/deps. + let mut libtc_dir = env::current_dir().unwrap(); + libtc_dir.pop(); + libtc_dir.push("target"); + libtc_dir.push(env::var("PROFILE").unwrap()); + libtc_dir.push("deps"); + + let libtc_dir = libtc_dir.to_str().expect("path is valid utf-8"); + println!("cargo:rustc-link-search={}", libtc_dir); + println!("cargo:rustc-link-lib=dylib=taskchampion"); + + // on windows, it appears that rust std requires BCrypt + if cfg!(target_os = "windows") { + println!("cargo:rustc-link-lib=dylib=bcrypt"); + } +} + +/// Build the Unity-based C test suite in `src/bindings_tests`, linking the result with this +/// package's library crate. +fn build_bindings_tests(suites: &[&'static str]) { + let mut build = cc::Build::new(); + build.include("../lib"); // include path for taskchampion.h + 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"); + + let mut files = vec!["src/bindings_tests/test.c".to_string()]; + for suite in suites { + files.push(format!("src/bindings_tests/{}.c", suite)); + } + for file in files { + build.file(&file); + println!("cargo:rerun-if-changed={}", file); + } + + build.compile("bindings-tests"); +} + +/// Make `bindings_test_suites.rs` listing all of the test suites, for use in building the +/// bindings-test binary. +fn make_suite_file(suites: &[&'static str]) { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("bindings_test_suites.rs"); + let mut content = String::new(); + for suite in suites { + content.push_str(format!("suite!({}_tests);\n", suite).as_ref()); + } + fs::write(&dest_path, content).unwrap(); +} + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let suites = &["uuid", "string", "task", "replica"]; + link_libtaskchampion(); + build_bindings_tests(suites); + make_suite_file(suites); +} diff --git a/integration-tests/src/bindings_tests/mod.rs b/integration-tests/src/bindings_tests/mod.rs new file mode 100644 index 000000000..327ca83fe --- /dev/null +++ b/integration-tests/src/bindings_tests/mod.rs @@ -0,0 +1,30 @@ +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 _tests C function in .c. +// 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 +// called from test crates. +macro_rules! suite( + { $s:ident } => { + pub fn $s() -> (i32, String) { + extern "C" { + fn $s() -> i32; + } + 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) + } + }; +); + +include!(concat!(env!("OUT_DIR"), "/bindings_test_suites.rs")); diff --git a/integration-tests/src/bindings_tests/replica.c b/integration-tests/src/bindings_tests/replica.c new file mode 100644 index 000000000..88651fc45 --- /dev/null +++ b/integration-tests/src/bindings_tests/replica.c @@ -0,0 +1,328 @@ +#include +#include +#include +#include "taskchampion.h" +#include "unity.h" + +// creating an in-memory replica does not crash +static void test_replica_creation(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NOT_NULL(rep); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + tc_replica_free(rep); +} + +// creating an on-disk replica does not crash +static void test_replica_creation_disk(void) { + TCReplica *rep = tc_replica_new_on_disk(tc_string_borrow("test-db"), NULL); + TEST_ASSERT_NOT_NULL(rep); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + tc_replica_free(rep); +} + +// undo on an empty in-memory TCReplica does nothing +static void test_replica_undo_empty(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + int undone; + int rv = tc_replica_undo(rep, &undone); + TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); + TEST_ASSERT_EQUAL(0, undone); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + tc_replica_free(rep); +} + +// adding an undo point succeeds +static void test_replica_add_undo_point(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_add_undo_point(rep, true)); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + tc_replica_free(rep); +} + +// working set operations succeed +static void test_replica_working_set(void) { + TCWorkingSet *ws; + TCTask *task1, *task2, *task3; + TCUuid uuid, uuid1, uuid2, uuid3; + + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, true)); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + ws = tc_replica_working_set(rep); + TEST_ASSERT_EQUAL(0, tc_working_set_len(ws)); + tc_working_set_free(ws); + + task1 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task1")); + TEST_ASSERT_NOT_NULL(task1); + uuid1 = tc_task_get_uuid(task1); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, true)); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + task2 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task2")); + TEST_ASSERT_NOT_NULL(task2); + uuid2 = tc_task_get_uuid(task2); + + task3 = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("task3")); + TEST_ASSERT_NOT_NULL(task3); + uuid3 = tc_task_get_uuid(task3); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, false)); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + // finish task2 to leave a "hole" + tc_task_to_mut(task2, rep); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_done(task2)); + tc_task_to_immut(task2); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_replica_rebuild_working_set(rep, false)); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + tc_task_free(task1); + tc_task_free(task2); + tc_task_free(task3); + + // working set should now be + // 0 -> None + // 1 -> uuid1 + // 2 -> None + // 3 -> uuid3 + ws = tc_replica_working_set(rep); + TEST_ASSERT_EQUAL(2, tc_working_set_len(ws)); + TEST_ASSERT_EQUAL(3, tc_working_set_largest_index(ws)); + + TEST_ASSERT_FALSE(tc_working_set_by_index(ws, 0, &uuid)); + TEST_ASSERT_TRUE(tc_working_set_by_index(ws, 1, &uuid)); + TEST_ASSERT_EQUAL_MEMORY(uuid1.bytes, uuid.bytes, sizeof(uuid)); + TEST_ASSERT_FALSE(tc_working_set_by_index(ws, 2, &uuid)); + TEST_ASSERT_TRUE(tc_working_set_by_index(ws, 3, &uuid)); + TEST_ASSERT_EQUAL_MEMORY(uuid3.bytes, uuid.bytes, sizeof(uuid)); + + TEST_ASSERT_EQUAL(1, tc_working_set_by_uuid(ws, uuid1)); + TEST_ASSERT_EQUAL(0, tc_working_set_by_uuid(ws, uuid2)); + TEST_ASSERT_EQUAL(3, tc_working_set_by_uuid(ws, uuid3)); + + tc_working_set_free(ws); + + tc_replica_free(rep); +} + +// When tc_replica_undo is passed NULL for undone_out, it still succeeds +static void test_replica_undo_empty_null_undone_out(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + int rv = tc_replica_undo(rep, NULL); + TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + tc_replica_free(rep); +} + +// creating a task succeeds and the resulting task looks good +static void test_replica_task_creation(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TCUuid uuid = tc_task_get_uuid(task); + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + TCString desc = tc_task_get_description(task); + TEST_ASSERT_NOT_NULL(desc.ptr); + TEST_ASSERT_EQUAL_STRING("my task", tc_string_content(&desc)); + tc_string_free(&desc); + + tc_task_free(task); + + // get the task again and verify it + task = tc_replica_get_task(rep, uuid); + TEST_ASSERT_NOT_NULL(task); + TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// When tc_replica_undo is passed NULL for undone_out, it still succeeds +static void test_replica_sync_local(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + mkdir("test-sync-server", 0755); // ignore error, if dir already exists + + TCString err; + TCServer *server = tc_server_new_local(tc_string_borrow("test-sync-server"), &err); + TEST_ASSERT_NOT_NULL(server); + TEST_ASSERT_NULL(err.ptr); + + int rv = tc_replica_sync(rep, server, false); + TEST_ASSERT_EQUAL(TC_RESULT_OK, rv); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + tc_server_free(server); + tc_replica_free(rep); + + // test error handling + server = tc_server_new_local(tc_string_borrow("/no/such/directory"), &err); + TEST_ASSERT_NULL(server); + TEST_ASSERT_NOT_NULL(err.ptr); + tc_string_free(&err); +} + +// When tc_replica_undo is passed NULL for undone_out, it still succeeds +static void test_replica_remote_server(void) { + TCString err; + TCServer *server = tc_server_new_remote( + tc_string_borrow("tc.freecinc.com"), + tc_uuid_new_v4(), + tc_string_borrow("\xf0\x28\x8c\x28"), // NOTE: not utf-8 + &err); + TEST_ASSERT_NOT_NULL(server); + TEST_ASSERT_NULL(err.ptr); + + // can't actually do anything with this server! + + tc_server_free(server); +} + +// a replica with tasks in it returns an appropriate list of tasks and list of uuids +static void test_replica_all_tasks(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task1 = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("task1")); + TEST_ASSERT_NOT_NULL(task1); + TCUuid uuid1 = tc_task_get_uuid(task1); + tc_task_free(task1); + + TCTask *task2 = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("task2")); + TEST_ASSERT_NOT_NULL(task2); + TCUuid uuid2 = tc_task_get_uuid(task2); + tc_task_free(task2); + + { + TCTaskList tasks = tc_replica_all_tasks(rep); + TEST_ASSERT_NOT_NULL(tasks.items); + TEST_ASSERT_EQUAL(2, tasks.len); + + bool seen1, seen2 = false; + for (size_t i = 0; i < tasks.len; i++) { + TCTask *task = tasks.items[i]; + TCString descr = tc_task_get_description(task); + if (0 == strcmp(tc_string_content(&descr), "task1")) { + seen1 = true; + } else if (0 == strcmp(tc_string_content(&descr), "task2")) { + seen2 = true; + } + tc_string_free(&descr); + } + TEST_ASSERT_TRUE(seen1); + TEST_ASSERT_TRUE(seen2); + + tc_task_list_free(&tasks); + TEST_ASSERT_NULL(tasks.items); + } + + { + TCUuidList uuids = tc_replica_all_task_uuids(rep); + TEST_ASSERT_NOT_NULL(uuids.items); + TEST_ASSERT_EQUAL(2, uuids.len); + + bool seen1, seen2 = false; + for (size_t i = 0; i < uuids.len; i++) { + TCUuid uuid = uuids.items[i]; + if (0 == memcmp(&uuid1, &uuid, sizeof(TCUuid))) { + seen1 = true; + } else if (0 == memcmp(&uuid2, &uuid, sizeof(TCUuid))) { + seen2 = true; + } + } + TEST_ASSERT_TRUE(seen1); + TEST_ASSERT_TRUE(seen2); + + tc_uuid_list_free(&uuids); + TEST_ASSERT_NULL(uuids.items); + } + + tc_replica_free(rep); +} + +// importing a task succeeds and the resulting task looks good +static void test_replica_task_import(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCUuid uuid; + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow("23cb25e0-5d1a-4932-8131-594ac6d3a843"), &uuid)); + TCTask *task = tc_replica_import_task_with_uuid(rep, uuid); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + TCString desc = tc_task_get_description(task); + TEST_ASSERT_NOT_NULL(desc.ptr); + TEST_ASSERT_EQUAL_STRING("", tc_string_content(&desc)); // default value + tc_string_free(&desc); + + tc_task_free(task); + + // get the task again and verify it + task = tc_replica_get_task(rep, uuid); + TEST_ASSERT_NOT_NULL(task); + TEST_ASSERT_EQUAL_MEMORY(uuid.bytes, tc_task_get_uuid(task).bytes, sizeof(uuid.bytes)); + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// importing a task succeeds and the resulting task looks good +static void test_replica_get_task_not_found(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCUuid uuid; + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow("23cb25e0-5d1a-4932-8131-594ac6d3a843"), &uuid)); + TCTask *task = tc_replica_get_task(rep, uuid); + TEST_ASSERT_NULL(task); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + tc_replica_free(rep); +} + +int replica_tests(void) { + UNITY_BEGIN(); + // each test case above should be named here, in order. + RUN_TEST(test_replica_creation); + RUN_TEST(test_replica_creation_disk); + RUN_TEST(test_replica_undo_empty); + RUN_TEST(test_replica_add_undo_point); + RUN_TEST(test_replica_working_set); + RUN_TEST(test_replica_undo_empty_null_undone_out); + RUN_TEST(test_replica_task_creation); + RUN_TEST(test_replica_sync_local); + RUN_TEST(test_replica_remote_server); + RUN_TEST(test_replica_all_tasks); + RUN_TEST(test_replica_task_import); + RUN_TEST(test_replica_get_task_not_found); + return UNITY_END(); +} diff --git a/integration-tests/src/bindings_tests/string.c b/integration-tests/src/bindings_tests/string.c new file mode 100644 index 000000000..2bd2749c0 --- /dev/null +++ b/integration-tests/src/bindings_tests/string.c @@ -0,0 +1,125 @@ +#include +#include +#include "unity.h" +#include "taskchampion.h" + +// creating strings does not crash +static void test_string_creation(void) { + TCString s = tc_string_borrow("abcdef"); + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// creating cloned strings does not crash +static void test_string_cloning(void) { + char *abcdef = strdup("abcdef"); + TCString s = tc_string_clone(abcdef); + TEST_ASSERT_NOT_NULL(s.ptr); + free(abcdef); + + TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// creating cloned strings with invalid utf-8 does not crash +// ..but content is NULL and content_and_len returns the value +static void test_string_cloning_invalid_utf8(void) { + TCString s = tc_string_clone("\xf0\x28\x8c\x28"); + TEST_ASSERT_NOT_NULL(s.ptr); + + // NOTE: this is not one of the cases where invalid UTF-8 results in NULL, + // but that may change. + + size_t len; + const char *buf = tc_string_content_with_len(&s, &len); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_EQUAL(4, len); + TEST_ASSERT_EQUAL_MEMORY("\xf0\x28\x8c\x28", buf, len); + + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// borrowed strings echo back their content +static void test_string_borrowed_strings_echo(void) { + TCString s = tc_string_borrow("abcdef"); + TEST_ASSERT_NOT_NULL(s.ptr); + + TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); + + size_t len; + const char *buf = tc_string_content_with_len(&s, &len); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_EQUAL(6, len); + TEST_ASSERT_EQUAL_MEMORY("abcdef", buf, len); + + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// cloned strings echo back their content +static void test_string_cloned_strings_echo(void) { + char *orig = strdup("abcdef"); + TCString s = tc_string_clone(orig); + TEST_ASSERT_NOT_NULL(s.ptr); + free(orig); + + TEST_ASSERT_EQUAL_STRING("abcdef", tc_string_content(&s)); + + size_t len; + const char *buf = tc_string_content_with_len(&s, &len); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_EQUAL(6, len); + TEST_ASSERT_EQUAL_MEMORY("abcdef", buf, len); + + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// tc_clone_with_len can have NULs, and tc_string_content returns NULL for +// strings containing embedded NULs +static void test_string_content_null_for_embedded_nuls(void) { + TCString s = tc_string_clone_with_len("ab\0de", 5); + TEST_ASSERT_NOT_NULL(s.ptr); + + TEST_ASSERT_NULL(tc_string_content(&s)); + + size_t len; + const char *buf = tc_string_content_with_len(&s, &len); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_EQUAL(5, len); + TEST_ASSERT_EQUAL_MEMORY("ab\0de", buf, len); + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +// tc_string_clone_with_len will accept invalid utf-8, but then tc_string_content +// returns NULL. +static void test_string_clone_with_len_invalid_utf8(void) { + TCString s = tc_string_clone_with_len("\xf0\x28\x8c\x28", 4); + TEST_ASSERT_NOT_NULL(s.ptr); + + TEST_ASSERT_NULL(tc_string_content(&s)); + + size_t len; + const char *buf = tc_string_content_with_len(&s, &len); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_EQUAL(4, len); + TEST_ASSERT_EQUAL_MEMORY("\xf0\x28\x8c\x28", buf, len); + tc_string_free(&s); + TEST_ASSERT_NULL(s.ptr); +} + +int string_tests(void) { + UNITY_BEGIN(); + // each test case above should be named here, in order. + RUN_TEST(test_string_creation); + RUN_TEST(test_string_cloning); + RUN_TEST(test_string_cloning_invalid_utf8); + RUN_TEST(test_string_borrowed_strings_echo); + RUN_TEST(test_string_cloned_strings_echo); + RUN_TEST(test_string_content_null_for_embedded_nuls); + RUN_TEST(test_string_clone_with_len_invalid_utf8); + return UNITY_END(); +} diff --git a/integration-tests/src/bindings_tests/task.c b/integration-tests/src/bindings_tests/task.c new file mode 100644 index 000000000..f3ff29ae0 --- /dev/null +++ b/integration-tests/src/bindings_tests/task.c @@ -0,0 +1,582 @@ +#include +#include +#include "unity.h" +#include "taskchampion.h" + +// creating a task succeeds and the resulting task looks good +static void test_task_creation(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + TCString desc = tc_task_get_description(task); + TEST_ASSERT_NOT_NULL(desc.ptr); + TEST_ASSERT_EQUAL_STRING("my task", tc_string_content(&desc)); + tc_string_free(&desc); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// freeing a mutable task works, marking it immutable +static void test_task_free_mutable_task(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + TCUuid uuid = tc_task_get_uuid(task); + + tc_task_to_mut(task, rep); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); + TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); + + tc_task_free(task); // implicitly converts to immut + + task = tc_replica_get_task(rep, uuid); + TEST_ASSERT_NOT_NULL(task); + TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); + tc_task_free(task); + + tc_replica_free(rep); +} + +// updating status on a task works +static void test_task_get_set_status(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + + tc_task_to_mut(task, rep); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_status(task, TC_STATUS_DELETED)); + TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while mut + tc_task_to_immut(task); + TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); // while immut + + tc_task_free(task); + + tc_replica_free(rep); +} + +// updating description on a task works +static void test_task_get_set_description(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TCString desc; + + tc_task_to_mut(task, rep); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_description(task, tc_string_borrow("updated"))); + + desc = tc_task_get_description(task); + TEST_ASSERT_NOT_NULL(desc.ptr); + TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&desc)); + tc_string_free(&desc); + + tc_task_to_immut(task); + + desc = tc_task_get_description(task); + TEST_ASSERT_NOT_NULL(desc.ptr); + TEST_ASSERT_EQUAL_STRING("updated", tc_string_content(&desc)); + tc_string_free(&desc); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// updating entry on a task works +static void test_task_get_set_entry(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + // creation of a task sets entry to current time + TEST_ASSERT_NOT_EQUAL(0, tc_task_get_entry(task)); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_entry(task, 1643679997)); + TEST_ASSERT_EQUAL(1643679997, tc_task_get_entry(task)); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_entry(task, 0)); + TEST_ASSERT_EQUAL(0, tc_task_get_entry(task)); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// updating wait on a task works +static void test_task_get_set_wait_and_is_waiting(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + // wait is not set on creation + TEST_ASSERT_EQUAL(0, tc_task_get_wait(task)); + TEST_ASSERT_FALSE(tc_task_is_waiting(task)); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 3643679997)); // 2085 + TEST_ASSERT_EQUAL(3643679997, tc_task_get_wait(task)); + TEST_ASSERT_TRUE(tc_task_is_waiting(task)); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 643679997)); // THE PAST! + TEST_ASSERT_EQUAL(643679997, tc_task_get_wait(task)); + TEST_ASSERT_FALSE(tc_task_is_waiting(task)); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 0)); + TEST_ASSERT_EQUAL(0, tc_task_get_wait(task)); + TEST_ASSERT_FALSE(tc_task_is_waiting(task)); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// updating modified on a task works +static void test_task_get_set_modified(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + // creation of a task sets modified to current time + TEST_ASSERT_NOT_EQUAL(0, tc_task_get_modified(task)); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_modified(task, 1643679997)); + TEST_ASSERT_EQUAL(1643679997, tc_task_get_modified(task)); + + // zero is not allowed + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_set_modified(task, 0)); + + tc_task_free(task); + + tc_replica_free(rep); +} + +// starting and stopping a task works, as seen by tc_task_is_active +static void test_task_start_stop_is_active(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TEST_ASSERT_FALSE(tc_task_is_active(task)); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_FALSE(tc_task_is_active(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_start(task)); + TEST_ASSERT_TRUE(tc_task_is_active(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_stop(task)); + TEST_ASSERT_FALSE(tc_task_is_active(task)); + + tc_task_free(task); + tc_replica_free(rep); +} + +// tc_task_done and delete work and set the status +static void test_task_done_and_delete(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_STATUS_PENDING, tc_task_get_status(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_done(task)); + TEST_ASSERT_EQUAL(TC_STATUS_COMPLETED, tc_task_get_status(task)); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_delete(task)); + TEST_ASSERT_EQUAL(TC_STATUS_DELETED, tc_task_get_status(task)); + + tc_task_free(task); + tc_replica_free(rep); +} + +// adding and removing tags to a task works, and invalid tags are rejected +static void test_task_add_remove_has_tag(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_FALSE(tc_task_has_tag(task, tc_string_borrow("next"))); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); + TEST_ASSERT_NULL(tc_task_error(task).ptr); + + TEST_ASSERT_TRUE(tc_task_has_tag(task, tc_string_borrow("next"))); + + // invalid - synthetic tag + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("PENDING"))); + TCString err = tc_task_error(task); + TEST_ASSERT_NOT_NULL(err.ptr); + tc_string_free(&err); + + // invald - not a valid tag string + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("my tag"))); + err = tc_task_error(task); + TEST_ASSERT_NOT_NULL(err.ptr); + tc_string_free(&err); + + // invald - not utf-8 + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_task_add_tag(task, tc_string_borrow("\xf0\x28\x8c\x28"))); + err = tc_task_error(task); + TEST_ASSERT_NOT_NULL(err.ptr); + tc_string_free(&err); + + TEST_ASSERT_TRUE(tc_task_has_tag(task, tc_string_borrow("next"))); + + // remove the tag + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_remove_tag(task, tc_string_borrow("next"))); + TEST_ASSERT_NULL(tc_task_error(task).ptr); + + TEST_ASSERT_FALSE(tc_task_has_tag(task, tc_string_borrow("next"))); + + tc_task_free(task); + tc_replica_free(rep); +} + +// get_tags returns the list of tags +static void test_task_get_tags(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); + + TCStringList tags = tc_task_get_tags(task); + + int found_pending = false, found_next = false; + for (size_t i = 0; i < tags.len; i++) { + if (strcmp("PENDING", tc_string_content(&tags.items[i])) == 0) { + found_pending = true; + } + if (strcmp("next", tc_string_content(&tags.items[i])) == 0) { + found_next = true; + } + } + TEST_ASSERT_TRUE(found_pending); + TEST_ASSERT_TRUE(found_next); + + tc_string_list_free(&tags); + TEST_ASSERT_NULL(tags.items); + + tc_task_free(task); + tc_replica_free(rep); +} + +// annotation manipulation (add, remove, list, free) +static void test_task_annotations(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + TCAnnotationList anns = tc_task_get_annotations(task); + TEST_ASSERT_EQUAL(0, anns.len); + TEST_ASSERT_NOT_NULL(anns.items); + tc_annotation_list_free(&anns); + + tc_task_to_mut(task, rep); + + TCAnnotation ann; + + ann.entry = 1644623411; + ann.description = tc_string_borrow("ann1"); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); + TEST_ASSERT_NULL(ann.description.ptr); + + ann.entry = 1644623422; + ann.description = tc_string_borrow("ann2"); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); + TEST_ASSERT_NULL(ann.description.ptr); + + anns = tc_task_get_annotations(task); + + int found1 = false, found2 = false; + for (size_t i = 0; i < anns.len; i++) { + if (0 == strcmp("ann1", tc_string_content(&anns.items[i].description))) { + TEST_ASSERT_EQUAL(anns.items[i].entry, 1644623411); + found1 = true; + } + if (0 == strcmp("ann2", tc_string_content(&anns.items[i].description))) { + TEST_ASSERT_EQUAL(anns.items[i].entry, 1644623422); + found2 = true; + } + } + TEST_ASSERT_TRUE(found1); + TEST_ASSERT_TRUE(found2); + + tc_annotation_list_free(&anns); + TEST_ASSERT_NULL(anns.items); + + tc_task_free(task); + tc_replica_free(rep); +} + +// UDA manipulation (add, remove, list, free) +static void test_task_udas(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task( + rep, + TC_STATUS_PENDING, + tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + tc_task_to_mut(task, rep); + + TCString value; + TCUdaList udas; + + TEST_ASSERT_NULL(tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")).ptr); + TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); + + udas = tc_task_get_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(0, udas.len); + tc_uda_list_free(&udas); + + udas = tc_task_get_legacy_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(0, udas.len); + tc_uda_list_free(&udas); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_set_uda(task, + tc_string_borrow("ns"), + tc_string_borrow("u1"), + tc_string_borrow("vvv"))); + + value = tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")); + TEST_ASSERT_NOT_NULL(value.ptr); + TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&value)); + tc_string_free(&value); + TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); + + udas = tc_task_get_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(1, udas.len); + TEST_ASSERT_EQUAL_STRING("ns", tc_string_content(&udas.items[0].ns)); + TEST_ASSERT_EQUAL_STRING("u1", tc_string_content(&udas.items[0].key)); + TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&udas.items[0].value)); + tc_uda_list_free(&udas); + + udas = tc_task_get_legacy_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(1, udas.len); + TEST_ASSERT_NULL(udas.items[0].ns.ptr); + TEST_ASSERT_EQUAL_STRING("ns.u1", tc_string_content(&udas.items[0].key)); + TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&udas.items[0].value)); + tc_uda_list_free(&udas); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_set_legacy_uda(task, + tc_string_borrow("leg1"), + tc_string_borrow("legv"))); + + value = tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")); + TEST_ASSERT_NOT_NULL(value.ptr); + TEST_ASSERT_EQUAL_STRING("vvv", tc_string_content(&value)); + tc_string_free(&value); + + value = tc_task_get_legacy_uda(task, tc_string_borrow("leg1")); + TEST_ASSERT_NOT_NULL(value.ptr); + TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&value)); + tc_string_free(&value); + + udas = tc_task_get_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(2, udas.len); + tc_uda_list_free(&udas); + + udas = tc_task_get_legacy_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(2, udas.len); + tc_uda_list_free(&udas); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_remove_uda(task, + tc_string_borrow("ns"), + tc_string_borrow("u1"))); + + TEST_ASSERT_NULL(tc_task_get_uda(task, tc_string_borrow("ns"), tc_string_borrow("u1")).ptr); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_remove_uda(task, + tc_string_borrow("ns"), + tc_string_borrow("u1"))); + + udas = tc_task_get_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(1, udas.len); + TEST_ASSERT_EQUAL_STRING("", tc_string_content(&udas.items[0].ns)); + TEST_ASSERT_EQUAL_STRING("leg1", tc_string_content(&udas.items[0].key)); + TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&udas.items[0].value)); + tc_uda_list_free(&udas); + + udas = tc_task_get_legacy_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(1, udas.len); + TEST_ASSERT_NULL(udas.items[0].ns.ptr); + TEST_ASSERT_EQUAL_STRING("leg1", tc_string_content(&udas.items[0].key)); + TEST_ASSERT_EQUAL_STRING("legv", tc_string_content(&udas.items[0].value)); + tc_uda_list_free(&udas); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_remove_legacy_uda(task, + tc_string_borrow("leg1"))); + + TEST_ASSERT_NULL(tc_task_get_legacy_uda(task, tc_string_borrow("leg1")).ptr); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, + tc_task_remove_legacy_uda(task, + tc_string_borrow("leg1"))); + + udas = tc_task_get_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(0, udas.len); + tc_uda_list_free(&udas); + + udas = tc_task_get_legacy_udas(task); + TEST_ASSERT_NOT_NULL(udas.items); + TEST_ASSERT_EQUAL(0, udas.len); + tc_uda_list_free(&udas); + + tc_task_free(task); + tc_replica_free(rep); +} + +static void tckvlist_assert_key(TCKVList *list, char *key, char *value) { + TEST_ASSERT_NOT_NULL(list); + for (size_t i = 0; i < list->len; i++) { + if (0 == strcmp(tc_string_content(&list->items[i].key), key)) { + TEST_ASSERT_EQUAL_STRING(value, tc_string_content(&list->items[i].value)); + return; + } + } + TEST_FAIL_MESSAGE("key not found"); +} + +// get_tags returns the list of tags +static void test_task_taskmap(void) { + TCReplica *rep = tc_replica_new_in_memory(); + TEST_ASSERT_NULL(tc_replica_error(rep).ptr); + + TCTask *task = tc_replica_new_task(rep, TC_STATUS_PENDING, tc_string_borrow("my task")); + TEST_ASSERT_NOT_NULL(task); + + tc_task_to_mut(task, rep); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_tag(task, tc_string_borrow("next"))); + + TCAnnotation ann; + ann.entry = 1644623411; + ann.description = tc_string_borrow("ann1"); + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_add_annotation(task, &ann)); + + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_task_set_wait(task, 3643679997)); // 2085 + + TCKVList taskmap = tc_task_get_taskmap(task); + tckvlist_assert_key(&taskmap, "annotation_1644623411", "ann1"); + tckvlist_assert_key(&taskmap, "tag_next", ""); + tckvlist_assert_key(&taskmap, "status", "pending"); + tckvlist_assert_key(&taskmap, "description", "my task"); + tc_kv_list_free(&taskmap); + + tc_task_free(task); + tc_replica_free(rep); +} + +int task_tests(void) { + UNITY_BEGIN(); + // each test case above should be named here, in order. + RUN_TEST(test_task_creation); + RUN_TEST(test_task_free_mutable_task); + RUN_TEST(test_task_get_set_status); + RUN_TEST(test_task_get_set_description); + RUN_TEST(test_task_get_set_entry); + RUN_TEST(test_task_get_set_modified); + RUN_TEST(test_task_get_set_wait_and_is_waiting); + RUN_TEST(test_task_start_stop_is_active); + RUN_TEST(test_task_done_and_delete); + RUN_TEST(test_task_add_remove_has_tag); + RUN_TEST(test_task_get_tags); + RUN_TEST(test_task_annotations); + RUN_TEST(test_task_udas); + RUN_TEST(test_task_taskmap); + return UNITY_END(); +} diff --git a/integration-tests/src/bindings_tests/test.c b/integration-tests/src/bindings_tests/test.c new file mode 100644 index 000000000..5afa236ca --- /dev/null +++ b/integration-tests/src/bindings_tests/test.c @@ -0,0 +1,30 @@ +#include +#include "unity.h" + +// these functions are shared between all test "suites" +// and cannot be customized per-suite. +void setUp(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); +} diff --git a/integration-tests/src/bindings_tests/unity/LICENSE.txt b/integration-tests/src/bindings_tests/unity/LICENSE.txt new file mode 100644 index 000000000..b9a329dde --- /dev/null +++ b/integration-tests/src/bindings_tests/unity/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/integration-tests/src/bindings_tests/unity/README.md b/integration-tests/src/bindings_tests/unity/README.md new file mode 100644 index 000000000..6f755ced0 --- /dev/null +++ b/integration-tests/src/bindings_tests/unity/README.md @@ -0,0 +1,3 @@ +# Unity + +This directory contains the src from https://github.com/ThrowTheSwitch/Unity, revision 8ba01386008196a92ef4fdbdb0b00f2434c79563. diff --git a/integration-tests/src/bindings_tests/unity/unity.c b/integration-tests/src/bindings_tests/unity/unity.c new file mode 100644 index 000000000..b88024875 --- /dev/null +++ b/integration-tests/src/bindings_tests/unity/unity.c @@ -0,0 +1,2119 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#include "unity.h" +#include + +#ifdef AVR +#include +#else +#define PROGMEM +#endif + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +void UNITY_OUTPUT_CHAR(int); +#endif + +/* Helpful macros for us to use here in Assert functions */ +#define UNITY_FAIL_AND_BAIL do { Unity.CurrentTestFailed = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) +#define UNITY_IGNORE_AND_BAIL do { Unity.CurrentTestIgnored = 1; UNITY_OUTPUT_FLUSH(); TEST_ABORT(); } while (0) +#define RETURN_IF_FAIL_OR_IGNORE do { if (Unity.CurrentTestFailed || Unity.CurrentTestIgnored) { TEST_ABORT(); } } while (0) + +struct UNITY_STORAGE_T Unity; + +#ifdef UNITY_OUTPUT_COLOR +const char PROGMEM UnityStrOk[] = "\033[42mOK\033[00m"; +const char PROGMEM UnityStrPass[] = "\033[42mPASS\033[00m"; +const char PROGMEM UnityStrFail[] = "\033[41mFAIL\033[00m"; +const char PROGMEM UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; +#else +const char PROGMEM UnityStrOk[] = "OK"; +const char PROGMEM UnityStrPass[] = "PASS"; +const char PROGMEM UnityStrFail[] = "FAIL"; +const char PROGMEM UnityStrIgnore[] = "IGNORE"; +#endif +static const char PROGMEM UnityStrNull[] = "NULL"; +static const char PROGMEM UnityStrSpacer[] = ". "; +static const char PROGMEM UnityStrExpected[] = " Expected "; +static const char PROGMEM UnityStrWas[] = " Was "; +static const char PROGMEM UnityStrGt[] = " to be greater than "; +static const char PROGMEM UnityStrLt[] = " to be less than "; +static const char PROGMEM UnityStrOrEqual[] = "or equal to "; +static const char PROGMEM UnityStrNotEqual[] = " to be not equal to "; +static const char PROGMEM UnityStrElement[] = " Element "; +static const char PROGMEM UnityStrByte[] = " Byte "; +static const char PROGMEM UnityStrMemory[] = " Memory Mismatch."; +static const char PROGMEM UnityStrDelta[] = " Values Not Within Delta "; +static const char PROGMEM UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char PROGMEM UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char PROGMEM UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +#ifndef UNITY_EXCLUDE_FLOAT +static const char PROGMEM UnityStrNot[] = "Not "; +static const char PROGMEM UnityStrInf[] = "Infinity"; +static const char PROGMEM UnityStrNegInf[] = "Negative Infinity"; +static const char PROGMEM UnityStrNaN[] = "NaN"; +static const char PROGMEM UnityStrDet[] = "Determinate"; +static const char PROGMEM UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +#endif +const char PROGMEM UnityStrErrShorthand[] = "Unity Shorthand Support Disabled"; +const char PROGMEM UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char PROGMEM UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char PROGMEM UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char PROGMEM UnityStrBreaker[] = "-----------------------"; +static const char PROGMEM UnityStrResultsTests[] = " Tests "; +static const char PROGMEM UnityStrResultsFailures[] = " Failures "; +static const char PROGMEM UnityStrResultsIgnored[] = " Ignored "; +#ifndef UNITY_EXCLUDE_DETAILS +static const char PROGMEM UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char PROGMEM UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; +#endif +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +/* Local helper function to print characters. */ +static void UnityPrintChar(const char* pch) +{ + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } +} + +/*-----------------------------------------------*/ +/* Local helper function to print ANSI escape strings e.g. "\033[42m". */ +#ifdef UNITY_OUTPUT_COLOR +static UNITY_UINT UnityPrintAnsiEscapeString(const char* string) +{ + const char* pch = string; + UNITY_UINT count = 0; + + while (*pch && (*pch != 'm')) + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + count++; + } + UNITY_OUTPUT_CHAR('m'); + count++; + + return count; +} +#endif + +/*-----------------------------------------------*/ +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + UnityPrintChar(pch); + pch++; + } + } +} +/*-----------------------------------------------*/ +void UnityPrintLen(const char* string, const UNITY_UINT32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && ((UNITY_UINT32)(pch - string) < length)) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (style == UNITY_DISPLAY_STYLE_CHAR) + { + /* printable characters plus CR & LF are printed */ + UNITY_OUTPUT_CHAR('\''); + if ((number <= 126) && (number >= 32)) + { + UNITY_OUTPUT_CHAR((int)number); + } + /* write escaped carriage returns */ + else if (number == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (number == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 2); + } + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrintNumber(number); + } + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned((UNITY_UINT)number); + } + else + { + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, (char)((style & 0xF) * 2)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const UNITY_INT number_to_print) +{ + UNITY_UINT number = (UNITY_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (~number) + 1; + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const UNITY_UINT number) +{ + UNITY_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print) +{ + int nibble; + char nibbles = nibbles_to_print; + + if ((unsigned)nibbles > UNITY_MAX_NIBBLES) + { + nibbles = UNITY_MAX_NIBBLES; + } + + while (nibbles > 0) + { + nibbles--; + nibble = (int)(number >> (nibbles * 4)) & 0x0F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) +{ + UNITY_UINT current_bit = (UNITY_UINT)1 << (UNITY_INT_WIDTH - 1); + UNITY_INT32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +/* + * This function prints a floating-point value in a format similar to + * printf("%.7g") on a single-precision machine or printf("%.9g") on a + * double-precision machine. The 7th digit won't always be totally correct + * in single-precision operation (for that level of accuracy, a more + * complicated algorithm would be needed). + */ +void UnityPrintFloat(const UNITY_DOUBLE input_number) +{ +#ifdef UNITY_INCLUDE_DOUBLE + static const int sig_digits = 9; + static const UNITY_INT32 min_scaled = 100000000; + static const UNITY_INT32 max_scaled = 1000000000; +#else + static const int sig_digits = 7; + static const UNITY_INT32 min_scaled = 1000000; + static const UNITY_INT32 max_scaled = 10000000; +#endif + + UNITY_DOUBLE number = input_number; + + /* print minus sign (does not handle negative zero) */ + if (number < 0.0f) + { + UNITY_OUTPUT_CHAR('-'); + number = -number; + } + + /* handle zero, NaN, and +/- infinity */ + if (number == 0.0f) + { + UnityPrint("0"); + } + else if (isnan(number)) + { + UnityPrint("nan"); + } + else if (isinf(number)) + { + UnityPrint("inf"); + } + else + { + UNITY_INT32 n_int = 0; + UNITY_INT32 n; + int exponent = 0; + int decimals; + int digits; + char buf[16] = {0}; + + /* + * Scale up or down by powers of 10. To minimize rounding error, + * start with a factor/divisor of 10^10, which is the largest + * power of 10 that can be represented exactly. Finally, compute + * (exactly) the remaining power of 10 and perform one more + * multiplication or division. + */ + if (number < 1.0f) + { + UNITY_DOUBLE factor = 1.0f; + + while (number < (UNITY_DOUBLE)max_scaled / 1e10f) { number *= 1e10f; exponent -= 10; } + while (number * factor < (UNITY_DOUBLE)min_scaled) { factor *= 10.0f; exponent--; } + + number *= factor; + } + else if (number > (UNITY_DOUBLE)max_scaled) + { + UNITY_DOUBLE divisor = 1.0f; + + while (number > (UNITY_DOUBLE)min_scaled * 1e10f) { number /= 1e10f; exponent += 10; } + while (number / divisor > (UNITY_DOUBLE)max_scaled) { divisor *= 10.0f; exponent++; } + + number /= divisor; + } + else + { + /* + * In this range, we can split off the integer part before + * doing any multiplications. This reduces rounding error by + * freeing up significant bits in the fractional part. + */ + UNITY_DOUBLE factor = 1.0f; + n_int = (UNITY_INT32)number; + number -= (UNITY_DOUBLE)n_int; + + while (n_int < min_scaled) { n_int *= 10; factor *= 10.0f; exponent--; } + + number *= factor; + } + + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + +#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO + /* round to even if exactly between two integers */ + if ((n & 1) && (((UNITY_DOUBLE)n - number) == 0.5f)) + n--; +#endif + + n += n_int; + + if (n >= max_scaled) + { + n = min_scaled; + exponent++; + } + + /* determine where to place decimal point */ + decimals = ((exponent <= 0) && (exponent >= -(sig_digits + 3))) ? (-exponent) : (sig_digits - 1); + exponent += decimals; + + /* truncate trailing zeroes after decimal point */ + while ((decimals > 0) && ((n % 10) == 0)) + { + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while ((n != 0) || (digits <= decimals)) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + + /* print out buffer (backwards) */ + while (digits > 0) + { + if (digits == decimals) + { + UNITY_OUTPUT_CHAR('.'); + } + UNITY_OUTPUT_CHAR(buf[--digits]); + } + + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if (exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while ((exponent != 0) || (digits < 2)) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } + } +} +#endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ +#ifdef UNITY_OUTPUT_FOR_ECLIPSE + UNITY_OUTPUT_CHAR('('); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(')'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else +#ifdef UNITY_OUTPUT_FOR_IAR_WORKBENCH + UnityPrint("'); + UnityPrint(Unity.CurrentTestName); + UnityPrint(" "); +#else +#ifdef UNITY_OUTPUT_FOR_QT_CREATOR + UnityPrint("file://"); + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((UNITY_INT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#endif +#endif +#endif +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EXEC_TIME(); + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg) +{ + if (msg) + { + UnityPrint(UnityStrSpacer); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, + const char* actual, + const UNITY_UINT32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +static int UnityIsOneArrayNull(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_LINE_TYPE lineNumber, + const char* msg) +{ + /* Both are NULL or same pointer */ + if (expected == actual) { return 0; } + + /* print and return true if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + return 1; + } + + /* print and return true if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + return 1; + } + + return 0; /* return false if neither is NULL */ +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((UNITY_UINT)mask, (UNITY_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if ((threshold == actual) && (compare & UNITY_EQUAL_TO)) { return; } + if ((threshold == actual)) { failed = 1; } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if ((actual > threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if ((actual < threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + } + else /* UINT or HEX */ + { + if (((UNITY_UINT)actual > (UNITY_UINT)threshold) && (compare & UNITY_SMALLER_THAN)) { failed = 1; } + if (((UNITY_UINT)actual < (UNITY_UINT)threshold) && (compare & UNITY_GREATER_THAN)) { failed = 1; } + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) { UnityPrint(UnityStrGt); } + if (compare & UNITY_SMALLER_THAN) { UnityPrint(UnityStrLt); } + if (compare & UNITY_EQUAL_TO) { UnityPrint(UnityStrOrEqual); } + if (compare == UNITY_NOT_EQUAL) { UnityPrint(UnityStrNotEqual); } + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +do { \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; \ +} while (0) + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if (expect_val != actual_val) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +/* Wrap this define in a function with variable types as float or double */ +#define UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff) \ + if (isinf(expected) && isinf(actual) && (((expected) < 0) == ((actual) < 0))) return 1; \ + if (UNITY_NAN_CHECK) return 1; \ + (diff) = (actual) - (expected); \ + if ((diff) < 0) (diff) = -(diff); \ + if ((delta) < 0) (delta) = -(delta); \ + return !(isnan(diff) || isinf(diff) || ((diff) > (delta))) + /* This first part of this condition will catch any NaN or Infinite values */ +#ifndef UNITY_NAN_NOT_EQUAL_NAN + #define UNITY_NAN_CHECK isnan(expected) && isnan(actual) +#else + #define UNITY_NAN_CHECK 0 +#endif + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + do { \ + UnityPrint(UnityStrExpected); \ + UnityPrintFloat(expected); \ + UnityPrint(UnityStrWas); \ + UnityPrintFloat(actual); \ + } while (0) +#else + #define UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual) \ + UnityPrint(UnityStrDelta) +#endif /* UNITY_EXCLUDE_FLOAT_PRINT */ + +/*-----------------------------------------------*/ +static int UnityFloatsWithin(UNITY_FLOAT delta, UNITY_FLOAT expected, UNITY_FLOAT actual) +{ + UNITY_FLOAT diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + if (!UnityFloatsWithin(*ptr_expected * UNITY_FLOAT_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + + if (!UnityFloatsWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat((UNITY_DOUBLE)actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +static int UnityDoublesWithin(UNITY_DOUBLE delta, UNITY_DOUBLE expected, UNITY_DOUBLE actual) +{ + UNITY_DOUBLE diff; + UNITY_FLOAT_OR_DOUBLE_WITHIN(delta, expected, actual, diff); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* ptr_actual = actual; + + RETURN_IF_FAIL_OR_IGNORE; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + if (!UnityDoublesWithin(*ptr_expected * UNITY_DOUBLE_PRECISION, *ptr_expected, *ptr_actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + if (flags == UNITY_ARRAY_TO_ARRAY) + { + ptr_expected++; + } + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if (!UnityDoublesWithin(delta, expected, actual)) + { + UnityTestResultsFailBegin(lineNumber); + UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = {UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet}; + UNITY_INT should_be_trait = ((UNITY_INT)style & 1); + UNITY_INT is_trait = !should_be_trait; + UNITY_INT trait_index = (UNITY_INT)(style >> 1); + + RETURN_IF_FAIL_OR_IGNORE; + + switch (style) + { + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) && (actual > 0); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) && (actual < 0); + break; + + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual) ? 1 : 0; + break; + + case UNITY_FLOAT_IS_DET: /* A determinate number is non infinite and not NaN. */ + case UNITY_FLOAT_IS_NOT_DET: + is_trait = !isinf(actual) && !isnan(actual); + break; + + default: /* including UNITY_FLOAT_INVALID_TRAIT */ + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + UnityPrintFloat(actual); +#else + if (should_be_trait) + { + UnityPrint(UnityStrNot); + } + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + RETURN_IF_FAIL_OR_IGNORE; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + else + { + if ((UNITY_UINT)actual > (UNITY_UINT)expected) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual - (UNITY_UINT)expected) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expected - (UNITY_UINT)actual) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 elements = num_elements; + unsigned int length = style & 0xF; + unsigned int increment = 0; + + RETURN_IF_FAIL_OR_IGNORE; + + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while ((elements > 0) && (elements--)) + { + UNITY_INT expect_val; + UNITY_INT actual_val; + + switch (length) + { + case 1: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT8*)actual; + increment = sizeof(UNITY_INT8); + break; + + case 2: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT16*)actual; + increment = sizeof(UNITY_INT16); + break; + +#ifdef UNITY_SUPPORT_64 + case 8: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT64*)actual; + increment = sizeof(UNITY_INT64); + break; +#endif + + default: /* default is length 4 bytes */ + case 4: + expect_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)expected; + actual_val = *(UNITY_PTR_ATTRIBUTE const UNITY_INT32*)actual; + increment = sizeof(UNITY_INT32); + length = 4; + break; + } + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual_val > expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + else + { + if ((UNITY_UINT)actual_val > (UNITY_UINT)expect_val) + { + Unity.CurrentTestFailed = (((UNITY_UINT)actual_val - (UNITY_UINT)expect_val) > delta); + } + else + { + Unity.CurrentTestFailed = (((UNITY_UINT)expect_val - (UNITY_UINT)actual_val) > delta); + } + } + + if (Unity.CurrentTestFailed) + { + if ((style & UNITY_DISPLAY_RANGE_UINT) && (length < (UNITY_INT_WIDTH / 8))) + { /* For UINT, remove sign extension (padding 1's) from signed type casts above */ + UNITY_INT mask = 1; + mask = (mask << 8 * length) - 1; + expect_val &= mask; + actual_val &= mask; + } + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((UNITY_INT)delta, style); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expect_val, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual_val, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + /* Walk through array by incrementing the pointers */ + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expected = (UNITY_INTERNAL_PTR)((const char*)expected + increment); + } + actual = (UNITY_INTERNAL_PTR)((const char*)actual + increment); + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_UINT32 i; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (i < length) && (expected[i] || actual[i]); i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_UINT32 i = 0; + UNITY_UINT32 j = 0; + const char* expd = NULL; + const char* act = NULL; + + RETURN_IF_FAIL_OR_IGNORE; + + /* if no elements, it's an error */ + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if ((const void*)expected == (const void*)actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + if (flags != UNITY_ARRAY_TO_ARRAY) + { + expd = (const char*)expected; + } + + do + { + act = actual[j]; + if (flags == UNITY_ARRAY_TO_ARRAY) + { + expd = ((const char* const*)expected)[j]; + } + + /* if both pointers not null compare the strings */ + if (expd && act) + { + for (i = 0; expd[i] || act[i]; i++) + { + if (expd[i] != act[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expd != act) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings(expd, act); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + UNITY_UINT32 elements = num_elements; + UNITY_UINT32 bytes; + + RETURN_IF_FAIL_OR_IGNORE; + + if ((elements == 0) || (length == 0)) + { + UnityPrintPointlessAndBail(); + } + + if (expected == actual) + { + return; /* Both are NULL or same pointer */ + } + + if (UnityIsOneArrayNull(expected, actual, lineNumber, msg)) + { + UNITY_FAIL_AND_BAIL; + } + + while (elements--) + { + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp++; + ptr_act++; + } + if (flags == UNITY_ARRAY_TO_VAL) + { + ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + } + } +} + +/*-----------------------------------------------*/ + +static union +{ + UNITY_INT8 i8; + UNITY_INT16 i16; + UNITY_INT32 i32; +#ifdef UNITY_SUPPORT_64 + UNITY_INT64 i64; +#endif +#ifndef UNITY_EXCLUDE_FLOAT + float f; +#endif +#ifndef UNITY_EXCLUDE_DOUBLE + double d; +#endif +} UnityQuickCompare; + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size) +{ + switch(size) + { + case 1: + UnityQuickCompare.i8 = (UNITY_INT8)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i8); + + case 2: + UnityQuickCompare.i16 = (UNITY_INT16)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i16); + +#ifdef UNITY_SUPPORT_64 + case 8: + UnityQuickCompare.i64 = (UNITY_INT64)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i64); +#endif + + default: /* 4 bytes */ + UnityQuickCompare.i32 = (UNITY_INT32)num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.i32); + } +} + +#ifndef UNITY_EXCLUDE_FLOAT +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num) +{ + UnityQuickCompare.f = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.f); +} +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +/*-----------------------------------------------*/ +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num) +{ + UnityQuickCompare.d = num; + return (UNITY_INTERNAL_PTR)(&UnityQuickCompare.d); +} +#endif + +/*----------------------------------------------- + * printf helper function + *-----------------------------------------------*/ +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +static void UnityPrintFVA(const char* format, va_list va) +{ + const char* pch = format; + if (pch != NULL) + { + while (*pch) + { + /* format identification character */ + if (*pch == '%') + { + pch++; + + if (pch != NULL) + { + switch (*pch) + { + case 'd': + case 'i': + { + const int number = va_arg(va, int); + UnityPrintNumber((UNITY_INT)number); + break; + } +#ifndef UNITY_EXCLUDE_FLOAT_PRINT + case 'f': + case 'g': + { + const double number = va_arg(va, double); + UnityPrintFloat((UNITY_DOUBLE)number); + break; + } +#endif + case 'u': + { + const unsigned int number = va_arg(va, unsigned int); + UnityPrintNumberUnsigned((UNITY_UINT)number); + break; + } + case 'b': + { + const unsigned int number = va_arg(va, unsigned int); + const UNITY_UINT mask = (UNITY_UINT)0 - (UNITY_UINT)1; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('b'); + UnityPrintMask(mask, (UNITY_UINT)number); + break; + } + case 'x': + case 'X': + case 'p': + { + const unsigned int number = va_arg(va, unsigned int); + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + UnityPrintNumberHex((UNITY_UINT)number, 8); + break; + } + case 'c': + { + const int ch = va_arg(va, int); + UnityPrintChar((const char *)&ch); + break; + } + case 's': + { + const char * string = va_arg(va, const char *); + UnityPrint(string); + break; + } + case '%': + { + UnityPrintChar(pch); + break; + } + default: + { + /* print the unknown format character */ + UNITY_OUTPUT_CHAR('%'); + UnityPrintChar(pch); + break; + } + } + } + } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if ((*pch == 27) && (*(pch + 1) == '[')) + { + pch += UnityPrintAnsiEscapeString(pch); + continue; + } +#endif + else if (*pch == '\n') + { + UNITY_PRINT_EOL(); + } + else + { + UnityPrintChar(pch); + } + + pch++; + } + } +} + +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if(format != NULL) + { + UnityPrint(": "); + va_list va; + va_start(va, format); + UnityPrintFVA(format, va); + va_end(va); + } + UNITY_PRINT_EOL(); +} +#endif /* ! UNITY_INCLUDE_PRINT_FORMATTED */ + + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +/*-----------------------------------------------*/ +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrFail); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifdef UNITY_PRINT_TEST_CONTEXT + UNITY_PRINT_TEST_CONTEXT(); +#endif +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + RETURN_IF_FAIL_OR_IGNORE; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityMessage(const char* msg, const UNITY_LINE_TYPE line) +{ + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint("INFO"); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_PRINT_EOL(); +} + +/*-----------------------------------------------*/ +/* If we have not defined our own test runner, then include our default test runner to make life easier */ +#ifndef UNITY_SKIP_DEFAULT_RUNNER +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + UNITY_EXEC_TIME_START(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT()) + { + tearDown(); + } + UNITY_EXEC_TIME_STOP(); + UnityConcludeTest(); +} +#endif + +/*-----------------------------------------------*/ +void UnitySetTestFile(const char* filename) +{ + Unity.TestFile = filename; +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((UNITY_INT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((UNITY_INT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((UNITY_INT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrint(UnityStrOk); + } + else + { + UnityPrint(UnityStrFail); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_FLUSH_CALL(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ +#ifdef UNITY_USE_COMMAND_LINE_ARGS + +char* UnityOptionIncludeNamed = NULL; +char* UnityOptionExcludeNamed = NULL; +int UnityVerbosity = 1; + +/*-----------------------------------------------*/ +int UnityParseOptions(int argc, char** argv) +{ + int i; + UnityOptionIncludeNamed = NULL; + UnityOptionExcludeNamed = NULL; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'l': /* list tests */ + return -1; + case 'n': /* include tests with name including this string */ + case 'f': /* an alias for -n */ + if (argv[i][2] == '=') + { + UnityOptionIncludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionIncludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Include Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + case 'q': /* quiet */ + UnityVerbosity = 0; + break; + case 'v': /* verbose */ + UnityVerbosity = 2; + break; + case 'x': /* exclude tests with name including this string */ + if (argv[i][2] == '=') + { + UnityOptionExcludeNamed = &argv[i][3]; + } + else if (++i < argc) + { + UnityOptionExcludeNamed = argv[i]; + } + else + { + UnityPrint("ERROR: No Test String to Exclude Matches For"); + UNITY_PRINT_EOL(); + return 1; + } + break; + default: + UnityPrint("ERROR: Unknown Option "); + UNITY_OUTPUT_CHAR(argv[i][1]); + UNITY_PRINT_EOL(); + return 1; + } + } + } + + return 0; +} + +/*-----------------------------------------------*/ +int IsStringInBiggerString(const char* longstring, const char* shortstring) +{ + const char* lptr = longstring; + const char* sptr = shortstring; + const char* lnext = lptr; + + if (*sptr == '*') + { + return 1; + } + + while (*lptr) + { + lnext = lptr + 1; + + /* If they current bytes match, go on to the next bytes */ + while (*lptr && *sptr && (*lptr == *sptr)) + { + lptr++; + sptr++; + + /* We're done if we match the entire string or up to a wildcard */ + if (*sptr == '*') + return 1; + if (*sptr == ',') + return 1; + if (*sptr == '"') + return 1; + if (*sptr == '\'') + return 1; + if (*sptr == ':') + return 2; + if (*sptr == 0) + return 1; + } + + /* Otherwise we start in the long pointer 1 character further and try again */ + lptr = lnext; + sptr = shortstring; + } + + return 0; +} + +/*-----------------------------------------------*/ +int UnityStringArgumentMatches(const char* str) +{ + int retval; + const char* ptr1; + const char* ptr2; + const char* ptrf; + + /* Go through the options and get the substrings for matching one at a time */ + ptr1 = str; + while (ptr1[0] != 0) + { + if ((ptr1[0] == '"') || (ptr1[0] == '\'')) + { + ptr1++; + } + + /* look for the start of the next partial */ + ptr2 = ptr1; + ptrf = 0; + do + { + ptr2++; + if ((ptr2[0] == ':') && (ptr2[1] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')) + { + ptrf = &ptr2[1]; + } + } while ((ptr2[0] != 0) && (ptr2[0] != '\'') && (ptr2[0] != '"') && (ptr2[0] != ',')); + + while ((ptr2[0] != 0) && ((ptr2[0] == ':') || (ptr2[0] == '\'') || (ptr2[0] == '"') || (ptr2[0] == ','))) + { + ptr2++; + } + + /* done if complete filename match */ + retval = IsStringInBiggerString(Unity.TestFile, ptr1); + if (retval == 1) + { + return retval; + } + + /* done if testname match after filename partial match */ + if ((retval == 2) && (ptrf != 0)) + { + if (IsStringInBiggerString(Unity.CurrentTestName, ptrf)) + { + return 1; + } + } + + /* done if complete testname match */ + if (IsStringInBiggerString(Unity.CurrentTestName, ptr1) == 1) + { + return 1; + } + + ptr1 = ptr2; + } + + /* we couldn't find a match for any substrings */ + return 0; +} + +/*-----------------------------------------------*/ +int UnityTestMatches(void) +{ + /* Check if this test name matches the included test pattern */ + int retval; + if (UnityOptionIncludeNamed) + { + retval = UnityStringArgumentMatches(UnityOptionIncludeNamed); + } + else + { + retval = 1; + } + + /* Check if this test name matches the excluded test pattern */ + if (UnityOptionExcludeNamed) + { + if (UnityStringArgumentMatches(UnityOptionExcludeNamed)) + { + retval = 0; + } + } + + return retval; +} + +#endif /* UNITY_USE_COMMAND_LINE_ARGS */ +/*-----------------------------------------------*/ diff --git a/integration-tests/src/bindings_tests/unity/unity.h b/integration-tests/src/bindings_tests/unity/unity.h new file mode 100644 index 000000000..14225a354 --- /dev/null +++ b/integration-tests/src/bindings_tests/unity/unity.h @@ -0,0 +1,661 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#define UNITY_VERSION_MAJOR 2 +#define UNITY_VERSION_MINOR 5 +#define UNITY_VERSION_BUILD 4 +#define UNITY_VERSION ((UNITY_VERSION_MAJOR << 16) | (UNITY_VERSION_MINOR << 8) | UNITY_VERSION_BUILD) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "unity_internals.h" + +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. + * If using unity directly, these will need to be provided for each test + * executable built. If you are using the test runner generator and/or + * Ceedling, these are optional. */ +void setUp(void); +void tearDown(void); + +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). If using + * Unity directly, you're in charge of calling these if they are desired. + * If using Ceedling or the test runner generator, these will be called + * automatically if they exist. */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/*------------------------------------------------------- + * Test Reset and Verify + *-------------------------------------------------------*/ + +/* These functions are intended to be called before during tests in order + * to support complex test loops, etc. Both are NOT built into Unity. Instead + * the test runner generator will create them. resetTest will run teardown and + * setup again, verifying any end-of-test needs between. verifyTest will only + * run the verification. */ +void resetTest(void); +void verifyTest(void); + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - set UNITY_INT_WIDTH + * - set UNITY_LONG_WIDTH + * - set UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_EXCLUDE_FLOAT_PRINT to trim binary size, won't print floating point values in errors + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing + + * Tests with Arguments + * - you'll want to define UNITY_USE_COMMAND_LINE_ARGS if you have the test runner passing arguments to Unity + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_MESSAGE(message) UnityMessage((message), __LINE__) +#define TEST_ONLY() +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#define TEST_PRINTF(message, ...) UnityPrintF(__LINE__, (message), __VA_ARGS__) +#endif + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() TEST_ABORT() +#define TEST_PASS_MESSAGE(message) do { UnityMessage((message), __LINE__); TEST_ABORT(); } while (0) + +/* This macro does nothing, but it is useful for build tools (like Ceedling) to make use of this to figure out + * which files should be linked to in order to perform a test. Use it like TEST_FILE("sandwiches.c") */ +#define TEST_FILE(a) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") +#define TEST_ASSERT_EMPTY(pointer) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, " Expected Empty") +#define TEST_ASSERT_NOT_EMPTY(pointer) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, " Expected Non-Empty") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR(expected, actual) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT)1 << (bit)), (UNITY_UINT)(0), (actual), __LINE__, NULL) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_size_t_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_CHAR_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_size_t_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, NULL) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_size_t_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) + +/* Arrays Compared To Single Value */ +#define TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_size_t(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, " Expected Equal") +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) +#define TEST_ASSERT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_EMPTY( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_EMPTY_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_EMPTY((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_CHAR((expected), (actual), __LINE__, (message)) + +/* Integer Not Equal To (of all sizes) */ +#define TEST_ASSERT_NOT_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_NOT_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + + +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_size_t_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_CHAR_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR((threshold), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_size_t_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_CHAR_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_CHAR_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Integer Array Ranges (of all sizes) */ +#define TEST_ASSERT_INT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_INT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_UINT64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_size_t_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX8_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX16_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX32_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_HEX64_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) +#define TEST_ASSERT_CHAR_ARRAY_WITHIN_MESSAGE(delta, expected, actual, num_elements, message) UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN((delta), (expected), (actual), num_elements, __LINE__, (message)) + + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_size_t_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_CHAR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) + +/* Arrays Compared To Single Value*/ +#define TEST_ASSERT_EACH_EQUAL_INT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_INT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_INT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_size_t_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX64((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_PTR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_PTR((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_STRING_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_STRING((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_MEMORY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY((expected), (actual), (len), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_CHAR_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_CHAR((expected), (actual), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_FLOAT_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_DOUBLE_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Shorthand */ +#ifdef UNITY_SHORTHAND_AS_OLD +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#endif +#ifdef UNITY_SHORTHAND_AS_INT +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_MEM +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((&expected), (&actual), sizeof(expected), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif +#ifdef UNITY_SHORTHAND_AS_RAW +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) == (actual)), __LINE__, message) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, message) +#endif +#ifdef UNITY_SHORTHAND_AS_NONE +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_FAIL(__LINE__, UnityStrErrShorthand) +#endif + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/integration-tests/src/bindings_tests/unity/unity_internals.h b/integration-tests/src/bindings_tests/unity/unity_internals.h new file mode 100644 index 000000000..d303e8fe7 --- /dev/null +++ b/integration-tests/src/bindings_tests/unity/unity_internals.h @@ -0,0 +1,1053 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-21 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#ifndef UNITY_EXCLUDE_SETJMP_H +#include +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +#ifndef UNITY_EXCLUDE_STDDEF_H +#include +#endif + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +#include +#endif + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX in , or default to 32 bits + * Attempt 2: UINTPTR_MAX in , or default to same size as long + * The user may override any of these derived constants: + * UNITY_INT_WIDTH, UNITY_LONG_WIDTH, UNITY_POINTER_WIDTH */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define UNITY_FUNCTION_ATTR(a) __attribute__((a)) +#else + #define UNITY_FUNCTION_ATTR(a) /* ignore */ +#endif + +#ifndef UNITY_NORETURN + #if defined(__cplusplus) + #if __cplusplus >= 201103L + #define UNITY_NORETURN [[ noreturn ]] + #endif + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #include + #define UNITY_NORETURN noreturn + #endif +#endif +#ifndef UNITY_NORETURN + #define UNITY_NORETURN UNITY_FUNCTION_ATTR(noreturn) +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specified. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the translation of the C program. + * Also sizeof(int) does return the size in addressable units on all platforms, + * which may not necessarily be the size in bytes. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_INT_WIDTH (32) + #endif /* UINT_MAX */ +#endif + +/* Determine the size of a long, if not already specified. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_LONG_WIDTH (32) + #endif /* ULONG_MAX */ +#endif + +/* Determine the size of a pointer, if not already specified. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #else /* Set to default */ + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH + #endif /* UINTPTR_MAX */ +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char UNITY_UINT8; + typedef unsigned short UNITY_UINT16; + typedef unsigned int UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed short UNITY_INT16; + typedef signed int UNITY_INT32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char UNITY_UINT8; + typedef unsigned int UNITY_UINT16; + typedef unsigned long UNITY_UINT32; + typedef signed char UNITY_INT8; + typedef signed int UNITY_INT16; + typedef signed long UNITY_INT32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +/* Auto-detect 64 Bit Support */ +#ifndef UNITY_SUPPORT_64 + #if UNITY_LONG_WIDTH == 64 || UNITY_POINTER_WIDTH == 64 + #define UNITY_SUPPORT_64 + #endif +#endif + +/* 64-Bit Support Dependent Configuration */ +#ifndef UNITY_SUPPORT_64 + /* No 64-bit Support */ + typedef UNITY_UINT32 UNITY_UINT; + typedef UNITY_INT32 UNITY_INT; + #define UNITY_MAX_NIBBLES (8) /* Maximum number of nibbles in a UNITY_(U)INT */ +#else + /* 64-bit Support */ + #if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long UNITY_UINT64; + typedef signed long long UNITY_INT64; + #elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long UNITY_UINT64; + typedef signed long UNITY_INT64; + #else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) + #endif + typedef UNITY_UINT64 UNITY_UINT; + typedef UNITY_INT64 UNITY_INT; + #define UNITY_MAX_NIBBLES (16) /* Maximum number of nibbles in a UNITY_(U)INT */ +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) + #define UNITY_PTR_TO_INT UNITY_INT32 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) + #define UNITY_PTR_TO_INT UNITY_INT64 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) + #define UNITY_PTR_TO_INT UNITY_INT16 + #define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE + #define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR + #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +#endif + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#ifndef UNITY_EXCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE /* Remove double when excluding float support */ +#endif +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +#define UNITY_EXCLUDE_FLOAT_PRINT +#endif + +#else + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE UNITY_FLOAT; + +/* isinf & isnan macros should be provided by math.h */ +#ifndef isinf +/* The value of Inf - Inf is NaN */ +#define isinf(n) (isnan((n) - (n)) && !isnan(n)) +#endif + +#ifndef isnan +/* NaN is the only floating point value that does NOT equal itself. + * Therefore if n != n, then it is NaN. */ +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike float, we DON'T include by default */ +#if defined(UNITY_EXCLUDE_DOUBLE) || !defined(UNITY_INCLUDE_DOUBLE) + + /* No Floating Point Support */ + #ifndef UNITY_EXCLUDE_DOUBLE + #define UNITY_EXCLUDE_DOUBLE + #else + #undef UNITY_INCLUDE_DOUBLE + #endif + + #ifndef UNITY_EXCLUDE_FLOAT + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_FLOAT UNITY_DOUBLE; + /* For parameter in UnityPrintFloat(UNITY_DOUBLE), which aliases to double or float */ + #endif + +#else + + /* Double Floating Point Support */ + #ifndef UNITY_DOUBLE_PRECISION + #define UNITY_DOUBLE_PRECISION (1e-12) + #endif + + #ifndef UNITY_DOUBLE_TYPE + #define UNITY_DOUBLE_TYPE double + #endif + typedef UNITY_DOUBLE_TYPE UNITY_DOUBLE; + +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR + /* Default to using putchar, which is defined in stdio.h */ + #include + #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION + extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH + #ifdef UNITY_USE_FLUSH_STDOUT + /* We want to use the stdout flush utility */ + #include + #define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) + #else + /* We've specified nothing, therefore flush should just be ignored */ + #define UNITY_OUTPUT_FLUSH() (void)0 + #endif +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION + extern void UNITY_OUTPUT_FLUSH_HEADER_DECLARATION; + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() +#else +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +#ifdef UNITY_INCLUDE_EXEC_TIME + #if !defined(UNITY_EXEC_TIME_START) && \ + !defined(UNITY_EXEC_TIME_STOP) && \ + !defined(UNITY_PRINT_EXEC_TIME) && \ + !defined(UNITY_TIME_TYPE) + /* If none any of these macros are defined then try to provide a default implementation */ + + #if defined(UNITY_CLOCK_MS) + /* This is a simple way to get a default implementation on platforms that support getting a millisecond counter */ + #define UNITY_TIME_TYPE UNITY_UINT + #define UNITY_EXEC_TIME_START() Unity.CurrentTestStartTime = UNITY_CLOCK_MS() + #define UNITY_EXEC_TIME_STOP() Unity.CurrentTestStopTime = UNITY_CLOCK_MS() + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(_WIN32) + #include + #define UNITY_TIME_TYPE clock_t + #define UNITY_GET_TIME(t) t = (clock_t)((clock() * 1000) / CLOCKS_PER_SEC) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = (Unity.CurrentTestStopTime - Unity.CurrentTestStartTime); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #elif defined(__unix__) || defined(__APPLE__) + #include + #define UNITY_TIME_TYPE struct timespec + #define UNITY_GET_TIME(t) clock_gettime(CLOCK_MONOTONIC, &t) + #define UNITY_EXEC_TIME_START() UNITY_GET_TIME(Unity.CurrentTestStartTime) + #define UNITY_EXEC_TIME_STOP() UNITY_GET_TIME(Unity.CurrentTestStopTime) + #define UNITY_PRINT_EXEC_TIME() { \ + UNITY_UINT execTimeMs = ((Unity.CurrentTestStopTime.tv_sec - Unity.CurrentTestStartTime.tv_sec) * 1000L); \ + execTimeMs += ((Unity.CurrentTestStopTime.tv_nsec - Unity.CurrentTestStartTime.tv_nsec) / 1000000L); \ + UnityPrint(" ("); \ + UnityPrintNumberUnsigned(execTimeMs); \ + UnityPrint(" ms)"); \ + } + #endif + #endif +#endif + +#ifndef UNITY_EXEC_TIME_START +#define UNITY_EXEC_TIME_START() do { /* nothing*/ } while (0) +#endif + +#ifndef UNITY_EXEC_TIME_STOP +#define UNITY_EXEC_TIME_STOP() do { /* nothing*/ } while (0) +#endif + +#ifndef UNITY_TIME_TYPE +#define UNITY_TIME_TYPE UNITY_UINT +#endif + +#ifndef UNITY_PRINT_EXEC_TIME +#define UNITY_PRINT_EXEC_TIME() do { /* nothing*/ } while (0) +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE UNITY_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE UNITY_UINT +#endif + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) +#define UNITY_DISPLAY_RANGE_CHAR (0x80) + +typedef enum +{ + UNITY_DISPLAY_STYLE_INT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + + UNITY_DISPLAY_STYLE_UINT = (UNITY_INT_WIDTH / 8) + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + + UNITY_DISPLAY_STYLE_CHAR = 1 + UNITY_DISPLAY_RANGE_CHAR + UNITY_DISPLAY_RANGE_INT, + + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +typedef enum +{ + UNITY_WITHIN = 0x0, + UNITY_EQUAL_TO = 0x1, + UNITY_GREATER_THAN = 0x2, + UNITY_GREATER_OR_EQUAL = 0x2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 0x4, + UNITY_SMALLER_OR_EQUAL = 0x4 + UNITY_EQUAL_TO, + UNITY_NOT_EQUAL = 0x0, + UNITY_UNKNOWN +} UNITY_COMPARISON_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum UNITY_FLOAT_TRAIT +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +typedef enum +{ + UNITY_ARRAY_TO_VAL = 0, + UNITY_ARRAY_TO_ARRAY, + UNITY_ARRAY_UNKNOWN +} UNITY_FLAGS_T; + +struct UNITY_STORAGE_T +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; +#ifdef UNITY_INCLUDE_EXEC_TIME + UNITY_TIME_TYPE CurrentTestStartTime; + UNITY_TIME_TYPE CurrentTestStopTime; +#endif +#ifndef UNITY_EXCLUDE_SETJMP_H + jmp_buf AbortFrame; +#endif +}; + +extern struct UNITY_STORAGE_T Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnitySetTestFile(const char* filename); +void UnityConcludeTest(void); + +#ifndef RUN_TEST +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); +#else +#define UNITY_SKIP_DEFAULT_RUNNER +#endif + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#define UNITY_CLR_DETAILS() do { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } while (0) +#define UNITY_SET_DETAIL(d1) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = 0; } while (0) +#define UNITY_SET_DETAILS(d1,d2) do { Unity.CurrentDetail1 = (d1); Unity.CurrentDetail2 = (d2); } while (0) + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +#ifdef UNITY_PRINT_TEST_CONTEXT +void UNITY_PRINT_TEST_CONTEXT(void); +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); + +#ifdef UNITY_INCLUDE_PRINT_FORMATTED +void UnityPrintF(const UNITY_LINE_TYPE line, const char* format, ...); +#endif + +void UnityPrintLen(const char* string, const UNITY_UINT32 length); +void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number); +void UnityPrintNumberByStyle(const UNITY_INT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const UNITY_INT number_to_print); +void UnityPrintNumberUnsigned(const UNITY_UINT number); +void UnityPrintNumberHex(const UNITY_UINT number, const char nibbles_to_print); + +#ifndef UNITY_EXCLUDE_FLOAT_PRINT +void UnityPrintFloat(const UNITY_DOUBLE input_number); +#endif + +/*------------------------------------------------------- + * Test Assertion Functions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +void UnityAssertBits(const UNITY_INT mask, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const UNITY_UINT32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( UNITY_INTERNAL_PTR expected, + const char** actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 length, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertNumbersWithin(const UNITY_UINT delta, + const UNITY_INT expected, + const UNITY_INT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertNumbersArrayWithin(const UNITY_UINT delta, + UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style, + const UNITY_FLAGS_T flags); + +#ifndef UNITY_EXCLUDE_SETJMP_H +UNITY_NORETURN void UnityFail(const char* message, const UNITY_LINE_TYPE line); +UNITY_NORETURN void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#else +void UnityFail(const char* message, const UNITY_LINE_TYPE line); +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); +#endif + +void UnityMessage(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const UNITY_FLOAT delta, + const UNITY_FLOAT expected, + const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected, + UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertFloatSpecial(const UNITY_FLOAT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const UNITY_DOUBLE delta, + const UNITY_DOUBLE expected, + const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* expected, + UNITY_PTR_ATTRIBUTE const UNITY_DOUBLE* actual, + const UNITY_UINT32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLAGS_T flags); + +void UnityAssertDoubleSpecial(const UNITY_DOUBLE actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Helpers + *-------------------------------------------------------*/ + +UNITY_INTERNAL_PTR UnityNumToPtr(const UNITY_INT num, const UNITY_UINT8 size); +#ifndef UNITY_EXCLUDE_FLOAT +UNITY_INTERNAL_PTR UnityFloatToPtr(const float num); +#endif +#ifndef UNITY_EXCLUDE_DOUBLE +UNITY_INTERNAL_PTR UnityDoubleToPtr(const double num); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrOk[]; +extern const char UnityStrPass[]; +extern const char UnityStrFail[]; +extern const char UnityStrIgnore[]; + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; +extern const char UnityStrErrShorthand[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#ifndef UNITY_EXCLUDE_SETJMP_H +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) +#define TEST_ABORT() longjmp(Unity.AbortFrame, 1) +#else +#define TEST_PROTECT() 1 +#define TEST_ABORT() return +#endif + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define UNITY_SUPPORT_VARIADIC_MACROS +#endif +#endif +#ifdef UNITY_SUPPORT_VARIADIC_MACROS +#define RUN_TEST(...) RUN_TEST_AT_LINE(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_AT_LINE(func, line, ...) UnityDefaultTestRun(func, #func, line) +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +#ifndef UNITY_SHORTHAND_AS_INT +#ifndef UNITY_SHORTHAND_AS_MEM +#ifndef UNITY_SHORTHAND_AS_NONE +#ifndef UNITY_SHORTHAND_AS_RAW +#define UNITY_SHORTHAND_AS_OLD +#endif +#endif +#endif +#endif + +/*----------------------------------------------- + * Command Line Argument Support + *-----------------------------------------------*/ + +#ifdef UNITY_USE_COMMAND_LINE_ARGS +int UnityParseOptions(int argc, char** argv); +int UnityTestMatches(void); +#endif + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) do { if (condition) { /* nothing*/ } else { UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message)); } } while (0) +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) == 0), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_EMPTY(pointer, line, message) UNITY_TEST_ASSERT(((pointer[0]) != 0), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_EQUAL_CHAR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_NOT_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16) (threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32) (threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 ) (threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT) (threshold), (UNITY_INT) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_CHAR(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 ) (actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16) (expected), (UNITY_INT)(UNITY_INT16) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_INT32) (expected), (UNITY_INT)(UNITY_INT32) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin( (delta), (UNITY_INT) (expected), (UNITY_INT) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT32)(delta), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(expected), (UNITY_INT)(UNITY_UINT)(UNITY_UINT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_CHAR_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 ) (expected), (UNITY_INT)(UNITY_INT8 ) (actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR) + +#define UNITY_TEST_ASSERT_INT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_INT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin( (delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX8_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX16_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT16)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX32_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT32)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_CHAR_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT8 )(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), ((UNITY_UINT32)(num_elements)), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((UNITY_PTR_TO_INT)(expected), (UNITY_PTR_TO_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (UNITY_UINT32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), 1, (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_CHAR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_ARRAY) + +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT) (expected), (UNITY_INT_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT16)(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT32)(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX8(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX16(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT16 )(expected), 2), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX32(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT32 )(expected), 4), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_PTR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_PTR_TO_INT) (expected), (UNITY_POINTER_WIDTH / 8)), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_STRING(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((UNITY_INTERNAL_PTR)(expected), (const char**)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_MEMORY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(len), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_CHAR(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT8 )(expected), 1), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_CHAR, UNITY_ARRAY_TO_VAL) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)(expected), 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_NOT_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_NOT_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UnityAssertNumbersArrayWithin((UNITY_UINT64)(delta), (UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_ARRAY_WITHIN(delta, expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((UNITY_FLOAT)(delta), (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((UNITY_FLOAT)(expected) * (UNITY_FLOAT)UNITY_FLOAT_PRECISION, (UNITY_FLOAT)(expected), (UNITY_FLOAT)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((UNITY_FLOAT*)(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_FLOAT(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray(UnityFloatToPtr(expected), (UNITY_FLOAT*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((UNITY_FLOAT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((UNITY_DOUBLE)(delta), (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((UNITY_DOUBLE)(expected) * (UNITY_DOUBLE)UNITY_DOUBLE_PRECISION, (UNITY_DOUBLE)(expected), (UNITY_DOUBLE)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((UNITY_DOUBLE*)(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_DOUBLE(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray(UnityDoubleToPtr(expected), (UNITY_DOUBLE*)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((UNITY_DOUBLE)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/integration-tests/src/bindings_tests/uuid.c b/integration-tests/src/bindings_tests/uuid.c new file mode 100644 index 000000000..83b02482d --- /dev/null +++ b/integration-tests/src/bindings_tests/uuid.c @@ -0,0 +1,72 @@ +#include +#include "unity.h" +#include "taskchampion.h" + +// creating UUIDs does not crash +static void test_uuid_creation(void) { + tc_uuid_new_v4(); + tc_uuid_nil(); +} + +// converting UUIDs to a buf works +static void test_uuid_to_buf(void) { + TEST_ASSERT_EQUAL(TC_UUID_STRING_BYTES, 36); + + TCUuid u2 = tc_uuid_nil(); + + char u2str[TC_UUID_STRING_BYTES]; + tc_uuid_to_buf(u2, u2str); + TEST_ASSERT_EQUAL_MEMORY("00000000-0000-0000-0000-000000000000", u2str, TC_UUID_STRING_BYTES); +} + +// converting UUIDs to a buf works +static void test_uuid_to_str(void) { + TCUuid u = tc_uuid_nil(); + TCString s = tc_uuid_to_str(u); + TEST_ASSERT_EQUAL_STRING( + "00000000-0000-0000-0000-000000000000", + tc_string_content(&s)); + tc_string_free(&s); +} + +// converting valid UUIDs from string works +static void test_uuid_valid_from_str(void) { + TCUuid u; + char *ustr = "23cb25e0-5d1a-4932-8131-594ac6d3a843"; + TEST_ASSERT_EQUAL(TC_RESULT_OK, tc_uuid_from_str(tc_string_borrow(ustr), &u)); + TEST_ASSERT_EQUAL(0x23, u.bytes[0]); + TEST_ASSERT_EQUAL(0x43, u.bytes[15]); +} + +// converting invalid UUIDs from string fails as expected +static void test_uuid_invalid_string_fails(void) { + TCUuid u; + char *ustr = "not-a-valid-uuid"; + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_borrow(ustr), &u)); +} + +// converting invalid UTF-8 UUIDs from string fails as expected +static void test_uuid_bad_utf8(void) { + TCUuid u; + char *ustr = "\xf0\x28\x8c\xbc"; + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_borrow(ustr), &u)); +} + +// converting a string with embedded NUL fails as expected +static void test_uuid_embedded_nul(void) { + TCUuid u; + TEST_ASSERT_EQUAL(TC_RESULT_ERROR, tc_uuid_from_str(tc_string_clone_with_len("ab\0de", 5), &u)); +} + +int uuid_tests(void) { + UNITY_BEGIN(); + // each test case above should be named here, in order. + RUN_TEST(test_uuid_creation); + RUN_TEST(test_uuid_valid_from_str); + RUN_TEST(test_uuid_to_buf); + RUN_TEST(test_uuid_to_str); + RUN_TEST(test_uuid_invalid_string_fails); + RUN_TEST(test_uuid_bad_utf8); + RUN_TEST(test_uuid_embedded_nul); + return UNITY_END(); +} diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs new file mode 100644 index 000000000..71b0b274c --- /dev/null +++ b/integration-tests/src/lib.rs @@ -0,0 +1 @@ +pub mod bindings_tests; diff --git a/integration-tests/tests/bindings.rs b/integration-tests/tests/bindings.rs new file mode 100644 index 000000000..a121dd721 --- /dev/null +++ b/integration-tests/tests/bindings.rs @@ -0,0 +1,31 @@ +use lazy_static::lazy_static; +use std::sync::Mutex; +use tempfile::TempDir; + +lazy_static! { + // the C library running the tests is not reentrant, so we use a mutex to ensure that only one + // test runs at a time. + static ref MUTEX: Mutex<()> = Mutex::new(()); +} + +macro_rules! suite( + { $s:ident } => { + #[test] + fn $s() { + let tmp_dir = TempDir::new().expect("TempDir failed"); + 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"); + } + } + }; +); + +include!(concat!(env!("OUT_DIR"), "/bindings_test_suites.rs")); diff --git a/replica-server-tests/tests/cross-sync.rs b/integration-tests/tests/cross-sync.rs similarity index 100% rename from replica-server-tests/tests/cross-sync.rs rename to integration-tests/tests/cross-sync.rs diff --git a/replica-server-tests/tests/snapshots.rs b/integration-tests/tests/snapshots.rs similarity index 100% rename from replica-server-tests/tests/snapshots.rs rename to integration-tests/tests/snapshots.rs diff --git a/lib/Cargo.toml b/lib/Cargo.toml new file mode 100644 index 000000000..6f85b62b7 --- /dev/null +++ b/lib/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "taskchampion-lib" +version = "0.1.0" +edition = "2018" + +[lib] +name = "taskchampion" +crate-type = ["staticlib", "cdylib"] + +[dependencies] +libc = "0.2.113" +chrono = "^0.4.10" +taskchampion = { path = "../taskchampion" } +uuid = { version = "^0.8.2", features = ["v4"] } +anyhow = "1.0" + +[dev-dependencies] +pretty_assertions = "1" diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 000000000..d2dbe101b --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,2 @@ +taskchampion.h: cbindgen.toml ../target/debug/libtaskchampion.so + cbindgen --config cbindgen.toml --crate taskchampion-lib --output $@ diff --git a/lib/header-intro.h b/lib/header-intro.h new file mode 100644 index 000000000..c0dd01153 --- /dev/null +++ b/lib/header-intro.h @@ -0,0 +1,76 @@ +/** + * TaskChampion + * + * This file defines the C interface to libtaskchampion. This is a thin + * wrapper around the Rust `taskchampion` crate. Refer to the documentation + * for that crate at https://docs.rs/taskchampion/latest/taskchampion/ for API + * details. The comments in this file focus mostly on the low-level details of + * passing values to and from TaskChampion. + * + * # Overview + * + * This library defines two major types used to interact with the API, that map directly + * to Rust types. + * + * * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html + * * TCTask - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html + * * TCServer - see https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html + * * TCWorkingSet - see https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html + * + * It also defines a few utility types: + * + * * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings. + * * TC…List - a list of objects represented as a C array + * * see below for the remainder + * + * # Safety + * + * Each type contains specific instructions to ensure memory safety. + * The general rules are as follows. + * + * No types in this library are threadsafe. All values should be used in only + * one thread for their entire lifetime. It is safe to use unrelated values in + * different threads (for example, different threads may use different + * TCReplica values concurrently). + * + * ## Pass by Pointer + * + * Several types such as TCReplica and TCString are "opaque" types and always + * handled as pointers in C. The bytes these pointers address are private to + * the Rust implemetation and must not be accessed from C. + * + * Pass-by-pointer values have exactly one owner, and that owner is responsible + * for freeing the value (using a `tc_…_free` function), or transferring + * ownership elsewhere. Except where documented otherwise, when a value is + * passed to C, ownership passes to C as well. When a value is passed to Rust, + * ownership stays with the C code. The exception is TCString, ownership of + * which passes to Rust when it is used as a function argument. + * + * The limited circumstances where one value must not outlive another, due to + * pointer references between them, are documented below. + * + * ## Pass by Value + * + * Types such as TCUuid and TC…List are passed by value, and contain fields + * that are accessible from C. C code is free to access the content of these + * types in a _read_only_ fashion. + * + * Pass-by-value values that contain pointers also have exactly one owner, + * responsible for freeing the value or transferring ownership. The tc_…_free + * functions for these types will replace the pointers with NULL to guard + * against use-after-free errors. The interior pointers in such values should + * never be freed directly (for example, `tc_string_free(tcuda.value)` is an + * error). + * + * TCUuid is a special case, because it does not contain pointers. It can be + * freely copied and need not be freed. + * + * ## Lists + * + * Lists are a special kind of pass-by-value type. Each contains `len` and + * `items`, where `items` is an array of length `len`. Lists, and the values + * in the `items` array, must be treated as read-only. On return from an API + * function, a list's ownership is with the C caller, which must eventually + * free the list. List data must be freed with the `tc_…_list_free` function. + * It is an error to free any value in the `items` array of a list. + */ diff --git a/lib/src/annotation.rs b/lib/src/annotation.rs new file mode 100644 index 000000000..774f94478 --- /dev/null +++ b/lib/src/annotation.rs @@ -0,0 +1,143 @@ +use crate::traits::*; +use crate::types::*; +use chrono::prelude::*; + +/// TCAnnotation contains the details of an annotation. +/// +/// # Safety +/// +/// An annotation must be initialized from a tc_.. function, and later freed +/// with `tc_annotation_free` or `tc_annotation_list_free`. +/// +/// Any function taking a `*TCAnnotation` requires: +/// - the pointer must not be NUL; +/// - the pointer must be one previously returned from a tc_… function; +/// - the memory referenced by the pointer must never be modified by C code; and +/// - ownership transfers to the called function, and the value must not be used +/// after the call returns. In fact, the value will be zeroed out to ensure this. +/// +/// TCAnnotations are not threadsafe. +#[repr(C)] +pub struct TCAnnotation { + /// Time the annotation was made. Must be nonzero. + pub entry: libc::time_t, + /// Content of the annotation. Must not be NULL. + pub description: TCString, +} + +impl PassByValue for TCAnnotation { + // NOTE: we cannot use `RustType = Annotation` here because conversion of the + // Rust to a String can fail. + type RustType = (DateTime, RustString<'static>); + + unsafe fn from_ctype(mut self) -> Self::RustType { + // SAFETY: + // - any time_t value is valid + // - time_t is copy, so ownership is not important + let entry = unsafe { libc::time_t::val_from_arg(self.entry) }.unwrap(); + // SAFETY: + // - self.description is valid (came from return_val in as_ctype) + // - self is owned, so we can take ownership of this TCString + let description = + unsafe { TCString::take_val_from_arg(&mut self.description, TCString::default()) }; + (entry, description) + } + + fn as_ctype((entry, description): Self::RustType) -> Self { + TCAnnotation { + entry: libc::time_t::as_ctype(Some(entry)), + // SAFETY: + // - ownership of the TCString tied to ownership of Self + description: unsafe { TCString::return_val(description) }, + } + } +} + +impl Default for TCAnnotation { + fn default() -> Self { + TCAnnotation { + entry: 0 as libc::time_t, + description: TCString::default(), + } + } +} + +/// TCAnnotationList represents a list of annotations. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCAnnotationList { + /// number of annotations in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// array of annotations. these remain owned by the TCAnnotationList instance and will be freed by + /// tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList. + items: *const TCAnnotation, +} + +impl CList for TCAnnotationList { + type Element = TCAnnotation; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCAnnotationList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used +/// after this call. +#[no_mangle] +pub unsafe extern "C" fn tc_annotation_free(tcann: *mut TCAnnotation) { + debug_assert!(!tcann.is_null()); + // SAFETY: + // - tcann is not NULL + // - *tcann is a valid TCAnnotation (caller promised to treat it as read-only) + let annotation = unsafe { TCAnnotation::take_val_from_arg(tcann, TCAnnotation::default()) }; + drop(annotation); +} + +/// Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. +#[no_mangle] +pub unsafe extern "C" fn tc_annotation_list_free(tcanns: *mut TCAnnotationList) { + // SAFETY: + // - tcanns is not NULL and points to a valid TCAnnotationList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_value_list(tcanns) } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_list_has_non_null_pointer() { + let tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) }; + assert!(!tcanns.items.is_null()); + assert_eq!(tcanns.len, 0); + assert_eq!(tcanns._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tcanns = unsafe { TCAnnotationList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_annotation_list_free(&mut tcanns) }; + assert!(tcanns.items.is_null()); + assert_eq!(tcanns.len, 0); + assert_eq!(tcanns._capacity, 0); + } +} diff --git a/lib/src/atomic.rs b/lib/src/atomic.rs new file mode 100644 index 000000000..8a0d9e3d1 --- /dev/null +++ b/lib/src/atomic.rs @@ -0,0 +1,34 @@ +//! Trait implementations for a few atomic types + +use crate::traits::*; +use chrono::prelude::*; + +impl PassByValue for usize { + type RustType = usize; + + unsafe fn from_ctype(self) -> usize { + self + } + + fn as_ctype(arg: usize) -> usize { + arg + } +} + +/// Convert an Option> to a libc::time_t, or zero if not set. +impl PassByValue for libc::time_t { + type RustType = Option>; + + unsafe fn from_ctype(self) -> Option> { + if self == 0 { + None + } else { + Some(Utc.timestamp(self as i64, 0)) + } + } + + fn as_ctype(arg: Option>) -> libc::time_t { + arg.map(|ts| ts.timestamp() as libc::time_t) + .unwrap_or(0 as libc::time_t) + } +} diff --git a/lib/src/kv.rs b/lib/src/kv.rs new file mode 100644 index 000000000..716883c75 --- /dev/null +++ b/lib/src/kv.rs @@ -0,0 +1,106 @@ +use crate::traits::*; +use crate::types::*; + +/// TCKV contains a key/value pair that is part of a task. +/// +/// Neither key nor value are ever NULL. They remain owned by the TCKV and +/// will be freed when it is freed with tc_kv_list_free. +#[repr(C)] +pub struct TCKV { + pub key: TCString, + pub value: TCString, +} + +impl PassByValue for TCKV { + type RustType = (RustString<'static>, RustString<'static>); + + unsafe fn from_ctype(self) -> Self::RustType { + // SAFETY: + // - self.key is not NULL (field docstring) + // - self.key came from return_ptr in as_ctype + // - self is owned, so we can take ownership of this TCString + let key = unsafe { TCString::val_from_arg(self.key) }; + // SAFETY: (same) + let value = unsafe { TCString::val_from_arg(self.value) }; + (key, value) + } + + fn as_ctype((key, value): Self::RustType) -> Self { + TCKV { + // SAFETY: + // - ownership of the TCString tied to ownership of Self + key: unsafe { TCString::return_val(key) }, + // SAFETY: + // - ownership of the TCString tied to ownership of Self + value: unsafe { TCString::return_val(value) }, + } + } +} + +/// TCKVList represents a list of key/value pairs. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCKVList { + /// number of key/value pairs in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// array of TCKV's. these remain owned by the TCKVList instance and will be freed by + /// tc_kv_list_free. This pointer is never NULL for a valid TCKVList. + items: *const TCKV, +} + +impl CList for TCKVList { + type Element = TCKV; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCKVList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList. +#[no_mangle] +pub unsafe extern "C" fn tc_kv_list_free(tckvs: *mut TCKVList) { + // SAFETY: + // - tckvs is not NULL and points to a valid TCKVList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_value_list(tckvs) } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_list_has_non_null_pointer() { + let tckvs = unsafe { TCKVList::return_val(Vec::new()) }; + assert!(!tckvs.items.is_null()); + assert_eq!(tckvs.len, 0); + assert_eq!(tckvs._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tckvs = unsafe { TCKVList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_kv_list_free(&mut tckvs) }; + assert!(tckvs.items.is_null()); + assert_eq!(tckvs.len, 0); + assert_eq!(tckvs._capacity, 0); + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs new file mode 100644 index 000000000..4f83aaf0d --- /dev/null +++ b/lib/src/lib.rs @@ -0,0 +1,39 @@ +// Not compatible with the MSRV +// #![warn(unsafe_op_in_unsafe_fn)] +#![allow(unused_unsafe)] +// Not working yet in stable - https://github.com/rust-lang/rust-clippy/issues/8020 +// #![warn(clippy::undocumented_unsafe_blocks)] + +// docstrings for extern "C" functions are reflected into C, and do not benefit +// from safety docs. +#![allow(clippy::missing_safety_doc)] + +mod traits; +mod util; + +pub mod annotation; +pub mod atomic; +pub mod kv; +pub mod replica; +pub mod result; +pub mod server; +pub mod status; +pub mod string; +pub mod task; +pub mod uda; +pub mod uuid; +pub mod workingset; + +pub(crate) mod types { + pub(crate) use crate::annotation::{TCAnnotation, TCAnnotationList}; + pub(crate) use crate::kv::{TCKVList, TCKV}; + pub(crate) use crate::replica::TCReplica; + pub(crate) use crate::result::TCResult; + pub(crate) use crate::server::TCServer; + pub(crate) use crate::status::TCStatus; + pub(crate) use crate::string::{RustString, TCString, TCStringList}; + pub(crate) use crate::task::{TCTask, TCTaskList}; + pub(crate) use crate::uda::{TCUda, TCUdaList, Uda}; + pub(crate) use crate::uuid::{TCUuid, TCUuidList}; + pub(crate) use crate::workingset::TCWorkingSet; +} diff --git a/lib/src/replica.rs b/lib/src/replica.rs new file mode 100644 index 000000000..11fd26186 --- /dev/null +++ b/lib/src/replica.rs @@ -0,0 +1,428 @@ +use crate::traits::*; +use crate::types::*; +use crate::util::err_to_ruststring; +use std::ptr::NonNull; +use taskchampion::{Replica, StorageConfig}; + +/// A replica represents an instance of a user's task data, providing an easy interface +/// for querying and modifying that data. +/// +/// # Error Handling +/// +/// When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then +/// `tc_replica_error` will return the error message. +/// +/// # Safety +/// +/// The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and +/// must later be freed to avoid a memory leak. +/// +/// Any function taking a `*TCReplica` requires: +/// - the pointer must not be NUL; +/// - the pointer must be one previously returned from a tc_… function; +/// - the memory referenced by the pointer must never be modified by C code; and +/// - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller. +/// +/// Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again. +/// +/// TCReplicas are not threadsafe. +pub struct TCReplica { + /// The wrapped Replica + inner: Replica, + + /// If true, this replica has an outstanding &mut (for a TaskMut) + mut_borrowed: bool, + + /// The error from the most recent operation, if any + error: Option>, +} + +impl PassByPointer for TCReplica {} + +impl TCReplica { + /// Mutably borrow the inner Replica + pub(crate) fn borrow_mut(&mut self) -> &mut Replica { + if self.mut_borrowed { + panic!("replica is already borrowed"); + } + self.mut_borrowed = true; + &mut self.inner + } + + /// Release the borrow made by [`borrow_mut`] + pub(crate) fn release_borrow(&mut self) { + if !self.mut_borrowed { + panic!("replica is not borrowed"); + } + self.mut_borrowed = false; + } +} + +impl From for TCReplica { + fn from(rep: Replica) -> TCReplica { + TCReplica { + inner: rep, + mut_borrowed: false, + error: None, + } + } +} + +/// Utility function to allow using `?` notation to return an error value. This makes +/// a mutable borrow, because most Replica methods require a `&mut`. +fn wrap(rep: *mut TCReplica, f: F, err_value: T) -> T +where + F: FnOnce(&mut Replica) -> anyhow::Result, +{ + debug_assert!(!rep.is_null()); + // SAFETY: + // - rep is not NULL (promised by caller) + // - *rep is a valid TCReplica (promised by caller) + // - rep is valid for the duration of this function + // - rep is not modified by anything else (not threadsafe) + let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) }; + if rep.mut_borrowed { + panic!("replica is borrowed and cannot be used"); + } + rep.error = None; + match f(&mut rep.inner) { + Ok(v) => v, + Err(e) => { + rep.error = Some(err_to_ruststring(e)); + err_value + } + } +} + +/// Utility function to allow using `?` notation to return an error value in the constructor. +fn wrap_constructor(f: F, error_out: *mut TCString, err_value: T) -> T +where + F: FnOnce() -> anyhow::Result, +{ + if !error_out.is_null() { + // SAFETY: + // - error_out is not NULL (just checked) + // - properly aligned and valid (promised by caller) + unsafe { *error_out = TCString::default() }; + } + + match f() { + Ok(v) => v, + Err(e) => { + if !error_out.is_null() { + // SAFETY: + // - error_out is not NULL (just checked) + // - properly aligned and valid (promised by caller) + unsafe { + TCString::val_to_arg_out(err_to_ruststring(e), error_out); + } + } + err_value + } + } +} + +/// Create a new TCReplica with an in-memory database. The contents of the database will be +/// lost when it is freed with tc_replica_free. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_new_in_memory() -> *mut TCReplica { + let storage = StorageConfig::InMemory + .into_storage() + .expect("in-memory always succeeds"); + // SAFETY: + // - caller promises to free this value + unsafe { TCReplica::from(Replica::new(storage)).return_ptr() } +} + +/// Create a new TCReplica with an on-disk database having the given filename. On error, a string +/// is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller +/// must free this string. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_new_on_disk( + path: TCString, + error_out: *mut TCString, +) -> *mut TCReplica { + wrap_constructor( + || { + // SAFETY: + // - path is valid (promised by caller) + // - caller will not use path after this call (convention) + let mut path = unsafe { TCString::val_from_arg(path) }; + let storage = StorageConfig::OnDisk { + taskdb_dir: path.to_path_buf()?, + } + .into_storage()?; + + // SAFETY: + // - caller promises to free this value + Ok(unsafe { TCReplica::from(Replica::new(storage)).return_ptr() }) + }, + error_out, + std::ptr::null_mut(), + ) +} + +/// Get a list of all tasks in the replica. +/// +/// Returns a TCTaskList with a NULL items field on error. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_all_tasks(rep: *mut TCReplica) -> TCTaskList { + wrap( + rep, + |rep| { + // note that the Replica API returns a hashmap here, but we discard + // the keys and return a simple list. The task UUIDs are available + // from task.get_uuid(), so information is not lost. + let tasks: Vec<_> = rep + .all_tasks()? + .drain() + .map(|(_uuid, t)| { + NonNull::new( + // SAFETY: + // - caller promises to free this value (via freeing the list) + unsafe { TCTask::from(t).return_ptr() }, + ) + .expect("TCTask::return_ptr returned NULL") + }) + .collect(); + // SAFETY: + // - value is not allocated and need not be freed + Ok(unsafe { TCTaskList::return_val(tasks) }) + }, + TCTaskList::null_value(), + ) +} + +/// Get a list of all uuids for tasks in the replica. +/// +/// Returns a TCUuidList with a NULL items field on error. +/// +/// The caller must free the UUID list with `tc_uuid_list_free`. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_all_task_uuids(rep: *mut TCReplica) -> TCUuidList { + wrap( + rep, + |rep| { + let uuids: Vec<_> = rep + .all_task_uuids()? + .drain(..) + // SAFETY: + // - value is not allocated and need not be freed + .map(|uuid| unsafe { TCUuid::return_val(uuid) }) + .collect(); + // SAFETY: + // - value will be freed (promised by caller) + Ok(unsafe { TCUuidList::return_val(uuids) }) + }, + TCUuidList::null_value(), + ) +} + +/// Get the current working set for this replica. The resulting value must be freed +/// with tc_working_set_free. +/// +/// Returns NULL on error. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_working_set(rep: *mut TCReplica) -> *mut TCWorkingSet { + wrap( + rep, + |rep| { + let ws = rep.working_set()?; + // SAFETY: + // - caller promises to free this value + Ok(unsafe { TCWorkingSet::return_ptr(ws.into()) }) + }, + std::ptr::null_mut(), + ) +} + +/// Get an existing task by its UUID. +/// +/// Returns NULL when the task does not exist, and on error. Consult tc_replica_error +/// to distinguish the two conditions. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_get_task(rep: *mut TCReplica, tcuuid: TCUuid) -> *mut TCTask { + wrap( + rep, + |rep| { + // SAFETY: + // - tcuuid is a valid TCUuid (all bytes are valid) + // - tcuuid is Copy so ownership doesn't matter + let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; + if let Some(task) = rep.get_task(uuid)? { + // SAFETY: + // - caller promises to free this task + Ok(unsafe { TCTask::from(task).return_ptr() }) + } else { + Ok(std::ptr::null_mut()) + } + }, + std::ptr::null_mut(), + ) +} + +/// Create a new task. The task must not already exist. +/// +/// Returns the task, or NULL on error. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_new_task( + rep: *mut TCReplica, + status: TCStatus, + description: TCString, +) -> *mut TCTask { + // SAFETY: + // - description is valid (promised by caller) + // - caller will not use description after this call (convention) + let mut description = unsafe { TCString::val_from_arg(description) }; + wrap( + rep, + |rep| { + let task = rep.new_task(status.into(), description.as_str()?.to_string())?; + // SAFETY: + // - caller promises to free this task + Ok(unsafe { TCTask::from(task).return_ptr() }) + }, + std::ptr::null_mut(), + ) +} + +/// Create a new task. The task must not already exist. +/// +/// Returns the task, or NULL on error. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_import_task_with_uuid( + rep: *mut TCReplica, + tcuuid: TCUuid, +) -> *mut TCTask { + wrap( + rep, + |rep| { + // SAFETY: + // - tcuuid is a valid TCUuid (all bytes are valid) + // - tcuuid is Copy so ownership doesn't matter + let uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; + let task = rep.import_task_with_uuid(uuid)?; + // SAFETY: + // - caller promises to free this task + Ok(unsafe { TCTask::from(task).return_ptr() }) + }, + std::ptr::null_mut(), + ) +} + +/// Synchronize this replica with a server. +/// +/// The `server` argument remains owned by the caller, and must be freed explicitly. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_sync( + rep: *mut TCReplica, + server: *mut TCServer, + avoid_snapshots: bool, +) -> TCResult { + wrap( + rep, + |rep| { + debug_assert!(!server.is_null()); + // SAFETY: + // - server is not NULL + // - *server is a valid TCServer (promised by caller) + // - server is valid for the lifetime of tc_replica_sync (not threadsafe) + // - server will not be accessed simultaneously (not threadsafe) + let server = unsafe { TCServer::from_ptr_arg_ref_mut(server) }; + rep.sync(server.as_mut(), avoid_snapshots)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Undo local operations until the most recent UndoPoint. +/// +/// If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if +/// there are no operations that can be done. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_undo(rep: *mut TCReplica, undone_out: *mut i32) -> TCResult { + wrap( + rep, + |rep| { + let undone = if rep.undo()? { 1 } else { 0 }; + if !undone_out.is_null() { + // SAFETY: + // - undone_out is not NULL (just checked) + // - undone_out is properly aligned (implicitly promised by caller) + unsafe { *undone_out = undone }; + } + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically +/// when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already +/// been created by this Replica, and may be useful when a Replica instance is held for a long time +/// and used to apply more than one user-visible change. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_add_undo_point(rep: *mut TCReplica, force: bool) -> TCResult { + wrap( + rep, + |rep| { + rep.add_undo_point(force)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber` +/// is true, then existing tasks may be moved to new working-set indices; in any case, on +/// completion all pending tasks are in the working set and all non- pending tasks are not. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_rebuild_working_set( + rep: *mut TCReplica, + renumber: bool, +) -> TCResult { + wrap( + rep, + |rep| { + rep.rebuild_working_set(renumber)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent +/// calls to this function will return NULL. The rep pointer must not be NULL. The caller must +/// free the returned string. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_error(rep: *mut TCReplica) -> TCString { + // SAFETY: + // - rep is not NULL (promised by caller) + // - *rep is a valid TCReplica (promised by caller) + // - rep is valid for the duration of this function + // - rep is not modified by anything else (not threadsafe) + let rep: &mut TCReplica = unsafe { TCReplica::from_ptr_arg_ref_mut(rep) }; + if let Some(rstring) = rep.error.take() { + // SAFETY: + // - caller promises to free this string + unsafe { TCString::return_val(rstring) } + } else { + TCString::default() + } +} + +/// Free a replica. The replica may not be used after this function returns and must not be freed +/// more than once. +#[no_mangle] +pub unsafe extern "C" fn tc_replica_free(rep: *mut TCReplica) { + // SAFETY: + // - replica is not NULL (promised by caller) + // - replica is valid (promised by caller) + // - caller will not use description after this call (promised by caller) + let replica = unsafe { TCReplica::take_from_ptr_arg(rep) }; + if replica.mut_borrowed { + panic!("replica is borrowed and cannot be freed"); + } + drop(replica); +} diff --git a/lib/src/result.rs b/lib/src/result.rs new file mode 100644 index 000000000..a7d53ea8d --- /dev/null +++ b/lib/src/result.rs @@ -0,0 +1,9 @@ +/// A result from a TC operation. Typically if this value is TC_RESULT_ERROR, +/// the associated object's `tc_.._error` method will return an error message. +/// cbindgen:prefix-with-name +/// cbindgen:rename-all=ScreamingSnakeCase +#[repr(i32)] +pub enum TCResult { + Error = -1, + Ok = 0, +} diff --git a/lib/src/server.rs b/lib/src/server.rs new file mode 100644 index 000000000..b7247bdb6 --- /dev/null +++ b/lib/src/server.rs @@ -0,0 +1,143 @@ +use crate::traits::*; +use crate::types::*; +use crate::util::err_to_ruststring; +use taskchampion::{Server, ServerConfig}; + +/// TCServer represents an interface to a sync server. Aside from new and free, a server +/// has no C-accessible API, but is designed to be passed to `tc_replica_sync`. +/// +/// ## Safety +/// +/// TCServer are not threadsafe, and must not be used with multiple replicas simultaneously. +pub struct TCServer(Box); + +impl PassByPointer for TCServer {} + +impl From> for TCServer { + fn from(server: Box) -> TCServer { + TCServer(server) + } +} + +impl AsMut> for TCServer { + fn as_mut(&mut self) -> &mut Box { + &mut self.0 + } +} + +/// Utility function to allow using `?` notation to return an error value. +fn wrap(f: F, error_out: *mut TCString, err_value: T) -> T +where + F: FnOnce() -> anyhow::Result, +{ + if !error_out.is_null() { + // SAFETY: + // - error_out is not NULL (just checked) + // - properly aligned and valid (promised by caller) + unsafe { *error_out = TCString::default() }; + } + + match f() { + Ok(v) => v, + Err(e) => { + if !error_out.is_null() { + // SAFETY: + // - error_out is not NULL (just checked) + // - properly aligned and valid (promised by caller) + unsafe { + TCString::val_to_arg_out(err_to_ruststring(e), error_out); + } + } + err_value + } + } +} + +/// Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the +/// description of the arguments. +/// +/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is +/// returned. The caller must free this string. +/// +/// The server must be freed after it is used - tc_replica_sync does not automatically free it. +#[no_mangle] +pub unsafe extern "C" fn tc_server_new_local( + server_dir: TCString, + error_out: *mut TCString, +) -> *mut TCServer { + wrap( + || { + // SAFETY: + // - server_dir is valid (promised by caller) + // - caller will not use server_dir after this call (convention) + let mut server_dir = unsafe { TCString::val_from_arg(server_dir) }; + let server_config = ServerConfig::Local { + server_dir: server_dir.to_path_buf()?, + }; + let server = server_config.into_server()?; + // SAFETY: caller promises to free this server. + Ok(unsafe { TCServer::return_ptr(server.into()) }) + }, + error_out, + std::ptr::null_mut(), + ) +} + +/// Create a new TCServer that connects to a remote server. See the TaskChampion docs for the +/// description of the arguments. +/// +/// On error, a string is written to the error_out parameter (if it is not NULL) and NULL is +/// returned. The caller must free this string. +/// +/// The server must be freed after it is used - tc_replica_sync does not automatically free it. +#[no_mangle] +pub unsafe extern "C" fn tc_server_new_remote( + origin: TCString, + client_key: TCUuid, + encryption_secret: TCString, + error_out: *mut TCString, +) -> *mut TCServer { + wrap( + || { + // SAFETY: + // - origin is valid (promised by caller) + // - origin ownership is transferred to this function + let origin = unsafe { TCString::val_from_arg(origin) }.into_string()?; + + // SAFETY: + // - client_key is a valid Uuid (any 8-byte sequence counts) + + let client_key = unsafe { TCUuid::val_from_arg(client_key) }; + // SAFETY: + // - encryption_secret is valid (promised by caller) + // - encryption_secret ownership is transferred to this function + let encryption_secret = unsafe { TCString::val_from_arg(encryption_secret) } + .as_bytes() + .to_vec(); + + let server_config = ServerConfig::Remote { + origin, + client_key, + encryption_secret, + }; + let server = server_config.into_server()?; + // SAFETY: caller promises to free this server. + Ok(unsafe { TCServer::return_ptr(server.into()) }) + }, + error_out, + std::ptr::null_mut(), + ) +} + +/// Free a server. The server may not be used after this function returns and must not be freed +/// more than once. +#[no_mangle] +pub unsafe extern "C" fn tc_server_free(server: *mut TCServer) { + debug_assert!(!server.is_null()); + // SAFETY: + // - server is not NULL + // - server came from tc_server_new_.., which used return_ptr + // - server will not be used after (promised by caller) + let server = unsafe { TCServer::take_from_ptr_arg(server) }; + drop(server); +} diff --git a/lib/src/status.rs b/lib/src/status.rs new file mode 100644 index 000000000..306f27630 --- /dev/null +++ b/lib/src/status.rs @@ -0,0 +1,36 @@ +pub use taskchampion::Status; + +/// The status of a task, as defined by the task data model. +/// cbindgen:prefix-with-name +/// cbindgen:rename-all=ScreamingSnakeCase +#[repr(C)] +pub enum TCStatus { + Pending, + Completed, + Deleted, + /// Unknown signifies a status in the task DB that was not + /// recognized. + Unknown, +} + +impl From for Status { + fn from(status: TCStatus) -> Status { + match status { + TCStatus::Pending => Status::Pending, + TCStatus::Completed => Status::Completed, + TCStatus::Deleted => Status::Deleted, + TCStatus::Unknown => Status::Unknown("unknown".to_string()), + } + } +} + +impl From for TCStatus { + fn from(status: Status) -> TCStatus { + match status { + Status::Pending => TCStatus::Pending, + Status::Completed => TCStatus::Completed, + Status::Deleted => TCStatus::Deleted, + Status::Unknown(_) => TCStatus::Unknown, + } + } +} diff --git a/lib/src/string.rs b/lib/src/string.rs new file mode 100644 index 000000000..86b1ac3bd --- /dev/null +++ b/lib/src/string.rs @@ -0,0 +1,703 @@ +use crate::traits::*; +use crate::util::{string_into_raw_parts, vec_into_raw_parts}; +use std::ffi::{CStr, CString, OsString}; +use std::os::raw::c_char; +use std::path::PathBuf; + +/// TCString supports passing strings into and out of the TaskChampion API. +/// +/// # Rust Strings and C Strings +/// +/// A Rust string can contain embedded NUL characters, while C considers such a character to mark +/// the end of a string. Strings containing embedded NULs cannot be represented as a "C string" +/// and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In +/// general, these two functions should be used for handling arbitrary data, while more convenient +/// forms may be used where embedded NUL characters are impossible, such as in static strings. +/// +/// # UTF-8 +/// +/// TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given +/// a `*TCString` containing invalid UTF-8. +/// +/// # Safety +/// +/// The `ptr` field may be checked for NULL, where documentation indicates this is possible. All +/// other fields in a TCString are private and must not be used from C. They exist in the struct +/// to ensure proper allocation and alignment. +/// +/// When a `TCString` appears as a return value or output argument, ownership is passed to the +/// caller. The caller must pass that ownership back to another function or free the string. +/// +/// Any function taking a `TCString` requires: +/// - the pointer must not be NUL; +/// - the pointer must be one previously returned from a tc_… function; and +/// - the memory referenced by the pointer must never be modified by C code. +/// +/// Unless specified otherwise, TaskChampion functions take ownership of a `TCString` when it is +/// given as a function argument, and the caller must not use or free TCStrings after passing them +/// to such API functions. +/// +/// A TCString with a NULL `ptr` field need not be freed, although tc_free_string will not fail +/// for such a value. +/// +/// TCString is not threadsafe. +/// cbindgen:field-names=[ptr, _u1, _u2, _u3] +#[repr(C)] +pub struct TCString { + // defined based on the type + ptr: *mut libc::c_void, + len: usize, + cap: usize, + + // type of TCString this represents + ty: u8, +} + +// TODO: figure out how to ignore this but still use it in TCString +/// A discriminator for TCString +#[repr(u8)] +enum TCStringType { + /// Null. Nothing is contained in this string. + /// + /// * `ptr` is NULL. + /// * `len` and `cap` are zero. + Null = 0, + + /// A CString. + /// + /// * `ptr` is the result of CString::into_raw, containing a terminating NUL. It may not be + /// valid UTF-8. + /// * `len` and `cap` are zero. + CString, + + /// A CStr, referencing memory borrowed from C + /// + /// * `ptr` points to the string, containing a terminating NUL. It may not be valid UTF-8. + /// * `len` and `cap` are zero. + CStr, + + /// A String. + /// + /// * `ptr`, `len`, and `cap` are as would be returned from String::into_raw_parts. + String, + + /// A byte sequence. + /// + /// * `ptr`, `len`, and `cap` are as would be returned from Vec::into_raw_parts. + Bytes, +} + +impl Default for TCString { + fn default() -> Self { + TCString { + ptr: std::ptr::null_mut(), + len: 0, + cap: 0, + ty: TCStringType::Null as u8, + } + } +} + +impl TCString { + pub(crate) fn is_null(&self) -> bool { + self.ptr.is_null() + } +} + +#[derive(PartialEq, Debug)] +pub enum RustString<'a> { + Null, + CString(CString), + CStr(&'a CStr), + String(String), + Bytes(Vec), +} + +impl<'a> Default for RustString<'a> { + fn default() -> Self { + RustString::Null + } +} + +impl PassByValue for TCString { + type RustType = RustString<'static>; + + unsafe fn from_ctype(self) -> Self::RustType { + match self.ty { + ty if ty == TCStringType::CString as u8 => { + // SAFETY: + // - ptr was derived from CString::into_raw + // - data was not modified since that time (caller promises) + RustString::CString(unsafe { CString::from_raw(self.ptr as *mut c_char) }) + } + ty if ty == TCStringType::CStr as u8 => { + // SAFETY: + // - ptr was created by CStr::as_ptr + // - data was not modified since that time (caller promises) + RustString::CStr(unsafe { CStr::from_ptr(self.ptr as *mut c_char) }) + } + ty if ty == TCStringType::String as u8 => { + // SAFETY: + // - ptr was created by string_into_raw_parts + // - data was not modified since that time (caller promises) + RustString::String(unsafe { + String::from_raw_parts(self.ptr as *mut u8, self.len, self.cap) + }) + } + ty if ty == TCStringType::Bytes as u8 => { + // SAFETY: + // - ptr was created by vec_into_raw_parts + // - data was not modified since that time (caller promises) + RustString::Bytes(unsafe { + Vec::from_raw_parts(self.ptr as *mut u8, self.len, self.cap) + }) + } + _ => RustString::Null, + } + } + + fn as_ctype(arg: Self::RustType) -> Self { + match arg { + RustString::Null => Self { + ty: TCStringType::Null as u8, + ..Default::default() + }, + RustString::CString(cstring) => Self { + ty: TCStringType::CString as u8, + ptr: cstring.into_raw() as *mut libc::c_void, + ..Default::default() + }, + RustString::CStr(cstr) => Self { + ty: TCStringType::CStr as u8, + ptr: cstr.as_ptr() as *mut libc::c_void, + ..Default::default() + }, + RustString::String(string) => { + let (ptr, len, cap) = string_into_raw_parts(string); + Self { + ty: TCStringType::String as u8, + ptr: ptr as *mut libc::c_void, + len, + cap, + } + } + RustString::Bytes(bytes) => { + let (ptr, len, cap) = vec_into_raw_parts(bytes); + Self { + ty: TCStringType::Bytes as u8, + ptr: ptr as *mut libc::c_void, + len, + cap, + } + } + } + } +} + +impl<'a> RustString<'a> { + /// Get a regular Rust &str for this value. + pub(crate) fn as_str(&mut self) -> Result<&str, std::str::Utf8Error> { + match self { + RustString::CString(cstring) => cstring.as_c_str().to_str(), + RustString::CStr(cstr) => cstr.to_str(), + RustString::String(ref string) => Ok(string.as_ref()), + RustString::Bytes(_) => { + self.bytes_to_string()?; + self.as_str() // now the String variant, so won't recurse + } + RustString::Null => unreachable!(), + } + } + + /// Consume this RustString and return an equivalent String, or an error if not + /// valid UTF-8. In the error condition, the original data is lost. + pub(crate) fn into_string(mut self) -> Result { + match self { + RustString::CString(cstring) => cstring.into_string().map_err(|e| e.utf8_error()), + RustString::CStr(cstr) => cstr.to_str().map(|s| s.to_string()), + RustString::String(string) => Ok(string), + RustString::Bytes(_) => { + self.bytes_to_string()?; + self.into_string() // now the String variant, so won't recurse + } + RustString::Null => unreachable!(), + } + } + + pub(crate) fn as_bytes(&self) -> &[u8] { + match self { + RustString::CString(cstring) => cstring.as_bytes(), + RustString::CStr(cstr) => cstr.to_bytes(), + RustString::String(string) => string.as_bytes(), + RustString::Bytes(bytes) => bytes.as_ref(), + RustString::Null => unreachable!(), + } + } + + /// Convert the RustString, in place, from the Bytes to String variant. On successful return, + /// the RustString has variant RustString::String. + fn bytes_to_string(&mut self) -> Result<(), std::str::Utf8Error> { + let mut owned = RustString::Null; + // temporarily swap a Null value into self; we'll swap that back + // shortly. + std::mem::swap(self, &mut owned); + match owned { + RustString::Bytes(bytes) => match String::from_utf8(bytes) { + Ok(string) => { + *self = RustString::String(string); + Ok(()) + } + Err(e) => { + let (e, bytes) = (e.utf8_error(), e.into_bytes()); + // put self back as we found it + *self = RustString::Bytes(bytes); + Err(e) + } + }, + _ => { + // not bytes, so just swap back + std::mem::swap(self, &mut owned); + Ok(()) + } + } + } + + /// Convert the RustString, in place, into one of the C variants. If this is not + /// possible, such as if the string contains an embedded NUL, then the string + /// remains unchanged. + fn string_to_cstring(&mut self) { + let mut owned = RustString::Null; + // temporarily swap a Null value into self; we'll swap that back shortly + std::mem::swap(self, &mut owned); + match owned { + RustString::String(string) => { + match CString::new(string) { + Ok(cstring) => { + *self = RustString::CString(cstring); + } + Err(nul_err) => { + // recover the underlying String from the NulError and restore + // the RustString + let original_bytes = nul_err.into_vec(); + // SAFETY: original_bytes came from a String moments ago, so still valid utf8 + let string = unsafe { String::from_utf8_unchecked(original_bytes) }; + *self = RustString::String(string); + } + } + } + _ => { + // not a CString, so just swap back + std::mem::swap(self, &mut owned); + } + } + } + + pub(crate) fn to_path_buf(&mut self) -> Result { + #[cfg(unix)] + let path: OsString = { + // on UNIX, we can use the bytes directly, without requiring that they + // be valid UTF-8. + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + OsStr::from_bytes(self.as_bytes()).to_os_string() + }; + #[cfg(windows)] + let path: OsString = { + // on Windows, we assume the filename is valid Unicode, so it can be + // represented as UTF-8. + OsString::from(self.as_str()?.to_string()) + }; + Ok(path.into()) + } +} + +impl<'a> From for RustString<'a> { + fn from(string: String) -> RustString<'a> { + RustString::String(string) + } +} + +impl<'a> From<&str> for RustString<'static> { + fn from(string: &str) -> RustString<'static> { + RustString::String(string.to_string()) + } +} + +/// Utility function to borrow a TCString from a pointer arg, modify it, +/// and restore it. +/// +/// This implements a kind of "interior mutability", relying on the +/// single-threaded use of all TC* types. +/// +/// # SAFETY +/// +/// - tcstring must not be NULL +/// - *tcstring must be a valid TCString +/// - *tcstring must not be accessed by anything else, despite the *const +unsafe fn wrap(tcstring: *const TCString, f: F) -> T +where + F: FnOnce(&mut RustString) -> T, +{ + debug_assert!(!tcstring.is_null()); + + // SAFETY: + // - we have exclusive to *tcstring (promised by caller) + let tcstring = tcstring as *mut TCString; + + // SAFETY: + // - tcstring is not NULL + // - *tcstring is a valid string (promised by caller) + let mut rstring = unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) }; + + let rv = f(&mut rstring); + + // update the caller's TCString with the updated RustString + // SAFETY: + // - tcstring is not NULL (we just took from it) + // - tcstring points to valid memory (we just took from it) + unsafe { TCString::val_to_arg_out(rstring, tcstring) }; + + rv +} + +/// TCStringList represents a list of strings. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCStringList { + /// number of strings in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// TCStringList representing each string. these remain owned by the TCStringList instance and will + /// be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the + /// *TCStringList at indexes 0..len-1 are not NULL. + items: *const TCString, +} + +impl CList for TCStringList { + type Element = TCString; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCStringList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Create a new TCString referencing the given C string. The C string must remain valid and +/// unchanged until after the TCString is freed. It's typically easiest to ensure this by using a +/// static string. +/// +/// NOTE: this function does _not_ take responsibility for freeing the given C string. The +/// given string can be freed once the TCString referencing it has been freed. +/// +/// For example: +/// +/// ``` +/// char *url = get_item_url(..); // dynamically allocate C string +/// tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed +/// free(url); // string is no longer referenced and can be freed +/// ``` +#[no_mangle] +pub unsafe extern "C" fn tc_string_borrow(cstr: *const libc::c_char) -> TCString { + debug_assert!(!cstr.is_null()); + // SAFETY: + // - cstr is not NULL (promised by caller, verified by assertion) + // - cstr's lifetime exceeds that of the TCString (promised by caller) + // - cstr contains a valid NUL terminator (promised by caller) + // - cstr's content will not change before it is destroyed (promised by caller) + let cstr: &CStr = unsafe { CStr::from_ptr(cstr) }; + // SAFETY: + // - caller promises to free this string + unsafe { TCString::return_val(RustString::CStr(cstr)) } +} + +/// Create a new TCString by cloning the content of the given C string. The resulting TCString +/// is independent of the given string, which can be freed or overwritten immediately. +#[no_mangle] +pub unsafe extern "C" fn tc_string_clone(cstr: *const libc::c_char) -> TCString { + debug_assert!(!cstr.is_null()); + // SAFETY: + // - cstr is not NULL (promised by caller, verified by assertion) + // - cstr's lifetime exceeds that of this function (by C convention) + // - cstr contains a valid NUL terminator (promised by caller) + // - cstr's content will not change before it is destroyed (by C convention) + let cstr: &CStr = unsafe { CStr::from_ptr(cstr) }; + let cstring: CString = cstr.into(); + // SAFETY: + // - caller promises to free this string + unsafe { TCString::return_val(RustString::CString(cstring)) } +} + +/// Create a new TCString containing the given string with the given length. This allows creation +/// of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting +/// TCString is independent of the passed buffer, which may be reused or freed immediately. +/// +/// The length should _not_ include any trailing NUL. +/// +/// The given length must be less than half the maximum value of usize. +#[no_mangle] +pub unsafe extern "C" fn tc_string_clone_with_len( + buf: *const libc::c_char, + len: usize, +) -> TCString { + debug_assert!(!buf.is_null()); + debug_assert!(len < isize::MAX as usize); + // SAFETY: + // - buf is valid for len bytes (by C convention) + // - (no alignment requirements for a byte slice) + // - content of buf will not be mutated during the lifetime of this slice (lifetime + // does not outlive this function call) + // - the length of the buffer is less than isize::MAX (promised by caller) + let slice = unsafe { std::slice::from_raw_parts(buf as *const u8, len) }; + + // allocate and copy into Rust-controlled memory + let vec = slice.to_vec(); + + // SAFETY: + // - caller promises to free this string + unsafe { TCString::return_val(RustString::Bytes(vec)) } +} + +/// Get the content of the string as a regular C string. The given string must be valid. The +/// returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The +/// returned C string is valid until the TCString is freed or passed to another TC API function. +/// +/// In general, prefer [`tc_string_content_with_len`] except when it's certain that the string is +/// valid and NUL-free. +/// +/// This function takes the TCString by pointer because it may be modified in-place to add a NUL +/// terminator. The pointer must not be NULL. +/// +/// This function does _not_ take ownership of the TCString. +#[no_mangle] +pub unsafe extern "C" fn tc_string_content(tcstring: *const TCString) -> *const libc::c_char { + // SAFETY; + // - tcstring is not NULL (promised by caller) + // - *tcstring is valid (promised by caller) + // - *tcstring is not accessed concurrently (single-threaded) + unsafe { + wrap(tcstring, |rstring| { + // try to eliminate the Bytes variant. If this fails, we'll return NULL + // below, so the error is ignorable. + let _ = rstring.bytes_to_string(); + + // and eliminate the String variant + rstring.string_to_cstring(); + + match &rstring { + RustString::CString(cstring) => cstring.as_ptr(), + RustString::String(_) => std::ptr::null(), // string_to_cstring failed + RustString::CStr(cstr) => cstr.as_ptr(), + RustString::Bytes(_) => std::ptr::null(), // already returned above + RustString::Null => unreachable!(), + } + }) + } +} + +/// Get the content of the string as a pointer and length. The given string must not be NULL. +/// This function can return any string, even one including NUL bytes or invalid UTF-8. The +/// returned buffer is valid until the TCString is freed or passed to another TaskChampio +/// function. +/// +/// This function takes the TCString by pointer because it may be modified in-place to add a NUL +/// terminator. The pointer must not be NULL. +/// +/// This function does _not_ take ownership of the TCString. +#[no_mangle] +pub unsafe extern "C" fn tc_string_content_with_len( + tcstring: *const TCString, + len_out: *mut usize, +) -> *const libc::c_char { + // SAFETY; + // - tcstring is not NULL (promised by caller) + // - *tcstring is valid (promised by caller) + // - *tcstring is not accessed concurrently (single-threaded) + unsafe { + wrap(tcstring, |rstring| { + let bytes = rstring.as_bytes(); + + // SAFETY: + // - len_out is not NULL (promised by caller) + // - len_out points to valid memory (promised by caller) + // - len_out is properly aligned (C convention) + usize::val_to_arg_out(bytes.len(), len_out); + bytes.as_ptr() as *const libc::c_char + }) + } +} + +/// Free a TCString. The given string must not be NULL. The string must not be used +/// after this function returns, and must not be freed more than once. +#[no_mangle] +pub unsafe extern "C" fn tc_string_free(tcstring: *mut TCString) { + // SAFETY: + // - tcstring is not NULL (promised by caller) + // - caller is exclusive owner of tcstring (promised by caller) + drop(unsafe { TCString::take_val_from_arg(tcstring, TCString::default()) }); +} + +/// Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. +#[no_mangle] +pub unsafe extern "C" fn tc_string_list_free(tcstrings: *mut TCStringList) { + // SAFETY: + // - tcstrings is not NULL and points to a valid TCStringList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_value_list(tcstrings) }; +} + +#[cfg(test)] +mod test { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn empty_list_has_non_null_pointer() { + let tcstrings = unsafe { TCStringList::return_val(Vec::new()) }; + assert!(!tcstrings.items.is_null()); + assert_eq!(tcstrings.len, 0); + assert_eq!(tcstrings._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tcstrings = unsafe { TCStringList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_string_list_free(&mut tcstrings) }; + assert!(tcstrings.items.is_null()); + assert_eq!(tcstrings.len, 0); + assert_eq!(tcstrings._capacity, 0); + } + + const INVALID_UTF8: &[u8] = b"abc\xf0\x28\x8c\x28"; + + fn make_cstring() -> RustString<'static> { + RustString::CString(CString::new("a string").unwrap()) + } + + fn make_cstr() -> RustString<'static> { + let cstr = CStr::from_bytes_with_nul(b"a string\0").unwrap(); + RustString::CStr(&cstr) + } + + fn make_string() -> RustString<'static> { + RustString::String("a string".into()) + } + + fn make_string_with_nul() -> RustString<'static> { + RustString::String("a \0 nul!".into()) + } + + fn make_invalid_bytes() -> RustString<'static> { + RustString::Bytes(INVALID_UTF8.to_vec()) + } + + fn make_bytes() -> RustString<'static> { + RustString::Bytes(b"bytes".to_vec()) + } + + #[test] + fn cstring_as_str() { + assert_eq!(make_cstring().as_str().unwrap(), "a string"); + } + + #[test] + fn cstr_as_str() { + assert_eq!(make_cstr().as_str().unwrap(), "a string"); + } + + #[test] + fn string_as_str() { + assert_eq!(make_string().as_str().unwrap(), "a string"); + } + + #[test] + fn string_with_nul_as_str() { + assert_eq!(make_string_with_nul().as_str().unwrap(), "a \0 nul!"); + } + + #[test] + fn invalid_bytes_as_str() { + let as_str_err = make_invalid_bytes().as_str().unwrap_err(); + assert_eq!(as_str_err.valid_up_to(), 3); // "abc" is valid + } + + #[test] + fn valid_bytes_as_str() { + assert_eq!(make_bytes().as_str().unwrap(), "bytes"); + } + + #[test] + fn cstring_as_bytes() { + assert_eq!(make_cstring().as_bytes(), b"a string"); + } + + #[test] + fn cstr_as_bytes() { + assert_eq!(make_cstr().as_bytes(), b"a string"); + } + + #[test] + fn string_as_bytes() { + assert_eq!(make_string().as_bytes(), b"a string"); + } + + #[test] + fn string_with_nul_as_bytes() { + assert_eq!(make_string_with_nul().as_bytes(), b"a \0 nul!"); + } + + #[test] + fn invalid_bytes_as_bytes() { + assert_eq!(make_invalid_bytes().as_bytes(), INVALID_UTF8); + } + + #[test] + fn cstring_string_to_cstring() { + let mut tcstring = make_cstring(); + tcstring.string_to_cstring(); + assert_eq!(tcstring, make_cstring()); // unchanged + } + + #[test] + fn cstr_string_to_cstring() { + let mut tcstring = make_cstr(); + tcstring.string_to_cstring(); + assert_eq!(tcstring, make_cstr()); // unchanged + } + + #[test] + fn string_string_to_cstring() { + let mut tcstring = make_string(); + tcstring.string_to_cstring(); + assert_eq!(tcstring, make_cstring()); // converted to CString, same content + } + + #[test] + fn string_with_nul_string_to_cstring() { + let mut tcstring = make_string_with_nul(); + tcstring.string_to_cstring(); + assert_eq!(tcstring, make_string_with_nul()); // unchanged + } + + #[test] + fn bytes_string_to_cstring() { + let mut tcstring = make_bytes(); + tcstring.string_to_cstring(); + assert_eq!(tcstring, make_bytes()); // unchanged + } +} diff --git a/lib/src/task.rs b/lib/src/task.rs new file mode 100644 index 000000000..c3c0b896b --- /dev/null +++ b/lib/src/task.rs @@ -0,0 +1,854 @@ +use crate::traits::*; +use crate::types::*; +use crate::util::err_to_ruststring; +use chrono::{TimeZone, Utc}; +use std::convert::TryFrom; +use std::ops::Deref; +use std::ptr::NonNull; +use std::str::FromStr; +use taskchampion::{Annotation, Tag, Task, TaskMut}; + +/// A task, as publicly exposed by this library. +/// +/// A task begins in "immutable" mode. It must be converted to "mutable" mode +/// to make any changes, and doing so requires exclusive access to the replica +/// until the task is freed or converted back to immutable mode. +/// +/// An immutable task carries no reference to the replica that created it, and can be used until it +/// is freed or converted to a TaskMut. A mutable task carries a reference to the replica and +/// must be freed or made immutable before the replica is freed. +/// +/// All `tc_task_..` functions taking a task as an argument require that it not be NULL. +/// +/// When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then +/// `tc_task_error` will return the error message. +/// +/// # Safety +/// +/// A task is an owned object, and must be freed with tc_task_free (or, if part of a list, +/// with tc_task_list_free). +/// +/// Any function taking a `*TCTask` requires: +/// - the pointer must not be NUL; +/// - the pointer must be one previously returned from a tc_… function; +/// - the memory referenced by the pointer must never be modified by C code; and +/// - except for `tc_{task,task_list}_free`, ownership of a `*TCTask` remains with the caller. +/// +/// Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again. +/// +/// TCTasks are not threadsafe. +pub struct TCTask { + /// The wrapped Task or TaskMut + inner: Inner, + + /// The error from the most recent operation, if any + error: Option>, +} + +enum Inner { + /// A regular, immutable task + Immutable(Task), + + /// A mutable task, together with the replica to which it holds an exclusive + /// reference. + Mutable(TaskMut<'static>, *mut TCReplica), + + /// A transitional state for a TCTask as it goes from mutable to immutable and back. A task + /// can only be in this state outside of [`to_mut`] and [`to_immut`] if a panic occurs during + /// one of those methods. + Invalid, +} + +impl PassByPointer for TCTask {} + +impl TCTask { + /// Make an immutable TCTask into a mutable TCTask. Does nothing if the task + /// is already mutable. + /// + /// # Safety + /// + /// The tcreplica pointer must not be NULL, and the replica it points to must not + /// be freed before TCTask.to_immut completes. + unsafe fn to_mut(&mut self, tcreplica: *mut TCReplica) { + self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) { + Inner::Immutable(task) => { + // SAFETY: + // - tcreplica is not null (promised by caller) + // - tcreplica outlives the pointer in this variant (promised by caller) + let tcreplica_ref: &mut TCReplica = + unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) }; + let rep_ref = tcreplica_ref.borrow_mut(); + Inner::Mutable(task.into_mut(rep_ref), tcreplica) + } + Inner::Mutable(task, tcreplica) => Inner::Mutable(task, tcreplica), + Inner::Invalid => unreachable!(), + } + } + + /// Make an mutable TCTask into a immutable TCTask. Does nothing if the task + /// is already immutable. + #[allow(clippy::wrong_self_convention)] // to_immut_mut is not better! + fn to_immut(&mut self) { + self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) { + Inner::Immutable(task) => Inner::Immutable(task), + Inner::Mutable(task, tcreplica) => { + // SAFETY: + // - tcreplica is not null (promised by caller of to_mut, which created this + // variant) + // - tcreplica is still alive (promised by caller of to_mut) + let tcreplica_ref: &mut TCReplica = + unsafe { TCReplica::from_ptr_arg_ref_mut(tcreplica) }; + tcreplica_ref.release_borrow(); + Inner::Immutable(task.into_immut()) + } + Inner::Invalid => unreachable!(), + } + } +} + +impl From for TCTask { + fn from(task: Task) -> TCTask { + TCTask { + inner: Inner::Immutable(task), + error: None, + } + } +} + +/// Utility function to get a shared reference to the underlying Task. All Task getters +/// are error-free, so this does not handle errors. +fn wrap(task: *mut TCTask, f: F) -> T +where + F: FnOnce(&Task) -> T, +{ + // SAFETY: + // - task is not null (promised by caller) + // - task outlives this function (promised by caller) + let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; + let task: &Task = match &tctask.inner { + Inner::Immutable(t) => t, + Inner::Mutable(t, _) => t.deref(), + Inner::Invalid => unreachable!(), + }; + tctask.error = None; + f(task) +} + +/// Utility function to get a mutable reference to the underlying Task. The +/// TCTask must be mutable. The inner function may use `?` syntax to return an +/// error, which will be represented with the `err_value` returned to C. +fn wrap_mut(task: *mut TCTask, f: F, err_value: T) -> T +where + F: FnOnce(&mut TaskMut) -> anyhow::Result, +{ + // SAFETY: + // - task is not null (promised by caller) + // - task outlives this function (promised by caller) + let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; + let task: &mut TaskMut = match tctask.inner { + Inner::Immutable(_) => panic!("Task is immutable"), + Inner::Mutable(ref mut t, _) => t, + Inner::Invalid => unreachable!(), + }; + tctask.error = None; + match f(task) { + Ok(rv) => rv, + Err(e) => { + tctask.error = Some(err_to_ruststring(e)); + err_value + } + } +} + +impl TryFrom> for Tag { + type Error = anyhow::Error; + + fn try_from(mut rstring: RustString) -> Result { + let tagstr = rstring.as_str()?; + Tag::from_str(tagstr) + } +} + +/// TCTaskList represents a list of tasks. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCTaskList { + /// number of tasks in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// array of pointers representing each task. these remain owned by the TCTaskList instance and + /// will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList, + /// and the *TCTaskList at indexes 0..len-1 are not NULL. + items: *const NonNull, +} + +impl CList for TCTaskList { + type Element = NonNull; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCTaskList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Convert an immutable task into a mutable task. +/// +/// The task must not be NULL. It is modified in-place, and becomes mutable. +/// +/// The replica must not be NULL. After this function returns, the replica _cannot be used at all_ +/// until this task is made immutable again. This implies that it is not allowed for more than one +/// task associated with a replica to be mutable at any time. +/// +/// Typical mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`: +/// +/// ```c +/// tc_task_to_mut(task, rep); +/// success = tc_task_done(task); +/// tc_task_to_immut(task, rep); +/// if (!success) { ... } +/// ``` +#[no_mangle] +pub unsafe extern "C" fn tc_task_to_mut(task: *mut TCTask, tcreplica: *mut TCReplica) { + // SAFETY: + // - task is not null (promised by caller) + // - task outlives 'a (promised by caller) + let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; + // SAFETY: + // - tcreplica is not NULL (promised by caller) + // - tcreplica lives until later call to to_immut via tc_task_to_immut (promised by caller, + // who cannot call tc_replica_free during this time) + unsafe { tctask.to_mut(tcreplica) }; +} + +/// Convert a mutable task into an immutable task. +/// +/// The task must not be NULL. It is modified in-place, and becomes immutable. +/// +/// The replica passed to `tc_task_to_mut` may be used freely after this call. +#[no_mangle] +pub unsafe extern "C" fn tc_task_to_immut(task: *mut TCTask) { + // SAFETY: + // - task is not null (promised by caller) + // - task outlives 'a (promised by caller) + let tctask: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; + tctask.to_immut(); +} + +/// Get a task's UUID. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_uuid(task: *mut TCTask) -> TCUuid { + wrap(task, |task| { + // SAFETY: + // - value is not allocated and need not be freed + unsafe { TCUuid::return_val(task.get_uuid()) } + }) +} + +/// Get a task's status. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_status(task: *mut TCTask) -> TCStatus { + wrap(task, |task| task.get_status().into()) +} + +/// Get the underlying key/value pairs for this task. The returned TCKVList is +/// a "snapshot" of the task and will not be updated if the task is subsequently +/// modified. It is the caller's responsibility to free the TCKVList. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_taskmap(task: *mut TCTask) -> TCKVList { + wrap(task, |task| { + let vec: Vec = task + .get_taskmap() + .iter() + .map(|(k, v)| { + let key = RustString::from(k.as_ref()); + let value = RustString::from(v.as_ref()); + TCKV::as_ctype((key, value)) + }) + .collect(); + // SAFETY: + // - caller will free this list + unsafe { TCKVList::return_val(vec) } + }) +} + +/// Get a task's description, or NULL if the task cannot be represented as a C string (e.g., if it +/// contains embedded NUL characters). +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_description(task: *mut TCTask) -> TCString { + wrap(task, |task| { + let descr = task.get_description(); + // SAFETY: + // - caller promises to free this string + unsafe { TCString::return_val(descr.into()) } + }) +} + +/// Get the entry timestamp for a task (when it was created), or 0 if not set. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_entry(task: *mut TCTask) -> libc::time_t { + wrap(task, |task| libc::time_t::as_ctype(task.get_entry())) +} + +/// Get the wait timestamp for a task, or 0 if not set. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_wait(task: *mut TCTask) -> libc::time_t { + wrap(task, |task| libc::time_t::as_ctype(task.get_wait())) +} + +/// Get the modified timestamp for a task, or 0 if not set. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_modified(task: *mut TCTask) -> libc::time_t { + wrap(task, |task| libc::time_t::as_ctype(task.get_modified())) +} + +/// Check if a task is waiting. +#[no_mangle] +pub unsafe extern "C" fn tc_task_is_waiting(task: *mut TCTask) -> bool { + wrap(task, |task| task.is_waiting()) +} + +/// Check if a task is active (started and not stopped). +#[no_mangle] +pub unsafe extern "C" fn tc_task_is_active(task: *mut TCTask) -> bool { + wrap(task, |task| task.is_active()) +} + +/// Check if a task has the given tag. If the tag is invalid, this function will return false, as +/// that (invalid) tag is not present. No error will be reported via `tc_task_error`. +#[no_mangle] +pub unsafe extern "C" fn tc_task_has_tag(task: *mut TCTask, tag: TCString) -> bool { + // SAFETY: + // - tag is valid (promised by caller) + // - caller will not use tag after this call (convention) + let tcstring = unsafe { TCString::val_from_arg(tag) }; + wrap(task, |task| { + if let Ok(tag) = Tag::try_from(tcstring) { + task.has_tag(&tag) + } else { + false + } + }) +} + +/// Get the tags for the task. +/// +/// The caller must free the returned TCStringList instance. The TCStringList instance does not +/// reference the task and the two may be freed in any order. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_tags(task: *mut TCTask) -> TCStringList { + wrap(task, |task| { + let vec: Vec = task + .get_tags() + .map(|t| { + // SAFETY: + // - this TCString will be freed via tc_string_list_free. + unsafe { TCString::return_val(t.as_ref().into()) } + }) + .collect(); + // SAFETY: + // - caller will free the list + unsafe { TCStringList::return_val(vec) } + }) +} + +/// Get the annotations for the task. +/// +/// The caller must free the returned TCAnnotationList instance. The TCStringList instance does not +/// reference the task and the two may be freed in any order. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_annotations(task: *mut TCTask) -> TCAnnotationList { + wrap(task, |task| { + let vec: Vec = task + .get_annotations() + .map(|a| { + let description = RustString::from(a.description); + TCAnnotation::as_ctype((a.entry, description)) + }) + .collect(); + // SAFETY: + // - caller will free the list + unsafe { TCAnnotationList::return_val(vec) } + }) +} + +/// Get the named UDA from the task. +/// +/// Returns a TCString with NULL ptr field if the UDA does not exist. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_uda<'a>( + task: *mut TCTask, + ns: TCString, + key: TCString, +) -> TCString { + wrap(task, |task| { + // SAFETY: + // - ns is valid (promised by caller) + // - caller will not use ns after this call (convention) + if let Ok(ns) = unsafe { TCString::val_from_arg(ns) }.as_str() { + // SAFETY: same + if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() { + if let Some(value) = task.get_uda(ns, key) { + // SAFETY: + // - caller will free this string (caller promises) + return unsafe { TCString::return_val(value.into()) }; + } + } + } + TCString::default() + }) +} + +/// Get the named legacy UDA from the task. +/// +/// Returns NULL if the UDA does not exist. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_legacy_uda<'a>(task: *mut TCTask, key: TCString) -> TCString { + wrap(task, |task| { + // SAFETY: + // - key is valid (promised by caller) + // - caller will not use key after this call (convention) + if let Ok(key) = unsafe { TCString::val_from_arg(key) }.as_str() { + if let Some(value) = task.get_legacy_uda(key) { + // SAFETY: + // - caller will free this string (caller promises) + return unsafe { TCString::return_val(value.into()) }; + } + } + TCString::default() + }) +} + +/// Get all UDAs for this task. +/// +/// Legacy UDAs are represented with an empty string in the ns field. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_udas(task: *mut TCTask) -> TCUdaList { + wrap(task, |task| { + let vec: Vec = task + .get_udas() + .map(|((ns, key), value)| { + // SAFETY: + // - will be freed by tc_uda_list_free + unsafe { + TCUda::return_val(Uda { + ns: Some(ns.into()), + key: key.into(), + value: value.into(), + }) + } + }) + .collect(); + // SAFETY: + // - caller will free this list + unsafe { TCUdaList::return_val(vec) } + }) +} + +/// Get all UDAs for this task. +/// +/// All TCUdas in this list have a NULL ns field. The entire UDA key is +/// included in the key field. The caller must free the returned list. +#[no_mangle] +pub unsafe extern "C" fn tc_task_get_legacy_udas(task: *mut TCTask) -> TCUdaList { + wrap(task, |task| { + let vec: Vec = task + .get_legacy_udas() + .map(|(key, value)| { + // SAFETY: + // - will be freed by tc_uda_list_free + unsafe { + TCUda::return_val(Uda { + ns: None, + key: key.into(), + value: value.into(), + }) + } + }) + .collect(); + // SAFETY: + // - caller will free this list + unsafe { TCUdaList::return_val(vec) } + }) +} + +/// Set a mutable task's status. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_status(task: *mut TCTask, status: TCStatus) -> TCResult { + wrap_mut( + task, + |task| { + task.set_status(status.into())?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a mutable task's description. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_description( + task: *mut TCTask, + description: TCString, +) -> TCResult { + // SAFETY: + // - description is valid (promised by caller) + // - caller will not use description after this call (convention) + let mut description = unsafe { TCString::val_from_arg(description) }; + wrap_mut( + task, + |task| { + task.set_description(description.as_str()?.to_string())?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a mutable task's entry (creation time). Pass entry=0 to unset +/// the entry field. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_entry(task: *mut TCTask, entry: libc::time_t) -> TCResult { + wrap_mut( + task, + |task| { + // SAFETY: any time_t value is a valid timestamp + task.set_entry(unsafe { entry.from_ctype() })?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_wait(task: *mut TCTask, wait: libc::time_t) -> TCResult { + wrap_mut( + task, + |task| { + // SAFETY: any time_t value is a valid timestamp + task.set_wait(unsafe { wait.from_ctype() })?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a mutable task's modified timestamp. The value cannot be zero. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_modified( + task: *mut TCTask, + modified: libc::time_t, +) -> TCResult { + wrap_mut( + task, + |task| { + task.set_modified( + // SAFETY: any time_t value is a valid timestamp + unsafe { modified.from_ctype() } + .ok_or_else(|| anyhow::anyhow!("modified cannot be zero"))?, + )?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Start a task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_start(task: *mut TCTask) -> TCResult { + wrap_mut( + task, + |task| { + task.start()?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Stop a task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_stop(task: *mut TCTask) -> TCResult { + wrap_mut( + task, + |task| { + task.stop()?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Mark a task as done. +#[no_mangle] +pub unsafe extern "C" fn tc_task_done(task: *mut TCTask) -> TCResult { + wrap_mut( + task, + |task| { + task.done()?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Mark a task as deleted. +#[no_mangle] +pub unsafe extern "C" fn tc_task_delete(task: *mut TCTask) -> TCResult { + wrap_mut( + task, + |task| { + task.delete()?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Add a tag to a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_add_tag(task: *mut TCTask, tag: TCString) -> TCResult { + // SAFETY: + // - tag is valid (promised by caller) + // - caller will not use tag after this call (convention) + let tcstring = unsafe { TCString::val_from_arg(tag) }; + wrap_mut( + task, + |task| { + let tag = Tag::try_from(tcstring)?; + task.add_tag(&tag)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Remove a tag from a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_remove_tag(task: *mut TCTask, tag: TCString) -> TCResult { + // SAFETY: + // - tag is valid (promised by caller) + // - caller will not use tag after this call (convention) + let tcstring = unsafe { TCString::val_from_arg(tag) }; + wrap_mut( + task, + |task| { + let tag = Tag::try_from(tcstring)?; + task.remove_tag(&tag)?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Add an annotation to a mutable task. This call takes ownership of the +/// passed annotation, which must not be used after the call returns. +#[no_mangle] +pub unsafe extern "C" fn tc_task_add_annotation( + task: *mut TCTask, + annotation: *mut TCAnnotation, +) -> TCResult { + // SAFETY: + // - annotation is not NULL (promised by caller) + // - annotation is return from a tc_string_.. so is valid + // - caller will not use annotation after this call + let (entry, description) = + unsafe { TCAnnotation::take_val_from_arg(annotation, TCAnnotation::default()) }; + wrap_mut( + task, + |task| { + let description = description.into_string()?; + task.add_annotation(Annotation { entry, description })?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Remove an annotation from a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64) -> TCResult { + wrap_mut( + task, + |task| { + task.remove_annotation(Utc.timestamp(entry, 0))?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a UDA on a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_uda( + task: *mut TCTask, + ns: TCString, + key: TCString, + value: TCString, +) -> TCResult { + // safety: + // - ns is valid (promised by caller) + // - caller will not use ns after this call (convention) + let mut ns = unsafe { TCString::val_from_arg(ns) }; + // SAFETY: same + let mut key = unsafe { TCString::val_from_arg(key) }; + // SAFETY: same + let mut value = unsafe { TCString::val_from_arg(value) }; + wrap_mut( + task, + |task| { + task.set_uda( + ns.as_str()?.to_string(), + key.as_str()?.to_string(), + value.as_str()?.to_string(), + )?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Remove a UDA fraom a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_remove_uda( + task: *mut TCTask, + ns: TCString, + key: TCString, +) -> TCResult { + // safety: + // - ns is valid (promised by caller) + // - caller will not use ns after this call (convention) + let mut ns = unsafe { TCString::val_from_arg(ns) }; + // SAFETY: same + let mut key = unsafe { TCString::val_from_arg(key) }; + wrap_mut( + task, + |task| { + task.remove_uda(ns.as_str()?.to_string(), key.as_str()?.to_string())?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Set a legacy UDA on a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_set_legacy_uda( + task: *mut TCTask, + key: TCString, + value: TCString, +) -> TCResult { + // safety: + // - key is valid (promised by caller) + // - caller will not use key after this call (convention) + let mut key = unsafe { TCString::val_from_arg(key) }; + // SAFETY: same + let mut value = unsafe { TCString::val_from_arg(value) }; + wrap_mut( + task, + |task| { + task.set_legacy_uda(key.as_str()?.to_string(), value.as_str()?.to_string())?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Remove a UDA fraom a mutable task. +#[no_mangle] +pub unsafe extern "C" fn tc_task_remove_legacy_uda(task: *mut TCTask, key: TCString) -> TCResult { + // safety: + // - key is valid (promised by caller) + // - caller will not use key after this call (convention) + let mut key = unsafe { TCString::val_from_arg(key) }; + wrap_mut( + task, + |task| { + task.remove_legacy_uda(key.as_str()?.to_string())?; + Ok(TCResult::Ok) + }, + TCResult::Error, + ) +} + +/// Get the latest error for a task, or a string NULL ptr field if the last operation succeeded. +/// Subsequent calls to this function will return NULL. The task pointer must not be NULL. The +/// caller must free the returned string. +#[no_mangle] +pub unsafe extern "C" fn tc_task_error(task: *mut TCTask) -> TCString { + // SAFETY: + // - task is not null (promised by caller) + // - task outlives 'a (promised by caller) + let task: &mut TCTask = unsafe { TCTask::from_ptr_arg_ref_mut(task) }; + if let Some(rstring) = task.error.take() { + // SAFETY: + // - caller promises to free this value + unsafe { TCString::return_val(rstring) } + } else { + TCString::default() + } +} + +/// Free a task. The given task must not be NULL. The task must not be used after this function +/// returns, and must not be freed more than once. +/// +/// If the task is currently mutable, it will first be made immutable. +#[no_mangle] +pub unsafe extern "C" fn tc_task_free(task: *mut TCTask) { + // SAFETY: + // - task is not NULL (promised by caller) + // - caller will not use the TCTask after this (promised by caller) + let mut tctask = unsafe { TCTask::take_from_ptr_arg(task) }; + + // convert to immut if it was mutable + tctask.to_immut(); + + drop(tctask); +} + +/// Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList. +#[no_mangle] +pub unsafe extern "C" fn tc_task_list_free(tctasks: *mut TCTaskList) { + // SAFETY: + // - tctasks is not NULL and points to a valid TCTaskList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_pointer_list(tctasks) }; +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_list_has_non_null_pointer() { + let tctasks = unsafe { TCTaskList::return_val(Vec::new()) }; + assert!(!tctasks.items.is_null()); + assert_eq!(tctasks.len, 0); + assert_eq!(tctasks._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tctasks = unsafe { TCTaskList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_task_list_free(&mut tctasks) }; + assert!(tctasks.items.is_null()); + assert_eq!(tctasks.len, 0); + assert_eq!(tctasks._capacity, 0); + } +} diff --git a/lib/src/traits.rs b/lib/src/traits.rs new file mode 100644 index 000000000..ffbdf9a79 --- /dev/null +++ b/lib/src/traits.rs @@ -0,0 +1,272 @@ +use crate::util::vec_into_raw_parts; +use std::ptr::NonNull; + +/// Support for values passed to Rust by value. These are represented as full structs in C. Such +/// values are implicitly copyable, via C's struct assignment. +/// +/// The Rust and C types may differ, with from_ctype and as_ctype converting between them. +/// Implement this trait for the C type. +/// +/// The RustType must be droppable (not containing raw pointers). +pub(crate) trait PassByValue: Sized { + type RustType; + + /// Convert a C value to a Rust value. + /// + /// # Safety + /// + /// `self` must be a valid CType. + #[allow(clippy::wrong_self_convention)] + unsafe fn from_ctype(self) -> Self::RustType; + + /// Convert a Rust value to a C value. + fn as_ctype(arg: Self::RustType) -> Self; + + /// Take a value from C as an argument. + /// + /// # Safety + /// + /// - `self` must be a valid instance of the C type. This is typically ensured either by + /// requiring that C code not modify it, or by defining the valid values in C comments. + unsafe fn val_from_arg(arg: Self) -> Self::RustType { + // SAFETY: + // - arg is a valid CType (promised by caller) + unsafe { arg.from_ctype() } + } + + /// Take a value from C as a pointer argument, replacing it with the given value. This is used + /// to invalidate the C value as an additional assurance against subsequent use of the value. + /// + /// # Safety + /// + /// - arg must not be NULL + /// - *arg must be a valid, properly aligned instance of the C type + unsafe fn take_val_from_arg(arg: *mut Self, mut replacement: Self) -> Self::RustType { + // SAFETY: + // - arg is valid (promised by caller) + // - replacement is valid and aligned (guaranteed by Rust) + unsafe { std::ptr::swap(arg, &mut replacement) }; + // SAFETY: + // - replacement (formerly *arg) is a valid CType (promised by caller) + unsafe { PassByValue::val_from_arg(replacement) } + } + + /// Return a value to C + /// + /// # Safety + /// + /// - if the value is allocated, the caller must ensure that the value is eventually freed + unsafe fn return_val(arg: Self::RustType) -> Self { + Self::as_ctype(arg) + } + + /// Return a value to C, via an "output parameter" + /// + /// # Safety + /// + /// - `arg_out` must not be NULL and must be properly aligned and pointing to valid memory + /// of the size of CType. + unsafe fn val_to_arg_out(val: Self::RustType, arg_out: *mut Self) { + debug_assert!(!arg_out.is_null()); + // SAFETY: + // - arg_out is not NULL (promised by caller, asserted) + // - arg_out is properly aligned and points to valid memory (promised by caller) + unsafe { *arg_out = Self::as_ctype(val) }; + } +} + +/// Support for values passed to Rust by pointer. These are represented as opaque structs in C, +/// and always handled as pointers. +pub(crate) trait PassByPointer: Sized { + /// Take a value from C as an argument. + /// + /// # Safety + /// + /// - arg must not be NULL + /// - arg must be a value returned from Box::into_raw (via return_ptr or ptr_to_arg_out) + /// - arg becomes invalid and must not be used after this call + unsafe fn take_from_ptr_arg(arg: *mut Self) -> Self { + debug_assert!(!arg.is_null()); + // SAFETY: see docstring + unsafe { *(Box::from_raw(arg)) } + } + + /// Borrow a value from C as an argument. + /// + /// # Safety + /// + /// - arg must not be NULL + /// - *arg must be a valid instance of Self + /// - arg must be valid for the lifetime assigned by the caller + /// - arg must not be modified by anything else during that lifetime + unsafe fn from_ptr_arg_ref<'a>(arg: *const Self) -> &'a Self { + debug_assert!(!arg.is_null()); + // SAFETY: see docstring + unsafe { &*arg } + } + + /// Mutably borrow a value from C as an argument. + /// + /// # Safety + /// + /// - arg must not be NULL + /// - *arg must be a valid instance of Self + /// - arg must be valid for the lifetime assigned by the caller + /// - arg must not be accessed by anything else during that lifetime + unsafe fn from_ptr_arg_ref_mut<'a>(arg: *mut Self) -> &'a mut Self { + debug_assert!(!arg.is_null()); + // SAFETY: see docstring + unsafe { &mut *arg } + } + + /// Return a value to C, transferring ownership + /// + /// # Safety + /// + /// - the caller must ensure that the value is eventually freed + unsafe fn return_ptr(self) -> *mut Self { + Box::into_raw(Box::new(self)) + } + + /// Return a value to C, transferring ownership, via an "output parameter". + /// + /// # Safety + /// + /// - the caller must ensure that the value is eventually freed + /// - arg_out must not be NULL + /// - arg_out must point to valid, properly aligned memory for a pointer value + unsafe fn ptr_to_arg_out(self, arg_out: *mut *mut Self) { + debug_assert!(!arg_out.is_null()); + // SAFETY: see docstring + unsafe { *arg_out = self.return_ptr() }; + } +} + +/// Support for C lists of objects referenced by value. +/// +/// The underlying C type should have three fields, containing items, length, and capacity. The +/// required trait functions just fetch and set these fields. +/// +/// The PassByValue trait will be implemented automatically, converting between the C type and +/// `Vec`. +/// +/// For most cases, it is only necessary to implement `tc_.._free` that calls either +/// drop_value_list (if Element is PassByValue) or drop_pointer_list (if element is PassByPointer). +/// +/// # Safety +/// +/// The C type must be documented as read-only. None of the fields may be modified, nor anything +/// accessible via the `items` array. +/// +/// This class guarantees that the items pointer is non-NULL for any valid list (even when len=0). +pub(crate) trait CList: Sized { + type Element; + + /// Create a new CList from the given items, len, and capacity. + /// + /// # Safety + /// + /// The arguments must either: + /// - be NULL, 0, and 0, respectively; or + /// - be valid for Vec::from_raw_parts + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self; + + /// Get the items, len, and capacity (in that order) for this instance. These must be + /// precisely the same values passed tearlier to `from_raw_parts`. + fn into_raw_parts(self) -> (*const Self::Element, usize, usize); + + /// Generate a NULL value. By default this is a NULL items pointer with zero length and + /// capacity. + fn null_value() -> Self { + // SAFETY: + // - satisfies the first case in from_raw_parts' safety documentation + unsafe { Self::from_raw_parts(std::ptr::null(), 0, 0) } + } +} + +/// Given a CList containing pass-by-value values, drop all of the values and +/// the list. +/// +/// This is a convenience function for `tc_.._list_free` functions. +/// +/// # Safety +/// +/// - List must be non-NULL and point to a valid CL instance +/// - The caller must not use the value array points to after this function, as +/// it has been freed. It will be replaced with the null value. +pub(crate) unsafe fn drop_value_list(list: *mut CL) +where + CL: CList, + T: PassByValue, +{ + debug_assert!(!list.is_null()); + + // SAFETY: + // - *list is a valid CL (promised by caller) + let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) }; + + // first, drop each of the elements in turn + for e in vec.drain(..) { + // SAFETY: + // - e is a valid Element (promised by caller) + // - e is owned + drop(unsafe { PassByValue::val_from_arg(e) }); + } + // then drop the vector + drop(vec); +} + +/// Given a CList containing NonNull pointers, drop all of the pointed-to values and the list. +/// +/// This is a convenience function for `tc_.._list_free` functions. +/// +/// # Safety +/// +/// - List must be non-NULL and point to a valid CL instance +/// - The caller must not use the value array points to after this function, as +/// it has been freed. It will be replaced with the null value. +pub(crate) unsafe fn drop_pointer_list(list: *mut CL) +where + CL: CList>, + T: PassByPointer, +{ + debug_assert!(!list.is_null()); + // SAFETY: + // - *list is a valid CL (promised by caller) + let mut vec = unsafe { CL::take_val_from_arg(list, CL::null_value()) }; + + // first, drop each of the elements in turn + for e in vec.drain(..) { + // SAFETY: + // - e is a valid Element (promised by caller) + // - e is owned + drop(unsafe { PassByPointer::take_from_ptr_arg(e.as_ptr()) }); + } + // then drop the vector + drop(vec); +} + +impl PassByValue for A +where + A: CList, +{ + type RustType = Vec; + + unsafe fn from_ctype(self) -> Self::RustType { + let (items, len, cap) = self.into_raw_parts(); + debug_assert!(!items.is_null()); + // SAFETY: + // - CList::from_raw_parts requires that items, len, and cap be valid for + // Vec::from_raw_parts if not NULL, and they are not NULL (as promised by caller) + // - CList::into_raw_parts returns precisely the values passed to from_raw_parts. + // - those parts are passed to Vec::from_raw_parts here. + unsafe { Vec::from_raw_parts(items as *mut _, len, cap) } + } + + fn as_ctype(arg: Self::RustType) -> Self { + let (items, len, cap) = vec_into_raw_parts(arg); + // SAFETY: + // - satisfies the second case in from_raw_parts' safety documentation + unsafe { Self::from_raw_parts(items, len, cap) } + } +} diff --git a/lib/src/uda.rs b/lib/src/uda.rs new file mode 100644 index 000000000..607d77e27 --- /dev/null +++ b/lib/src/uda.rs @@ -0,0 +1,148 @@ +use crate::traits::*; +use crate::types::*; + +/// TCUda contains the details of a UDA. +#[repr(C)] +pub struct TCUda { + /// Namespace of the UDA. For legacy UDAs, this may have a NULL ptr field. + pub ns: TCString, + /// UDA key. Must not be NULL. + pub key: TCString, + /// Content of the UDA. Must not be NULL. + pub value: TCString, +} + +pub(crate) struct Uda { + pub ns: Option>, + pub key: RustString<'static>, + pub value: RustString<'static>, +} + +impl PassByValue for TCUda { + type RustType = Uda; + + unsafe fn from_ctype(self) -> Self::RustType { + Uda { + ns: if self.ns.is_null() { + None + } else { + // SAFETY: + // - self is owned, so we can take ownership of this TCString + // - self.ns is a valid, non-null TCString (NULL just checked) + Some(unsafe { TCString::val_from_arg(self.ns) }) + }, + // SAFETY: + // - self is owned, so we can take ownership of this TCString + // - self.key is a valid, non-null TCString (see type docstring) + key: unsafe { TCString::val_from_arg(self.key) }, + // SAFETY: + // - self is owned, so we can take ownership of this TCString + // - self.value is a valid, non-null TCString (see type docstring) + value: unsafe { TCString::val_from_arg(self.value) }, + } + } + + fn as_ctype(uda: Uda) -> Self { + TCUda { + // SAFETY: caller assumes ownership of this value + ns: if let Some(ns) = uda.ns { + unsafe { TCString::return_val(ns) } + } else { + TCString::default() + }, + // SAFETY: caller assumes ownership of this value + key: unsafe { TCString::return_val(uda.key) }, + // SAFETY: caller assumes ownership of this value + value: unsafe { TCString::return_val(uda.value) }, + } + } +} + +impl Default for TCUda { + fn default() -> Self { + TCUda { + ns: TCString::default(), + key: TCString::default(), + value: TCString::default(), + } + } +} + +/// TCUdaList represents a list of UDAs. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCUdaList { + /// number of UDAs in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// array of UDAs. These remain owned by the TCUdaList instance and will be freed by + /// tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. + items: *const TCUda, +} + +impl CList for TCUdaList { + type Element = TCUda; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCUdaList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Free a TCUda instance. The instance, and the TCStrings it contains, must not be used +/// after this call. +#[no_mangle] +pub unsafe extern "C" fn tc_uda_free(tcuda: *mut TCUda) { + debug_assert!(!tcuda.is_null()); + // SAFETY: + // - *tcuda is a valid TCUda (caller promises to treat it as read-only) + let uda = unsafe { TCUda::take_val_from_arg(tcuda, TCUda::default()) }; + drop(uda); +} + +/// Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList. +#[no_mangle] +pub unsafe extern "C" fn tc_uda_list_free(tcudas: *mut TCUdaList) { + // SAFETY: + // - tcudas is not NULL and points to a valid TCUdaList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_value_list(tcudas) } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_list_has_non_null_pointer() { + let tcudas = unsafe { TCUdaList::return_val(Vec::new()) }; + assert!(!tcudas.items.is_null()); + assert_eq!(tcudas.len, 0); + assert_eq!(tcudas._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tcudas = unsafe { TCUdaList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_uda_list_free(&mut tcudas) }; + assert!(tcudas.items.is_null()); + assert_eq!(tcudas.len, 0); + assert_eq!(tcudas._capacity, 0); + } +} diff --git a/lib/src/util.rs b/lib/src/util.rs new file mode 100644 index 000000000..bfd739282 --- /dev/null +++ b/lib/src/util.rs @@ -0,0 +1,23 @@ +use crate::string::RustString; + +pub(crate) fn err_to_ruststring(e: impl std::string::ToString) -> RustString<'static> { + RustString::from(e.to_string()) +} + +/// An implementation of Vec::into_raw_parts, which is still unstable. Returns ptr, len, cap. +pub(crate) fn vec_into_raw_parts(vec: Vec) -> (*mut T, usize, usize) { + // emulate Vec::into_raw_parts(): + // - disable dropping the Vec with ManuallyDrop + // - extract ptr, len, and capacity using those methods + let mut vec = std::mem::ManuallyDrop::new(vec); + (vec.as_mut_ptr(), vec.len(), vec.capacity()) +} + +/// An implementation of String::into_raw_parts, which is still unstable. Returns ptr, len, cap. +pub(crate) fn string_into_raw_parts(string: String) -> (*mut u8, usize, usize) { + // emulate String::into_raw_parts(): + // - disable dropping the String with ManuallyDrop + // - extract ptr, len, and capacity using those methods + let mut string = std::mem::ManuallyDrop::new(string); + (string.as_mut_ptr(), string.len(), string.capacity()) +} diff --git a/lib/src/uuid.rs b/lib/src/uuid.rs new file mode 100644 index 000000000..c4ec8f474 --- /dev/null +++ b/lib/src/uuid.rs @@ -0,0 +1,168 @@ +use crate::traits::*; +use crate::types::*; +use libc; +use taskchampion::Uuid; + +// NOTE: this must be a simple constant so that cbindgen can evaluate it +/// Length, in bytes, of the string representation of a UUID (without NUL terminator) +pub const TC_UUID_STRING_BYTES: usize = 36; + +/// TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed. +/// Uuids are typically treated as opaque, but the bytes are available in big-endian format. +/// +/// cbindgen:field-names=[bytes] +#[repr(C)] +pub struct TCUuid([u8; 16]); + +impl PassByValue for TCUuid { + type RustType = Uuid; + + unsafe fn from_ctype(self) -> Self::RustType { + // SAFETY: + // - any 16-byte value is a valid Uuid + Uuid::from_bytes(self.0) + } + + fn as_ctype(arg: Uuid) -> Self { + TCUuid(*arg.as_bytes()) + } +} + +/// Create a new, randomly-generated UUID. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_new_v4() -> TCUuid { + // SAFETY: + // - value is not allocated + unsafe { TCUuid::return_val(Uuid::new_v4()) } +} + +/// Create a new UUID with the nil value. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_nil() -> TCUuid { + // SAFETY: + // - value is not allocated + unsafe { TCUuid::return_val(Uuid::nil()) } +} + +/// TCUuidList represents a list of uuids. +/// +/// The content of this struct must be treated as read-only. +#[repr(C)] +pub struct TCUuidList { + /// number of uuids in items + len: libc::size_t, + + /// total size of items (internal use only) + _capacity: libc::size_t, + + /// array of uuids. these remain owned by the TCUuidList instance and will be freed by + /// tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList. + items: *const TCUuid, +} + +impl CList for TCUuidList { + type Element = TCUuid; + + unsafe fn from_raw_parts(items: *const Self::Element, len: usize, cap: usize) -> Self { + TCUuidList { + len, + _capacity: cap, + items, + } + } + + fn into_raw_parts(self) -> (*const Self::Element, usize, usize) { + (self.items, self.len, self._capacity) + } +} + +/// Write the string representation of a TCUuid into the given buffer, which must be +/// at least TC_UUID_STRING_BYTES long. No NUL terminator is added. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_to_buf(tcuuid: TCUuid, buf: *mut libc::c_char) { + debug_assert!(!buf.is_null()); + // SAFETY: + // - buf is valid for len bytes (by C convention) + // - (no alignment requirements for a byte slice) + // - content of buf will not be mutated during the lifetime of this slice (lifetime + // does not outlive this function call) + // - the length of the buffer is less than isize::MAX (promised by caller) + let buf: &mut [u8] = unsafe { + std::slice::from_raw_parts_mut(buf as *mut u8, ::uuid::adapter::Hyphenated::LENGTH) + }; + // SAFETY: + // - tcuuid is a valid TCUuid (all byte patterns are valid) + let uuid: Uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; + uuid.to_hyphenated().encode_lower(buf); +} + +/// Return the hyphenated string representation of a TCUuid. The returned string +/// must be freed with tc_string_free. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_to_str(tcuuid: TCUuid) -> TCString { + // SAFETY: + // - tcuuid is a valid TCUuid (all byte patterns are valid) + let uuid: Uuid = unsafe { TCUuid::val_from_arg(tcuuid) }; + let s = uuid.to_string(); + // SAFETY: + // - caller promises to free this value. + unsafe { TCString::return_val(s.into()) } +} + +/// Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given +/// string is not valid. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_from_str(s: TCString, uuid_out: *mut TCUuid) -> TCResult { + debug_assert!(!s.is_null()); + debug_assert!(!uuid_out.is_null()); + // SAFETY: + // - s is valid (promised by caller) + // - caller will not use s after this call (convention) + let mut s = unsafe { TCString::val_from_arg(s) }; + if let Ok(s) = s.as_str() { + if let Ok(u) = Uuid::parse_str(s) { + // SAFETY: + // - uuid_out is not NULL (promised by caller) + // - alignment is not required + unsafe { TCUuid::val_to_arg_out(u, uuid_out) }; + return TCResult::Ok; + } + } + TCResult::Error +} + +/// Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after +/// this call. +/// +/// When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList. +#[no_mangle] +pub unsafe extern "C" fn tc_uuid_list_free(tcuuids: *mut TCUuidList) { + // SAFETY: + // - tcuuids is not NULL and points to a valid TCUuidList (caller is not allowed to + // modify the list) + // - caller promises not to use the value after return + unsafe { drop_value_list(tcuuids) }; +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_list_has_non_null_pointer() { + let tcuuids = unsafe { TCUuidList::return_val(Vec::new()) }; + assert!(!tcuuids.items.is_null()); + assert_eq!(tcuuids.len, 0); + assert_eq!(tcuuids._capacity, 0); + } + + #[test] + fn free_sets_null_pointer() { + let mut tcuuids = unsafe { TCUuidList::return_val(Vec::new()) }; + // SAFETY: testing expected behavior + unsafe { tc_uuid_list_free(&mut tcuuids) }; + assert!(tcuuids.items.is_null()); + assert_eq!(tcuuids.len, 0); + assert_eq!(tcuuids._capacity, 0); + } +} diff --git a/lib/src/workingset.rs b/lib/src/workingset.rs new file mode 100644 index 000000000..672194886 --- /dev/null +++ b/lib/src/workingset.rs @@ -0,0 +1,103 @@ +use crate::traits::*; +use crate::types::*; +use taskchampion::{Uuid, WorkingSet}; + +/// A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically +/// updated based on changes in the replica. Its lifetime is independent of the replica and it can +/// be freed at any time. +/// +/// To iterate over a working set, search indexes 1 through largest_index. +/// +/// # Safety +/// +/// The `*TCWorkingSet` returned from `tc_replica_working_set` is owned by the caller and +/// must later be freed to avoid a memory leak. Its lifetime is independent of the replica +/// from which it was generated. +/// +/// Any function taking a `*TCWorkingSet` requires: +/// - the pointer must not be NUL; +/// - the pointer must be one previously returned from `tc_replica_working_set` +/// - the memory referenced by the pointer must never be accessed by C code; and +/// - except for `tc_replica_free`, ownership of a `*TCWorkingSet` remains with the caller. +/// +/// Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again. +/// +/// TCWorkingSet is not threadsafe. +pub struct TCWorkingSet(WorkingSet); + +impl PassByPointer for TCWorkingSet {} + +impl From for TCWorkingSet { + fn from(ws: WorkingSet) -> TCWorkingSet { + TCWorkingSet(ws) + } +} + +/// Utility function to get a shared reference to the underlying WorkingSet. +fn wrap(ws: *mut TCWorkingSet, f: F) -> T +where + F: FnOnce(&WorkingSet) -> T, +{ + // SAFETY: + // - ws is not null (promised by caller) + // - ws outlives 'a (promised by caller) + let tcws: &TCWorkingSet = unsafe { TCWorkingSet::from_ptr_arg_ref(ws) }; + f(&tcws.0) +} + +/// Get the working set's length, or the number of UUIDs it contains. +#[no_mangle] +pub unsafe extern "C" fn tc_working_set_len(ws: *mut TCWorkingSet) -> usize { + wrap(ws, |ws| ws.len()) +} + +/// Get the working set's largest index. +#[no_mangle] +pub unsafe extern "C" fn tc_working_set_largest_index(ws: *mut TCWorkingSet) -> usize { + wrap(ws, |ws| ws.largest_index()) +} + +/// Get the UUID for the task at the given index. Returns true if the UUID exists in the working +/// set. If not, returns false and does not change uuid_out. +#[no_mangle] +pub unsafe extern "C" fn tc_working_set_by_index( + ws: *mut TCWorkingSet, + index: usize, + uuid_out: *mut TCUuid, +) -> bool { + debug_assert!(!uuid_out.is_null()); + wrap(ws, |ws| { + if let Some(uuid) = ws.by_index(index) { + // SAFETY: + // - uuid_out is not NULL (promised by caller) + // - alignment is not required + unsafe { TCUuid::val_to_arg_out(uuid, uuid_out) }; + true + } else { + false + } + }) +} + +/// Get the working set index for the task with the given UUID. Returns 0 if the task is not in +/// the working set. +#[no_mangle] +pub unsafe extern "C" fn tc_working_set_by_uuid(ws: *mut TCWorkingSet, uuid: TCUuid) -> usize { + wrap(ws, |ws| { + // SAFETY: + // - tcuuid is a valid TCUuid (all byte patterns are valid) + let uuid: Uuid = unsafe { TCUuid::val_from_arg(uuid) }; + ws.by_uuid(uuid).unwrap_or(0) + }) +} + +/// Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this +/// function returns, and must not be freed more than once. +#[no_mangle] +pub unsafe extern "C" fn tc_working_set_free(ws: *mut TCWorkingSet) { + // SAFETY: + // - rep is not NULL (promised by caller) + // - caller will not use the TCWorkingSet after this (promised by caller) + let ws = unsafe { TCWorkingSet::take_from_ptr_arg(ws) }; + drop(ws); +} diff --git a/lib/taskchampion.h b/lib/taskchampion.h new file mode 100644 index 000000000..f09bddfe1 --- /dev/null +++ b/lib/taskchampion.h @@ -0,0 +1,1011 @@ +/** + * TaskChampion + * + * This file defines the C interface to libtaskchampion. This is a thin + * wrapper around the Rust `taskchampion` crate. Refer to the documentation + * for that crate at https://docs.rs/taskchampion/latest/taskchampion/ for API + * details. The comments in this file focus mostly on the low-level details of + * passing values to and from TaskChampion. + * + * # Overview + * + * This library defines two major types used to interact with the API, that map directly + * to Rust types. + * + * * TCReplica - see https://docs.rs/taskchampion/latest/taskchampion/struct.Replica.html + * * TCTask - see https://docs.rs/taskchampion/latest/taskchampion/struct.Task.html + * * TCServer - see https://docs.rs/taskchampion/latest/taskchampion/trait.Server.html + * * TCWorkingSet - see https://docs.rs/taskchampion/latest/taskchampion/struct.WorkingSet.html + * + * It also defines a few utility types: + * + * * TCString - a wrapper around both C (NUL-terminated) and Rust (always utf-8) strings. + * * TC…List - a list of objects represented as a C array + * * see below for the remainder + * + * # Safety + * + * Each type contains specific instructions to ensure memory safety. + * The general rules are as follows. + * + * No types in this library are threadsafe. All values should be used in only + * one thread for their entire lifetime. It is safe to use unrelated values in + * different threads (for example, different threads may use different + * TCReplica values concurrently). + * + * ## Pass by Pointer + * + * Several types such as TCReplica and TCString are "opaque" types and always + * handled as pointers in C. The bytes these pointers address are private to + * the Rust implemetation and must not be accessed from C. + * + * Pass-by-pointer values have exactly one owner, and that owner is responsible + * for freeing the value (using a `tc_…_free` function), or transferring + * ownership elsewhere. Except where documented otherwise, when a value is + * passed to C, ownership passes to C as well. When a value is passed to Rust, + * ownership stays with the C code. The exception is TCString, ownership of + * which passes to Rust when it is used as a function argument. + * + * The limited circumstances where one value must not outlive another, due to + * pointer references between them, are documented below. + * + * ## Pass by Value + * + * Types such as TCUuid and TC…List are passed by value, and contain fields + * that are accessible from C. C code is free to access the content of these + * types in a _read_only_ fashion. + * + * Pass-by-value values that contain pointers also have exactly one owner, + * responsible for freeing the value or transferring ownership. The tc_…_free + * functions for these types will replace the pointers with NULL to guard + * against use-after-free errors. The interior pointers in such values should + * never be freed directly (for example, `tc_string_free(tcuda.value)` is an + * error). + * + * TCUuid is a special case, because it does not contain pointers. It can be + * freely copied and need not be freed. + * + * ## Lists + * + * Lists are a special kind of pass-by-value type. Each contains `len` and + * `items`, where `items` is an array of length `len`. Lists, and the values + * in the `items` array, must be treated as read-only. On return from an API + * function, a list's ownership is with the C caller, which must eventually + * free the list. List data must be freed with the `tc_…_list_free` function. + * It is an error to free any value in the `items` array of a list. + */ + + +#include +#include +#include + +/** + * Length, in bytes, of the string representation of a UUID (without NUL terminator) + */ +#define TC_UUID_STRING_BYTES 36 + +/** + * A result from a TC operation. Typically if this value is TC_RESULT_ERROR, + * the associated object's `tc_.._error` method will return an error message. + */ +enum TCResult +#ifdef __cplusplus + : int32_t +#endif // __cplusplus + { + TC_RESULT_ERROR = -1, + TC_RESULT_OK = 0, +}; +#ifndef __cplusplus +typedef int32_t TCResult; +#endif // __cplusplus + +/** + * The status of a task, as defined by the task data model. + */ +typedef enum TCStatus { + TC_STATUS_PENDING, + TC_STATUS_COMPLETED, + TC_STATUS_DELETED, + /** + * Unknown signifies a status in the task DB that was not + * recognized. + */ + TC_STATUS_UNKNOWN, +} TCStatus; + +/** + * A replica represents an instance of a user's task data, providing an easy interface + * for querying and modifying that data. + * + * # Error Handling + * + * When a `tc_replica_..` function that returns a TCResult returns TC_RESULT_ERROR, then + * `tc_replica_error` will return the error message. + * + * # Safety + * + * The `*TCReplica` returned from `tc_replica_new…` functions is owned by the caller and + * must later be freed to avoid a memory leak. + * + * Any function taking a `*TCReplica` requires: + * - the pointer must not be NUL; + * - the pointer must be one previously returned from a tc_… function; + * - the memory referenced by the pointer must never be modified by C code; and + * - except for `tc_replica_free`, ownership of a `*TCReplica` remains with the caller. + * + * Once passed to `tc_replica_free`, a `*TCReplica` becomes invalid and must not be used again. + * + * TCReplicas are not threadsafe. + */ +typedef struct TCReplica TCReplica; + +/** + * TCServer represents an interface to a sync server. Aside from new and free, a server + * has no C-accessible API, but is designed to be passed to `tc_replica_sync`. + * + * ## Safety + * + * TCServer are not threadsafe, and must not be used with multiple replicas simultaneously. + */ +typedef struct TCServer TCServer; + +/** + * A task, as publicly exposed by this library. + * + * A task begins in "immutable" mode. It must be converted to "mutable" mode + * to make any changes, and doing so requires exclusive access to the replica + * until the task is freed or converted back to immutable mode. + * + * An immutable task carries no reference to the replica that created it, and can be used until it + * is freed or converted to a TaskMut. A mutable task carries a reference to the replica and + * must be freed or made immutable before the replica is freed. + * + * All `tc_task_..` functions taking a task as an argument require that it not be NULL. + * + * When a `tc_task_..` function that returns a TCResult returns TC_RESULT_ERROR, then + * `tc_task_error` will return the error message. + * + * # Safety + * + * A task is an owned object, and must be freed with tc_task_free (or, if part of a list, + * with tc_task_list_free). + * + * Any function taking a `*TCTask` requires: + * - the pointer must not be NUL; + * - the pointer must be one previously returned from a tc_… function; + * - the memory referenced by the pointer must never be modified by C code; and + * - except for `tc_{task,task_list}_free`, ownership of a `*TCTask` remains with the caller. + * + * Once passed to tc_task_free, a `*TCTask` becomes invalid and must not be used again. + * + * TCTasks are not threadsafe. + */ +typedef struct TCTask TCTask; + +/** + * A TCWorkingSet represents a snapshot of the working set for a replica. It is not automatically + * updated based on changes in the replica. Its lifetime is independent of the replica and it can + * be freed at any time. + * + * To iterate over a working set, search indexes 1 through largest_index. + * + * # Safety + * + * The `*TCWorkingSet` returned from `tc_replica_working_set` is owned by the caller and + * must later be freed to avoid a memory leak. Its lifetime is independent of the replica + * from which it was generated. + * + * Any function taking a `*TCWorkingSet` requires: + * - the pointer must not be NUL; + * - the pointer must be one previously returned from `tc_replica_working_set` + * - the memory referenced by the pointer must never be accessed by C code; and + * - except for `tc_replica_free`, ownership of a `*TCWorkingSet` remains with the caller. + * + * Once passed to `tc_replica_free`, a `*TCWorkingSet` becomes invalid and must not be used again. + * + * TCWorkingSet is not threadsafe. + */ +typedef struct TCWorkingSet TCWorkingSet; + +/** + * TCString supports passing strings into and out of the TaskChampion API. + * + * # Rust Strings and C Strings + * + * A Rust string can contain embedded NUL characters, while C considers such a character to mark + * the end of a string. Strings containing embedded NULs cannot be represented as a "C string" + * and must be accessed using `tc_string_content_and_len` and `tc_string_clone_with_len`. In + * general, these two functions should be used for handling arbitrary data, while more convenient + * forms may be used where embedded NUL characters are impossible, such as in static strings. + * + * # UTF-8 + * + * TaskChampion expects all strings to be valid UTF-8. `tc_string_…` functions will fail if given + * a `*TCString` containing invalid UTF-8. + * + * # Safety + * + * The `ptr` field may be checked for NULL, where documentation indicates this is possible. All + * other fields in a TCString are private and must not be used from C. They exist in the struct + * to ensure proper allocation and alignment. + * + * When a `TCString` appears as a return value or output argument, ownership is passed to the + * caller. The caller must pass that ownership back to another function or free the string. + * + * Any function taking a `TCString` requires: + * - the pointer must not be NUL; + * - the pointer must be one previously returned from a tc_… function; and + * - the memory referenced by the pointer must never be modified by C code. + * + * Unless specified otherwise, TaskChampion functions take ownership of a `TCString` when it is + * given as a function argument, and the caller must not use or free TCStrings after passing them + * to such API functions. + * + * A TCString with a NULL `ptr` field need not be freed, although tc_free_string will not fail + * for such a value. + * + * TCString is not threadsafe. + */ +typedef struct TCString { + void *ptr; + size_t _u1; + size_t _u2; + uint8_t _u3; +} TCString; + +/** + * TCAnnotation contains the details of an annotation. + * + * # Safety + * + * An annotation must be initialized from a tc_.. function, and later freed + * with `tc_annotation_free` or `tc_annotation_list_free`. + * + * Any function taking a `*TCAnnotation` requires: + * - the pointer must not be NUL; + * - the pointer must be one previously returned from a tc_… function; + * - the memory referenced by the pointer must never be modified by C code; and + * - ownership transfers to the called function, and the value must not be used + * after the call returns. In fact, the value will be zeroed out to ensure this. + * + * TCAnnotations are not threadsafe. + */ +typedef struct TCAnnotation { + /** + * Time the annotation was made. Must be nonzero. + */ + time_t entry; + /** + * Content of the annotation. Must not be NULL. + */ + struct TCString description; +} TCAnnotation; + +/** + * TCAnnotationList represents a list of annotations. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCAnnotationList { + /** + * number of annotations in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * array of annotations. these remain owned by the TCAnnotationList instance and will be freed by + * tc_annotation_list_free. This pointer is never NULL for a valid TCAnnotationList. + */ + const struct TCAnnotation *items; +} TCAnnotationList; + +/** + * TCKV contains a key/value pair that is part of a task. + * + * Neither key nor value are ever NULL. They remain owned by the TCKV and + * will be freed when it is freed with tc_kv_list_free. + */ +typedef struct TCKV { + struct TCString key; + struct TCString value; +} TCKV; + +/** + * TCKVList represents a list of key/value pairs. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCKVList { + /** + * number of key/value pairs in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * array of TCKV's. these remain owned by the TCKVList instance and will be freed by + * tc_kv_list_free. This pointer is never NULL for a valid TCKVList. + */ + const struct TCKV *items; +} TCKVList; + +/** + * TCTaskList represents a list of tasks. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCTaskList { + /** + * number of tasks in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * array of pointers representing each task. these remain owned by the TCTaskList instance and + * will be freed by tc_task_list_free. This pointer is never NULL for a valid TCTaskList, + * and the *TCTaskList at indexes 0..len-1 are not NULL. + */ + struct TCTask *const *items; +} TCTaskList; + +/** + * TCUuid is used as a task identifier. Uuids do not contain any pointers and need not be freed. + * Uuids are typically treated as opaque, but the bytes are available in big-endian format. + * + */ +typedef struct TCUuid { + uint8_t bytes[16]; +} TCUuid; + +/** + * TCUuidList represents a list of uuids. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCUuidList { + /** + * number of uuids in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * array of uuids. these remain owned by the TCUuidList instance and will be freed by + * tc_uuid_list_free. This pointer is never NULL for a valid TCUuidList. + */ + const struct TCUuid *items; +} TCUuidList; + +/** + * TCStringList represents a list of strings. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCStringList { + /** + * number of strings in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * TCStringList representing each string. these remain owned by the TCStringList instance and will + * be freed by tc_string_list_free. This pointer is never NULL for a valid TCStringList, and the + * *TCStringList at indexes 0..len-1 are not NULL. + */ + const struct TCString *items; +} TCStringList; + +/** + * TCUda contains the details of a UDA. + */ +typedef struct TCUda { + /** + * Namespace of the UDA. For legacy UDAs, this may have a NULL ptr field. + */ + struct TCString ns; + /** + * UDA key. Must not be NULL. + */ + struct TCString key; + /** + * Content of the UDA. Must not be NULL. + */ + struct TCString value; +} TCUda; + +/** + * TCUdaList represents a list of UDAs. + * + * The content of this struct must be treated as read-only. + */ +typedef struct TCUdaList { + /** + * number of UDAs in items + */ + size_t len; + /** + * total size of items (internal use only) + */ + size_t _capacity; + /** + * array of UDAs. These remain owned by the TCUdaList instance and will be freed by + * tc_uda_list_free. This pointer is never NULL for a valid TCUdaList. + */ + const struct TCUda *items; +} TCUdaList; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Free a TCAnnotation instance. The instance, and the TCString it contains, must not be used + * after this call. + */ +void tc_annotation_free(struct TCAnnotation *tcann); + +/** + * Free a TCAnnotationList instance. The instance, and all TCAnnotations it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCAnnotationList. + */ +void tc_annotation_list_free(struct TCAnnotationList *tcanns); + +/** + * Free a TCKVList instance. The instance, and all TCKVs it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCKVList. + */ +void tc_kv_list_free(struct TCKVList *tckvs); + +/** + * Create a new TCReplica with an in-memory database. The contents of the database will be + * lost when it is freed with tc_replica_free. + */ +struct TCReplica *tc_replica_new_in_memory(void); + +/** + * Create a new TCReplica with an on-disk database having the given filename. On error, a string + * is written to the error_out parameter (if it is not NULL) and NULL is returned. The caller + * must free this string. + */ +struct TCReplica *tc_replica_new_on_disk(struct TCString path, struct TCString *error_out); + +/** + * Get a list of all tasks in the replica. + * + * Returns a TCTaskList with a NULL items field on error. + */ +struct TCTaskList tc_replica_all_tasks(struct TCReplica *rep); + +/** + * Get a list of all uuids for tasks in the replica. + * + * Returns a TCUuidList with a NULL items field on error. + * + * The caller must free the UUID list with `tc_uuid_list_free`. + */ +struct TCUuidList tc_replica_all_task_uuids(struct TCReplica *rep); + +/** + * Get the current working set for this replica. The resulting value must be freed + * with tc_working_set_free. + * + * Returns NULL on error. + */ +struct TCWorkingSet *tc_replica_working_set(struct TCReplica *rep); + +/** + * Get an existing task by its UUID. + * + * Returns NULL when the task does not exist, and on error. Consult tc_replica_error + * to distinguish the two conditions. + */ +struct TCTask *tc_replica_get_task(struct TCReplica *rep, struct TCUuid tcuuid); + +/** + * Create a new task. The task must not already exist. + * + * Returns the task, or NULL on error. + */ +struct TCTask *tc_replica_new_task(struct TCReplica *rep, + enum TCStatus status, + struct TCString description); + +/** + * Create a new task. The task must not already exist. + * + * Returns the task, or NULL on error. + */ +struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid); + +/** + * Synchronize this replica with a server. + * + * The `server` argument remains owned by the caller, and must be freed explicitly. + */ +TCResult tc_replica_sync(struct TCReplica *rep, struct TCServer *server, bool avoid_snapshots); + +/** + * Undo local operations until the most recent UndoPoint. + * + * If undone_out is not NULL, then on success it is set to 1 if operations were undone, or 0 if + * there are no operations that can be done. + */ +TCResult tc_replica_undo(struct TCReplica *rep, int32_t *undone_out); + +/** + * Add an UndoPoint, if one has not already been added by this Replica. This occurs automatically + * when a change is made. The `force` flag allows forcing a new UndoPoint even if one has already + * been created by this Replica, and may be useful when a Replica instance is held for a long time + * and used to apply more than one user-visible change. + */ +TCResult tc_replica_add_undo_point(struct TCReplica *rep, bool force); + +/** + * Rebuild this replica's working set, based on whether tasks are pending or not. If `renumber` + * is true, then existing tasks may be moved to new working-set indices; in any case, on + * completion all pending tasks are in the working set and all non- pending tasks are not. + */ +TCResult tc_replica_rebuild_working_set(struct TCReplica *rep, bool renumber); + +/** + * Get the latest error for a replica, or a string with NULL ptr if no error exists. Subsequent + * calls to this function will return NULL. The rep pointer must not be NULL. The caller must + * free the returned string. + */ +struct TCString tc_replica_error(struct TCReplica *rep); + +/** + * Free a replica. The replica may not be used after this function returns and must not be freed + * more than once. + */ +void tc_replica_free(struct TCReplica *rep); + +/** + * Create a new TCServer that operates locally (on-disk). See the TaskChampion docs for the + * description of the arguments. + * + * On error, a string is written to the error_out parameter (if it is not NULL) and NULL is + * returned. The caller must free this string. + * + * The server must be freed after it is used - tc_replica_sync does not automatically free it. + */ +struct TCServer *tc_server_new_local(struct TCString server_dir, struct TCString *error_out); + +/** + * Create a new TCServer that connects to a remote server. See the TaskChampion docs for the + * description of the arguments. + * + * On error, a string is written to the error_out parameter (if it is not NULL) and NULL is + * returned. The caller must free this string. + * + * The server must be freed after it is used - tc_replica_sync does not automatically free it. + */ +struct TCServer *tc_server_new_remote(struct TCString origin, + struct TCUuid client_key, + struct TCString encryption_secret, + struct TCString *error_out); + +/** + * Free a server. The server may not be used after this function returns and must not be freed + * more than once. + */ +void tc_server_free(struct TCServer *server); + +/** + * Create a new TCString referencing the given C string. The C string must remain valid and + * unchanged until after the TCString is freed. It's typically easiest to ensure this by using a + * static string. + * + * NOTE: this function does _not_ take responsibility for freeing the given C string. The + * given string can be freed once the TCString referencing it has been freed. + * + * For example: + * + * ``` + * char *url = get_item_url(..); // dynamically allocate C string + * tc_task_annotate(task, tc_string_borrow(url)); // TCString created, passed, and freed + * free(url); // string is no longer referenced and can be freed + * ``` + */ +struct TCString tc_string_borrow(const char *cstr); + +/** + * Create a new TCString by cloning the content of the given C string. The resulting TCString + * is independent of the given string, which can be freed or overwritten immediately. + */ +struct TCString tc_string_clone(const char *cstr); + +/** + * Create a new TCString containing the given string with the given length. This allows creation + * of strings containing embedded NUL characters. As with `tc_string_clone`, the resulting + * TCString is independent of the passed buffer, which may be reused or freed immediately. + * + * The length should _not_ include any trailing NUL. + * + * The given length must be less than half the maximum value of usize. + */ +struct TCString tc_string_clone_with_len(const char *buf, size_t len); + +/** + * Get the content of the string as a regular C string. The given string must be valid. The + * returned value is NULL if the string contains NUL bytes or (in some cases) invalid UTF-8. The + * returned C string is valid until the TCString is freed or passed to another TC API function. + * + * In general, prefer [`tc_string_content_with_len`] except when it's certain that the string is + * valid and NUL-free. + * + * This function takes the TCString by pointer because it may be modified in-place to add a NUL + * terminator. The pointer must not be NULL. + * + * This function does _not_ take ownership of the TCString. + */ +const char *tc_string_content(const struct TCString *tcstring); + +/** + * Get the content of the string as a pointer and length. The given string must not be NULL. + * This function can return any string, even one including NUL bytes or invalid UTF-8. The + * returned buffer is valid until the TCString is freed or passed to another TaskChampio + * function. + * + * This function takes the TCString by pointer because it may be modified in-place to add a NUL + * terminator. The pointer must not be NULL. + * + * This function does _not_ take ownership of the TCString. + */ +const char *tc_string_content_with_len(const struct TCString *tcstring, size_t *len_out); + +/** + * Free a TCString. The given string must not be NULL. The string must not be used + * after this function returns, and must not be freed more than once. + */ +void tc_string_free(struct TCString *tcstring); + +/** + * Free a TCStringList instance. The instance, and all TCStringList it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCStringList. + */ +void tc_string_list_free(struct TCStringList *tcstrings); + +/** + * Convert an immutable task into a mutable task. + * + * The task must not be NULL. It is modified in-place, and becomes mutable. + * + * The replica must not be NULL. After this function returns, the replica _cannot be used at all_ + * until this task is made immutable again. This implies that it is not allowed for more than one + * task associated with a replica to be mutable at any time. + * + * Typical mutation of tasks is bracketed with `tc_task_to_mut` and `tc_task_to_immut`: + * + * ```c + * tc_task_to_mut(task, rep); + * success = tc_task_done(task); + * tc_task_to_immut(task, rep); + * if (!success) { ... } + * ``` + */ +void tc_task_to_mut(struct TCTask *task, struct TCReplica *tcreplica); + +/** + * Convert a mutable task into an immutable task. + * + * The task must not be NULL. It is modified in-place, and becomes immutable. + * + * The replica passed to `tc_task_to_mut` may be used freely after this call. + */ +void tc_task_to_immut(struct TCTask *task); + +/** + * Get a task's UUID. + */ +struct TCUuid tc_task_get_uuid(struct TCTask *task); + +/** + * Get a task's status. + */ +enum TCStatus tc_task_get_status(struct TCTask *task); + +/** + * Get the underlying key/value pairs for this task. The returned TCKVList is + * a "snapshot" of the task and will not be updated if the task is subsequently + * modified. It is the caller's responsibility to free the TCKVList. + */ +struct TCKVList tc_task_get_taskmap(struct TCTask *task); + +/** + * Get a task's description, or NULL if the task cannot be represented as a C string (e.g., if it + * contains embedded NUL characters). + */ +struct TCString tc_task_get_description(struct TCTask *task); + +/** + * Get the entry timestamp for a task (when it was created), or 0 if not set. + */ +time_t tc_task_get_entry(struct TCTask *task); + +/** + * Get the wait timestamp for a task, or 0 if not set. + */ +time_t tc_task_get_wait(struct TCTask *task); + +/** + * Get the modified timestamp for a task, or 0 if not set. + */ +time_t tc_task_get_modified(struct TCTask *task); + +/** + * Check if a task is waiting. + */ +bool tc_task_is_waiting(struct TCTask *task); + +/** + * Check if a task is active (started and not stopped). + */ +bool tc_task_is_active(struct TCTask *task); + +/** + * Check if a task has the given tag. If the tag is invalid, this function will return false, as + * that (invalid) tag is not present. No error will be reported via `tc_task_error`. + */ +bool tc_task_has_tag(struct TCTask *task, struct TCString tag); + +/** + * Get the tags for the task. + * + * The caller must free the returned TCStringList instance. The TCStringList instance does not + * reference the task and the two may be freed in any order. + */ +struct TCStringList tc_task_get_tags(struct TCTask *task); + +/** + * Get the annotations for the task. + * + * The caller must free the returned TCAnnotationList instance. The TCStringList instance does not + * reference the task and the two may be freed in any order. + */ +struct TCAnnotationList tc_task_get_annotations(struct TCTask *task); + +/** + * Get the named UDA from the task. + * + * Returns a TCString with NULL ptr field if the UDA does not exist. + */ +struct TCString tc_task_get_uda(struct TCTask *task, struct TCString ns, struct TCString key); + +/** + * Get the named legacy UDA from the task. + * + * Returns NULL if the UDA does not exist. + */ +struct TCString tc_task_get_legacy_uda(struct TCTask *task, struct TCString key); + +/** + * Get all UDAs for this task. + * + * Legacy UDAs are represented with an empty string in the ns field. + */ +struct TCUdaList tc_task_get_udas(struct TCTask *task); + +/** + * Get all UDAs for this task. + * + * All TCUdas in this list have a NULL ns field. The entire UDA key is + * included in the key field. The caller must free the returned list. + */ +struct TCUdaList tc_task_get_legacy_udas(struct TCTask *task); + +/** + * Set a mutable task's status. + */ +TCResult tc_task_set_status(struct TCTask *task, enum TCStatus status); + +/** + * Set a mutable task's description. + */ +TCResult tc_task_set_description(struct TCTask *task, struct TCString description); + +/** + * Set a mutable task's entry (creation time). Pass entry=0 to unset + * the entry field. + */ +TCResult tc_task_set_entry(struct TCTask *task, time_t entry); + +/** + * Set a mutable task's wait timestamp. Pass wait=0 to unset the wait field. + */ +TCResult tc_task_set_wait(struct TCTask *task, time_t wait); + +/** + * Set a mutable task's modified timestamp. The value cannot be zero. + */ +TCResult tc_task_set_modified(struct TCTask *task, time_t modified); + +/** + * Start a task. + */ +TCResult tc_task_start(struct TCTask *task); + +/** + * Stop a task. + */ +TCResult tc_task_stop(struct TCTask *task); + +/** + * Mark a task as done. + */ +TCResult tc_task_done(struct TCTask *task); + +/** + * Mark a task as deleted. + */ +TCResult tc_task_delete(struct TCTask *task); + +/** + * Add a tag to a mutable task. + */ +TCResult tc_task_add_tag(struct TCTask *task, struct TCString tag); + +/** + * Remove a tag from a mutable task. + */ +TCResult tc_task_remove_tag(struct TCTask *task, struct TCString tag); + +/** + * Add an annotation to a mutable task. This call takes ownership of the + * passed annotation, which must not be used after the call returns. + */ +TCResult tc_task_add_annotation(struct TCTask *task, struct TCAnnotation *annotation); + +/** + * Remove an annotation from a mutable task. + */ +TCResult tc_task_remove_annotation(struct TCTask *task, int64_t entry); + +/** + * Set a UDA on a mutable task. + */ +TCResult tc_task_set_uda(struct TCTask *task, + struct TCString ns, + struct TCString key, + struct TCString value); + +/** + * Remove a UDA fraom a mutable task. + */ +TCResult tc_task_remove_uda(struct TCTask *task, struct TCString ns, struct TCString key); + +/** + * Set a legacy UDA on a mutable task. + */ +TCResult tc_task_set_legacy_uda(struct TCTask *task, struct TCString key, struct TCString value); + +/** + * Remove a UDA fraom a mutable task. + */ +TCResult tc_task_remove_legacy_uda(struct TCTask *task, struct TCString key); + +/** + * Get the latest error for a task, or a string NULL ptr field if the last operation succeeded. + * Subsequent calls to this function will return NULL. The task pointer must not be NULL. The + * caller must free the returned string. + */ +struct TCString tc_task_error(struct TCTask *task); + +/** + * Free a task. The given task must not be NULL. The task must not be used after this function + * returns, and must not be freed more than once. + * + * If the task is currently mutable, it will first be made immutable. + */ +void tc_task_free(struct TCTask *task); + +/** + * Free a TCTaskList instance. The instance, and all TCTaskList it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCTaskList. + */ +void tc_task_list_free(struct TCTaskList *tctasks); + +/** + * Free a TCUda instance. The instance, and the TCStrings it contains, must not be used + * after this call. + */ +void tc_uda_free(struct TCUda *tcuda); + +/** + * Free a TCUdaList instance. The instance, and all TCUdas it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCUdaList. + */ +void tc_uda_list_free(struct TCUdaList *tcudas); + +/** + * Create a new, randomly-generated UUID. + */ +struct TCUuid tc_uuid_new_v4(void); + +/** + * Create a new UUID with the nil value. + */ +struct TCUuid tc_uuid_nil(void); + +/** + * Write the string representation of a TCUuid into the given buffer, which must be + * at least TC_UUID_STRING_BYTES long. No NUL terminator is added. + */ +void tc_uuid_to_buf(struct TCUuid tcuuid, char *buf); + +/** + * Return the hyphenated string representation of a TCUuid. The returned string + * must be freed with tc_string_free. + */ +struct TCString tc_uuid_to_str(struct TCUuid tcuuid); + +/** + * Parse the given string as a UUID. Returns TC_RESULT_ERROR on parse failure or if the given + * string is not valid. + */ +TCResult tc_uuid_from_str(struct TCString s, struct TCUuid *uuid_out); + +/** + * Free a TCUuidList instance. The instance, and all TCUuids it contains, must not be used after + * this call. + * + * When this call returns, the `items` pointer will be NULL, signalling an invalid TCUuidList. + */ +void tc_uuid_list_free(struct TCUuidList *tcuuids); + +/** + * Get the working set's length, or the number of UUIDs it contains. + */ +size_t tc_working_set_len(struct TCWorkingSet *ws); + +/** + * Get the working set's largest index. + */ +size_t tc_working_set_largest_index(struct TCWorkingSet *ws); + +/** + * Get the UUID for the task at the given index. Returns true if the UUID exists in the working + * set. If not, returns false and does not change uuid_out. + */ +bool tc_working_set_by_index(struct TCWorkingSet *ws, size_t index, struct TCUuid *uuid_out); + +/** + * Get the working set index for the task with the given UUID. Returns 0 if the task is not in + * the working set. + */ +size_t tc_working_set_by_uuid(struct TCWorkingSet *ws, struct TCUuid uuid); + +/** + * Free a TCWorkingSet. The given value must not be NULL. The value must not be used after this + * function returns, and must not be freed more than once. + */ +void tc_working_set_free(struct TCWorkingSet *ws); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/replica-server-tests/src/lib.rs b/replica-server-tests/src/lib.rs deleted file mode 100644 index e783558fc..000000000 --- a/replica-server-tests/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -// test-only crate diff --git a/taskchampion/src/replica.rs b/taskchampion/src/replica.rs index b85cb84c7..79938423f 100644 --- a/taskchampion/src/replica.rs +++ b/taskchampion/src/replica.rs @@ -110,7 +110,7 @@ impl Replica { let mut task = Task::new(uuid, taskmap).into_mut(self); task.set_description(description)?; task.set_status(status)?; - task.set_entry(Utc::now())?; + task.set_entry(Some(Utc::now()))?; trace!("task {} created", uuid); Ok(task.into_immut()) } @@ -177,7 +177,7 @@ impl Replica { /// Add an UndoPoint, if one has not already been added by this Replica. This occurs /// automatically when a change is made. The `force` flag allows forcing a new UndoPoint - /// even if one has laready been created by this Replica, and may be useful when a Replica + /// even if one has already been created by this Replica, and may be useful when a Replica /// instance is held for a long time and used to apply more than one user-visible change. pub fn add_undo_point(&mut self, force: bool) -> anyhow::Result<()> { if force || !self.added_undo_point { diff --git a/taskchampion/src/storage/mod.rs b/taskchampion/src/storage/mod.rs index d3d494a1b..dd3c9786a 100644 --- a/taskchampion/src/storage/mod.rs +++ b/taskchampion/src/storage/mod.rs @@ -106,6 +106,7 @@ pub trait StorageTxn { fn clear_working_set(&mut self) -> Result<()>; /// Check whether this storage is entirely empty + #[allow(clippy::wrong_self_convention)] // mut is required here for storage access fn is_empty(&mut self) -> Result { let mut empty = true; empty = empty && self.all_tasks()?.is_empty(); diff --git a/taskchampion/src/task/status.rs b/taskchampion/src/task/status.rs index 2b2afb6ba..31fee9cf7 100644 --- a/taskchampion/src/task/status.rs +++ b/taskchampion/src/task/status.rs @@ -1,5 +1,6 @@ /// The status of a task, as defined by the task data model. #[derive(Debug, PartialEq, Clone, strum_macros::Display)] +#[repr(C)] pub enum Status { Pending, Completed, diff --git a/taskchampion/src/task/task.rs b/taskchampion/src/task/task.rs index 82bafb7d7..d5517f5b4 100644 --- a/taskchampion/src/task/task.rs +++ b/taskchampion/src/task/task.rs @@ -120,6 +120,10 @@ impl Task { .unwrap_or("") } + pub fn get_entry(&self) -> Option> { + self.get_timestamp(Prop::Entry.as_ref()) + } + pub fn get_priority(&self) -> Priority { self.taskmap .get(Prop::Status.as_ref()) @@ -299,8 +303,8 @@ impl<'r> TaskMut<'r> { self.set_string(Prop::Description.as_ref(), Some(description)) } - pub(crate) fn set_entry(&mut self, entry: DateTime) -> anyhow::Result<()> { - self.set_timestamp(Prop::Entry.as_ref(), Some(entry)) + pub fn set_entry(&mut self, entry: Option>) -> anyhow::Result<()> { + self.set_timestamp(Prop::Entry.as_ref(), entry) } pub fn set_wait(&mut self, wait: Option>) -> anyhow::Result<()> { @@ -526,6 +530,24 @@ mod test { assert!(!task.is_active()); } + #[test] + fn test_entry_not_set() { + let task = Task::new(Uuid::new_v4(), TaskMap::new()); + assert_eq!(task.get_entry(), None); + } + + #[test] + fn test_entry_set() { + let ts = Utc.ymd(1980, 1, 1).and_hms(0, 0, 0); + let task = Task::new( + Uuid::new_v4(), + vec![(String::from("entry"), format!("{}", ts.timestamp()))] + .drain(..) + .collect(), + ); + assert_eq!(task.get_entry(), Some(ts)); + } + #[test] fn test_wait_not_set() { let task = Task::new(Uuid::new_v4(), TaskMap::new()); diff --git a/taskchampion/src/workingset.rs b/taskchampion/src/workingset.rs index ea746a72b..15a509753 100644 --- a/taskchampion/src/workingset.rs +++ b/taskchampion/src/workingset.rs @@ -38,6 +38,11 @@ impl WorkingSet { self.by_index.iter().filter(|e| e.is_some()).count() } + /// Get the largest index in the working set, or zero if the set is empty. + pub fn largest_index(&self) -> usize { + self.by_index.len().saturating_sub(1) + } + /// True if the length is zero pub fn is_empty(&self) -> bool { self.by_index.iter().all(|e| e.is_none()) @@ -103,6 +108,24 @@ mod test { assert_eq!(ws.is_empty(), true); } + #[test] + fn test_largest_index() { + let uuid1 = Uuid::new_v4(); + let uuid2 = Uuid::new_v4(); + + let ws = WorkingSet::new(vec![]); + assert_eq!(ws.largest_index(), 0); + + let ws = WorkingSet::new(vec![None, Some(uuid1)]); + assert_eq!(ws.largest_index(), 1); + + let ws = WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2)]); + assert_eq!(ws.largest_index(), 3); + + let ws = WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2), None]); + assert_eq!(ws.largest_index(), 4); + } + #[test] fn test_by_index() { let (uuid1, uuid2, ws) = make(); diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 000000000..6deb10d58 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask" +version = "0.4.1" +edition = "2018" + +[dependencies] +anyhow = "1.0" +cbindgen = "0.20.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 000000000..3990a4f39 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,48 @@ +//! This executable defines the `cargo xtask` subcommands. +//! +//! At the moment it is very simple, but if this grows more subcommands then +//! it will be sensible to use `clap` or another similar library. + +use cbindgen::*; +use std::env; +use std::path::PathBuf; + +pub fn main() -> anyhow::Result<()> { + let arg = env::args().nth(1); + match arg.as_ref().map(|arg| arg.as_str()) { + Some("codegen") => codegen(), + Some(arg) => anyhow::bail!("unknown xtask {}", arg), + _ => anyhow::bail!("unknown xtask"), + } +} + +/// `cargo xtask codegen` +/// +/// This uses cbindgen to generate `lib/taskchampion.h`. +fn codegen() -> anyhow::Result<()> { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let workspace_dir = manifest_dir.parent().unwrap(); + let lib_crate_dir = workspace_dir.join("lib"); + + Builder::new() + .with_crate(&lib_crate_dir) + .with_config(Config { + header: Some(include_str!("../../lib/header-intro.h").into()), + language: Language::C, + cpp_compat: true, + sys_includes: vec!["stdbool.h".into(), "stdint.h".into(), "time.h".into()], + usize_is_size_t: true, + no_includes: true, + enumeration: EnumConfig { + // this appears to still default to true for C + enum_class: false, + ..Default::default() + }, + ..Default::default() + }) + .generate() + .expect("Unable to generate bindings") + .write_to_file(lib_crate_dir.join("taskchampion.h")); + + Ok(()) +}