diff options
author | Vicent Martà <tanoku@gmail.com> | 2012-01-24 20:35:15 -0800 |
---|---|---|
committer | Vicent Martà <tanoku@gmail.com> | 2012-01-24 20:35:15 -0800 |
commit | 3fd1520cd4d8b4d6b6493a7d3dc393ffd9abf1db (patch) | |
tree | 51b29f5d8ffeb31ba751ab2a099e4f2a32d4be07 /tests-clar/clar_main.c | |
parent | a9fe8ae0ee1ddcc289fad53f1a671f02a3e9a88f (diff) | |
download | libgit2-3fd1520cd4d8b4d6b6493a7d3dc393ffd9abf1db.tar.gz |
Rename the Clay test suite to Clar
Clay is the name of a programming language on the makings, and we want
to avoid confusions. Sorry for the huge diff!
Diffstat (limited to 'tests-clar/clar_main.c')
-rw-r--r-- | tests-clar/clar_main.c | 1230 |
1 files changed, 1230 insertions, 0 deletions
diff --git a/tests-clar/clar_main.c b/tests-clar/clar_main.c new file mode 100644 index 000000000..16e627041 --- /dev/null +++ b/tests-clar/clar_main.c @@ -0,0 +1,1230 @@ +#include <assert.h> +#include <setjmp.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <stdarg.h> + +/* required for sandboxing */ +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef _WIN32 +# include <windows.h> +# include <io.h> +# include <shellapi.h> +# include <direct.h> + +# define _MAIN_CC __cdecl + +# define stat(path, st) _stat(path, st) +# define mkdir(path, mode) _mkdir(path) +# define chdir(path) _chdir(path) +# define access(path, mode) _access(path, mode) +# define strdup(str) _strdup(str) + +# ifndef __MINGW32__ +# pragma comment(lib, "shell32") +# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE) +# define W_OK 02 +# define S_ISDIR(x) ((x & _S_IFDIR) != 0) +# define mktemp_s(path, len) _mktemp_s(path, len) +# endif + typedef struct _stat STAT_T; +#else +# include <sys/wait.h> /* waitpid(2) */ +# include <unistd.h> +# define _MAIN_CC + typedef struct stat STAT_T; +#endif + +#include "clar.h" + +static void fs_rm(const char *_source); +static void fs_copy(const char *_source, const char *dest); + +static const char * +fixture_path(const char *base, const char *fixture_name); + +struct clar_error { + const char *test; + int test_number; + const char *suite; + const char *file; + int line_number; + const char *error_msg; + char *description; + + struct clar_error *next; +}; + +static struct { + const char *active_test; + const char *active_suite; + + int suite_errors; + int total_errors; + + int test_count; + + struct clar_error *errors; + struct clar_error *last_error; + + void (*local_cleanup)(void *); + void *local_cleanup_payload; + + jmp_buf trampoline; + int trampoline_enabled; +} _clar; + +struct clar_func { + const char *name; + void (*ptr)(void); +}; + +struct clar_suite { + const char *name; + struct clar_func initialize; + struct clar_func cleanup; + const struct clar_func *tests; + size_t test_count; +}; + +/* From clar_print_*.c */ +static void clar_print_init(int test_count, int suite_count, const char *suite_names); +static void clar_print_shutdown(int test_count, int suite_count, int error_count); +static void clar_print_error(int num, const struct clar_error *error); +static void clar_print_ontest(const char *test_name, int test_number, int failed); +static void clar_print_onsuite(const char *suite_name); +static void clar_print_onabort(const char *msg, ...); + +/* From clar_sandbox.c */ +static void clar_unsandbox(void); +static int clar_sandbox(void); + +/* Event callback overrides */ +#define clar_on_test() /* nop */ +#define clar_on_suite() /* nop */ + +/* Autogenerated test data by clar */ +static const struct clar_func _clar_cb_attr_file[] = { + {"assign_variants", &test_attr_file__assign_variants}, + {"check_attr_examples", &test_attr_file__check_attr_examples}, + {"match_variants", &test_attr_file__match_variants}, + {"simple_read", &test_attr_file__simple_read} +}; +static const struct clar_func _clar_cb_attr_lookup[] = { + {"assign_variants", &test_attr_lookup__assign_variants}, + {"check_attr_examples", &test_attr_lookup__check_attr_examples}, + {"from_buffer", &test_attr_lookup__from_buffer}, + {"match_variants", &test_attr_lookup__match_variants}, + {"simple", &test_attr_lookup__simple} +}; +static const struct clar_func _clar_cb_attr_repo[] = { + {"bad_macros", &test_attr_repo__bad_macros}, + {"foreach", &test_attr_repo__foreach}, + {"get_many", &test_attr_repo__get_many}, + {"get_one", &test_attr_repo__get_one}, + {"macros", &test_attr_repo__macros}, + {"manpage_example", &test_attr_repo__manpage_example} +}; +static const struct clar_func _clar_cb_buf_basic[] = { + {"printf", &test_buf_basic__printf}, + {"resize", &test_buf_basic__resize} +}; +static const struct clar_func _clar_cb_config_add[] = { + {"to_existing_section", &test_config_add__to_existing_section}, + {"to_new_section", &test_config_add__to_new_section} +}; +static const struct clar_func _clar_cb_config_new[] = { + {"write_new_config", &test_config_new__write_new_config} +}; +static const struct clar_func _clar_cb_config_read[] = { + {"blank_lines", &test_config_read__blank_lines}, + {"case_sensitive", &test_config_read__case_sensitive}, + {"empty_files", &test_config_read__empty_files}, + {"header_in_last_line", &test_config_read__header_in_last_line}, + {"invalid_ext_headers", &test_config_read__invalid_ext_headers}, + {"lone_variable", &test_config_read__lone_variable}, + {"multiline_value", &test_config_read__multiline_value}, + {"number_suffixes", &test_config_read__number_suffixes}, + {"prefixes", &test_config_read__prefixes}, + {"simple_read", &test_config_read__simple_read}, + {"subsection_header", &test_config_read__subsection_header} +}; +static const struct clar_func _clar_cb_config_stress[] = { + {"dont_break_on_invalid_input", &test_config_stress__dont_break_on_invalid_input} +}; +static const struct clar_func _clar_cb_config_write[] = { + {"delete_inexistent", &test_config_write__delete_inexistent}, + {"delete_value", &test_config_write__delete_value}, + {"replace_value", &test_config_write__replace_value} +}; +static const struct clar_func _clar_cb_core_buffer[] = { + {"0", &test_core_buffer__0}, + {"1", &test_core_buffer__1}, + {"2", &test_core_buffer__2}, + {"3", &test_core_buffer__3}, + {"4", &test_core_buffer__4}, + {"5", &test_core_buffer__5}, + {"6", &test_core_buffer__6}, + {"7", &test_core_buffer__7}, + {"8", &test_core_buffer__8}, + {"9", &test_core_buffer__9} +}; +static const struct clar_func _clar_cb_core_dirent[] = { + {"dont_traverse_dot", &test_core_dirent__dont_traverse_dot}, + {"dont_traverse_empty_folders", &test_core_dirent__dont_traverse_empty_folders}, + {"traverse_slash_terminated_folder", &test_core_dirent__traverse_slash_terminated_folder}, + {"traverse_subfolder", &test_core_dirent__traverse_subfolder}, + {"traverse_weird_filenames", &test_core_dirent__traverse_weird_filenames} +}; +static const struct clar_func _clar_cb_core_filebuf[] = { + {"0", &test_core_filebuf__0}, + {"1", &test_core_filebuf__1}, + {"2", &test_core_filebuf__2}, + {"3", &test_core_filebuf__3}, + {"4", &test_core_filebuf__4}, + {"5", &test_core_filebuf__5} +}; +static const struct clar_func _clar_cb_core_hex[] = { + {"fromhex", &test_core_hex__fromhex} +}; +static const struct clar_func _clar_cb_core_oid[] = { + {"streq", &test_core_oid__streq} +}; +static const struct clar_func _clar_cb_core_path[] = { + {"00_dirname", &test_core_path__00_dirname}, + {"01_basename", &test_core_path__01_basename}, + {"02_topdir", &test_core_path__02_topdir}, + {"05_joins", &test_core_path__05_joins}, + {"06_long_joins", &test_core_path__06_long_joins}, + {"07_path_to_dir", &test_core_path__07_path_to_dir}, + {"08_self_join", &test_core_path__08_self_join}, + {"09_percent_decode", &test_core_path__09_percent_decode}, + {"10_fromurl", &test_core_path__10_fromurl}, + {"11_walkup", &test_core_path__11_walkup} +}; +static const struct clar_func _clar_cb_core_rmdir[] = { + {"delete_recursive", &test_core_rmdir__delete_recursive}, + {"fail_to_delete_non_empty_dir", &test_core_rmdir__fail_to_delete_non_empty_dir} +}; +static const struct clar_func _clar_cb_core_string[] = { + {"0", &test_core_string__0}, + {"1", &test_core_string__1} +}; +static const struct clar_func _clar_cb_core_strtol[] = { + {"int32", &test_core_strtol__int32}, + {"int64", &test_core_strtol__int64} +}; +static const struct clar_func _clar_cb_core_vector[] = { + {"0", &test_core_vector__0}, + {"1", &test_core_vector__1}, + {"2", &test_core_vector__2}, + {"3", &test_core_vector__3}, + {"4", &test_core_vector__4}, + {"5", &test_core_vector__5} +}; +static const struct clar_func _clar_cb_index_read_tree[] = { + {"read_write_involution", &test_index_read_tree__read_write_involution} +}; +static const struct clar_func _clar_cb_index_rename[] = { + {"single_file", &test_index_rename__single_file} +}; +static const struct clar_func _clar_cb_network_createremotethenload[] = { + {"parsing", &test_network_createremotethenload__parsing} +}; +static const struct clar_func _clar_cb_network_remotelocal[] = { + {"retrieve_advertised_references", &test_network_remotelocal__retrieve_advertised_references}, + {"retrieve_advertised_references_from_spaced_repository", &test_network_remotelocal__retrieve_advertised_references_from_spaced_repository} +}; +static const struct clar_func _clar_cb_network_remotes[] = { + {"fnmatch", &test_network_remotes__fnmatch}, + {"parsing", &test_network_remotes__parsing}, + {"refspec_parsing", &test_network_remotes__refspec_parsing}, + {"transform", &test_network_remotes__transform} +}; +static const struct clar_func _clar_cb_object_commit_commitstagedfile[] = { + {"generate_predictable_object_ids", &test_object_commit_commitstagedfile__generate_predictable_object_ids} +}; +static const struct clar_func _clar_cb_object_raw_chars[] = { + {"build_valid_oid_from_raw_bytes", &test_object_raw_chars__build_valid_oid_from_raw_bytes}, + {"find_invalid_chars_in_oid", &test_object_raw_chars__find_invalid_chars_in_oid} +}; +static const struct clar_func _clar_cb_object_raw_compare[] = { + {"compare_allocfmt_oids", &test_object_raw_compare__compare_allocfmt_oids}, + {"compare_fmt_oids", &test_object_raw_compare__compare_fmt_oids}, + {"compare_pathfmt_oids", &test_object_raw_compare__compare_pathfmt_oids}, + {"succeed_on_copy_oid", &test_object_raw_compare__succeed_on_copy_oid}, + {"succeed_on_oid_comparison_equal", &test_object_raw_compare__succeed_on_oid_comparison_equal}, + {"succeed_on_oid_comparison_greater", &test_object_raw_compare__succeed_on_oid_comparison_greater}, + {"succeed_on_oid_comparison_lesser", &test_object_raw_compare__succeed_on_oid_comparison_lesser} +}; +static const struct clar_func _clar_cb_object_raw_convert[] = { + {"succeed_on_oid_to_string_conversion", &test_object_raw_convert__succeed_on_oid_to_string_conversion}, + {"succeed_on_oid_to_string_conversion_big", &test_object_raw_convert__succeed_on_oid_to_string_conversion_big} +}; +static const struct clar_func _clar_cb_object_raw_fromstr[] = { + {"fail_on_invalid_oid_string", &test_object_raw_fromstr__fail_on_invalid_oid_string}, + {"succeed_on_valid_oid_string", &test_object_raw_fromstr__succeed_on_valid_oid_string} +}; +static const struct clar_func _clar_cb_object_raw_hash[] = { + {"hash_buffer_in_single_call", &test_object_raw_hash__hash_buffer_in_single_call}, + {"hash_by_blocks", &test_object_raw_hash__hash_by_blocks}, + {"hash_commit_object", &test_object_raw_hash__hash_commit_object}, + {"hash_junk_data", &test_object_raw_hash__hash_junk_data}, + {"hash_multi_byte_object", &test_object_raw_hash__hash_multi_byte_object}, + {"hash_one_byte_object", &test_object_raw_hash__hash_one_byte_object}, + {"hash_tag_object", &test_object_raw_hash__hash_tag_object}, + {"hash_tree_object", &test_object_raw_hash__hash_tree_object}, + {"hash_two_byte_object", &test_object_raw_hash__hash_two_byte_object}, + {"hash_vector", &test_object_raw_hash__hash_vector}, + {"hash_zero_length_object", &test_object_raw_hash__hash_zero_length_object} +}; +static const struct clar_func _clar_cb_object_raw_short[] = { + {"oid_shortener_no_duplicates", &test_object_raw_short__oid_shortener_no_duplicates}, + {"oid_shortener_stresstest_git_oid_shorten", &test_object_raw_short__oid_shortener_stresstest_git_oid_shorten} +}; +static const struct clar_func _clar_cb_object_raw_size[] = { + {"validate_oid_size", &test_object_raw_size__validate_oid_size} +}; +static const struct clar_func _clar_cb_object_raw_type2string[] = { + {"check_type_is_loose", &test_object_raw_type2string__check_type_is_loose}, + {"convert_string_to_type", &test_object_raw_type2string__convert_string_to_type}, + {"convert_type_to_string", &test_object_raw_type2string__convert_type_to_string} +}; +static const struct clar_func _clar_cb_object_tree_diff[] = { + {"addition", &test_object_tree_diff__addition}, + {"deletion", &test_object_tree_diff__deletion}, + {"modification", &test_object_tree_diff__modification}, + {"more", &test_object_tree_diff__more} +}; +static const struct clar_func _clar_cb_object_tree_frompath[] = { + {"fail_when_processing_an_invalid_path", &test_object_tree_frompath__fail_when_processing_an_invalid_path}, + {"fail_when_processing_an_unknown_tree_segment", &test_object_tree_frompath__fail_when_processing_an_unknown_tree_segment}, + {"retrieve_tree_from_path_to_treeentry", &test_object_tree_frompath__retrieve_tree_from_path_to_treeentry} +}; +static const struct clar_func _clar_cb_odb_loose[] = { + {"exists", &test_odb_loose__exists}, + {"simple_reads", &test_odb_loose__simple_reads} +}; +static const struct clar_func _clar_cb_odb_packed[] = { + {"mass_read", &test_odb_packed__mass_read}, + {"read_header_0", &test_odb_packed__read_header_0}, + {"read_header_1", &test_odb_packed__read_header_1} +}; +static const struct clar_func _clar_cb_odb_sorting[] = { + {"alternate_backends_sorting", &test_odb_sorting__alternate_backends_sorting}, + {"basic_backends_sorting", &test_odb_sorting__basic_backends_sorting} +}; +static const struct clar_func _clar_cb_refs_crashes[] = { + {"double_free", &test_refs_crashes__double_free} +}; +static const struct clar_func _clar_cb_repo_getters[] = { + {"empty", &test_repo_getters__empty}, + {"head_detached", &test_repo_getters__head_detached}, + {"head_orphan", &test_repo_getters__head_orphan} +}; +static const struct clar_func _clar_cb_repo_init[] = { + {"bare_repo", &test_repo_init__bare_repo}, + {"bare_repo_noslash", &test_repo_init__bare_repo_noslash}, + {"standard_repo", &test_repo_init__standard_repo}, + {"standard_repo_noslash", &test_repo_init__standard_repo_noslash} +}; +static const struct clar_func _clar_cb_repo_open[] = { + {"bare_empty_repo", &test_repo_open__bare_empty_repo}, + {"standard_empty_repo", &test_repo_open__standard_empty_repo} +}; +static const struct clar_func _clar_cb_status_ignore[] = { + {"0", &test_status_ignore__0} +}; +static const struct clar_func _clar_cb_status_single[] = { + {"hash_single_file", &test_status_single__hash_single_file} +}; +static const struct clar_func _clar_cb_status_worktree[] = { + {"empty_repository", &test_status_worktree__empty_repository}, + {"ignores", &test_status_worktree__ignores}, + {"single_file", &test_status_worktree__single_file}, + {"whole_repository", &test_status_worktree__whole_repository} +}; + +static const struct clar_suite _clar_suites[] = { + { + "attr::file", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_attr_file, 4 + }, + { + "attr::lookup", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_attr_lookup, 5 + }, + { + "attr::repo", + {"initialize", &test_attr_repo__initialize}, + {"cleanup", &test_attr_repo__cleanup}, + _clar_cb_attr_repo, 6 + }, + { + "buf::basic", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_buf_basic, 2 + }, + { + "config::add", + {"initialize", &test_config_add__initialize}, + {"cleanup", &test_config_add__cleanup}, + _clar_cb_config_add, 2 + }, + { + "config::new", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_config_new, 1 + }, + { + "config::read", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_config_read, 11 + }, + { + "config::stress", + {"initialize", &test_config_stress__initialize}, + {"cleanup", &test_config_stress__cleanup}, + _clar_cb_config_stress, 1 + }, + { + "config::write", + {"initialize", &test_config_write__initialize}, + {"cleanup", &test_config_write__cleanup}, + _clar_cb_config_write, 3 + }, + { + "core::buffer", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_buffer, 10 + }, + { + "core::dirent", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_dirent, 5 + }, + { + "core::filebuf", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_filebuf, 6 + }, + { + "core::hex", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_hex, 1 + }, + { + "core::oid", + {"initialize", &test_core_oid__initialize}, + {NULL, NULL}, + _clar_cb_core_oid, 1 + }, + { + "core::path", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_path, 10 + }, + { + "core::rmdir", + {"initialize", &test_core_rmdir__initialize}, + {NULL, NULL}, + _clar_cb_core_rmdir, 2 + }, + { + "core::string", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_string, 2 + }, + { + "core::strtol", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_strtol, 2 + }, + { + "core::vector", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_core_vector, 6 + }, + { + "index::read::tree", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_index_read_tree, 1 + }, + { + "index::rename", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_index_rename, 1 + }, + { + "network::createremotethenload", + {"initialize", &test_network_createremotethenload__initialize}, + {"cleanup", &test_network_createremotethenload__cleanup}, + _clar_cb_network_createremotethenload, 1 + }, + { + "network::remotelocal", + {"initialize", &test_network_remotelocal__initialize}, + {"cleanup", &test_network_remotelocal__cleanup}, + _clar_cb_network_remotelocal, 2 + }, + { + "network::remotes", + {"initialize", &test_network_remotes__initialize}, + {"cleanup", &test_network_remotes__cleanup}, + _clar_cb_network_remotes, 4 + }, + { + "object::commit::commitstagedfile", + {"initialize", &test_object_commit_commitstagedfile__initialize}, + {"cleanup", &test_object_commit_commitstagedfile__cleanup}, + _clar_cb_object_commit_commitstagedfile, 1 + }, + { + "object::raw::chars", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_chars, 2 + }, + { + "object::raw::compare", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_compare, 7 + }, + { + "object::raw::convert", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_convert, 2 + }, + { + "object::raw::fromstr", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_fromstr, 2 + }, + { + "object::raw::hash", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_hash, 11 + }, + { + "object::raw::short", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_short, 2 + }, + { + "object::raw::size", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_size, 1 + }, + { + "object::raw::type2string", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_object_raw_type2string, 3 + }, + { + "object::tree::diff", + {"initialize", &test_object_tree_diff__initialize}, + {"cleanup", &test_object_tree_diff__cleanup}, + _clar_cb_object_tree_diff, 4 + }, + { + "object::tree::frompath", + {"initialize", &test_object_tree_frompath__initialize}, + {"cleanup", &test_object_tree_frompath__cleanup}, + _clar_cb_object_tree_frompath, 3 + }, + { + "odb::loose", + {"initialize", &test_odb_loose__initialize}, + {"cleanup", &test_odb_loose__cleanup}, + _clar_cb_odb_loose, 2 + }, + { + "odb::packed", + {"initialize", &test_odb_packed__initialize}, + {"cleanup", &test_odb_packed__cleanup}, + _clar_cb_odb_packed, 3 + }, + { + "odb::sorting", + {"initialize", &test_odb_sorting__initialize}, + {"cleanup", &test_odb_sorting__cleanup}, + _clar_cb_odb_sorting, 2 + }, + { + "refs::crashes", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_refs_crashes, 1 + }, + { + "repo::getters", + {"initialize", &test_repo_getters__initialize}, + {"cleanup", &test_repo_getters__cleanup}, + _clar_cb_repo_getters, 3 + }, + { + "repo::init", + {"initialize", &test_repo_init__initialize}, + {NULL, NULL}, + _clar_cb_repo_init, 4 + }, + { + "repo::open", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_repo_open, 2 + }, + { + "status::ignore", + {"initialize", &test_status_ignore__initialize}, + {"cleanup", &test_status_ignore__cleanup}, + _clar_cb_status_ignore, 1 + }, + { + "status::single", + {NULL, NULL}, + {NULL, NULL}, + _clar_cb_status_single, 1 + }, + { + "status::worktree", + {"initialize", &test_status_worktree__initialize}, + {"cleanup", &test_status_worktree__cleanup}, + _clar_cb_status_worktree, 4 + } +}; + +static size_t _clar_suite_count = 45; +static size_t _clar_callback_count = 150; + +/* Core test functions */ +static void +clar_run_test( + const struct clar_func *test, + const struct clar_func *initialize, + const struct clar_func *cleanup) +{ + int error_st = _clar.suite_errors; + + clar_on_test(); + _clar.trampoline_enabled = 1; + + if (setjmp(_clar.trampoline) == 0) { + if (initialize->ptr != NULL) + initialize->ptr(); + + test->ptr(); + } + + _clar.trampoline_enabled = 0; + + if (_clar.local_cleanup != NULL) + _clar.local_cleanup(_clar.local_cleanup_payload); + + if (cleanup->ptr != NULL) + cleanup->ptr(); + + _clar.test_count++; + + /* remove any local-set cleanup methods */ + _clar.local_cleanup = NULL; + _clar.local_cleanup_payload = NULL; + + clar_print_ontest( + test->name, + _clar.test_count, + (_clar.suite_errors > error_st) + ); +} + +static void +clar_report_errors(void) +{ + int i = 1; + struct clar_error *error, *next; + + error = _clar.errors; + while (error != NULL) { + next = error->next; + clar_print_error(i++, error); + free(error->description); + free(error); + error = next; + } + + _clar.errors = _clar.last_error = NULL; +} + +static void +clar_run_suite(const struct clar_suite *suite) +{ + const struct clar_func *test = suite->tests; + size_t i; + + clar_print_onsuite(suite->name); + clar_on_suite(); + + _clar.active_suite = suite->name; + _clar.suite_errors = 0; + + for (i = 0; i < suite->test_count; ++i) { + _clar.active_test = test[i].name; + clar_run_test(&test[i], &suite->initialize, &suite->cleanup); + } +} + +#if 0 /* temporarily disabled */ +static void +clar_run_single(const struct clar_func *test, + const struct clar_suite *suite) +{ + _clar.suite_errors = 0; + _clar.active_suite = suite->name; + _clar.active_test = test->name; + + clar_run_test(test, &suite->initialize, &suite->cleanup); +} +#endif + +static void +clar_usage(const char *arg) +{ + printf("Usage: %s [options]\n\n", arg); + printf("Options:\n"); +// printf(" -tXX\t\tRun only the test number XX\n"); + printf(" -sXX\t\tRun only the suite number XX\n"); + exit(-1); +} + +static void +clar_parse_args(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; ++i) { + char *argument = argv[i]; + char action; + int num; + + if (argument[0] != '-') + clar_usage(argv[0]); + + action = argument[1]; + num = strtol(argument + 2, &argument, 10); + + if (*argument != '\0' || num < 0) + clar_usage(argv[0]); + + switch (action) { + case 's': + if ((size_t)num >= _clar_suite_count) { + clar_print_onabort("Suite number %d does not exist.\n", num); + exit(-1); + } + + clar_run_suite(&_clar_suites[num]); + break; + + default: + clar_usage(argv[0]); + } + } +} + +static int +clar_test(int argc, char **argv) +{ + clar_print_init( + (int)_clar_callback_count, + (int)_clar_suite_count, + "" + ); + + if (clar_sandbox() < 0) { + clar_print_onabort("Failed to sandbox the test runner.\n"); + exit(-1); + } + + clar_on_init(); + + if (argc > 1) { + clar_parse_args(argc, argv); + } else { + size_t i; + for (i = 0; i < _clar_suite_count; ++i) + clar_run_suite(&_clar_suites[i]); + } + + clar_print_shutdown( + _clar.test_count, + (int)_clar_suite_count, + _clar.total_errors + ); + + clar_on_shutdown(); + + clar_unsandbox(); + return _clar.total_errors; +} + +void +clar__assert( + int condition, + const char *file, + int line, + const char *error_msg, + const char *description, + int should_abort) +{ + struct clar_error *error; + + if (condition) + return; + + error = calloc(1, sizeof(struct clar_error)); + + if (_clar.errors == NULL) + _clar.errors = error; + + if (_clar.last_error != NULL) + _clar.last_error->next = error; + + _clar.last_error = error; + + error->test = _clar.active_test; + error->test_number = _clar.test_count; + error->suite = _clar.active_suite; + error->file = file; + error->line_number = line; + error->error_msg = error_msg; + + if (description != NULL) + error->description = strdup(description); + + _clar.suite_errors++; + _clar.total_errors++; + + if (should_abort) { + if (!_clar.trampoline_enabled) { + clar_print_onabort( + "Fatal error: a cleanup method raised an exception."); + exit(-1); + } + + longjmp(_clar.trampoline, -1); + } +} + +void cl_set_cleanup(void (*cleanup)(void *), void *opaque) +{ + _clar.local_cleanup = cleanup; + _clar.local_cleanup_payload = opaque; +} + +static char _clar_path[4096]; + +static int +is_valid_tmp_path(const char *path) +{ + STAT_T st; + + if (stat(path, &st) != 0) + return 0; + + if (!S_ISDIR(st.st_mode)) + return 0; + + return (access(path, W_OK) == 0); +} + +static int +find_tmp_path(char *buffer, size_t length) +{ +#ifndef _WIN32 + static const size_t var_count = 4; + static const char *env_vars[] = { + "TMPDIR", "TMP", "TEMP", "USERPROFILE" + }; + + size_t i; + + for (i = 0; i < var_count; ++i) { + const char *env = getenv(env_vars[i]); + if (!env) + continue; + + if (is_valid_tmp_path(env)) { + strncpy(buffer, env, length); + return 0; + } + } + + /* If the environment doesn't say anything, try to use /tmp */ + if (is_valid_tmp_path("/tmp")) { + strncpy(buffer, "/tmp", length); + return 0; + } + +#else + if (GetTempPath((DWORD)length, buffer)) + return 0; +#endif + + /* This system doesn't like us, try to use the current directory */ + if (is_valid_tmp_path(".")) { + strncpy(buffer, ".", length); + return 0; + } + + return -1; +} + +static void clar_unsandbox(void) +{ + if (_clar_path[0] == '\0') + return; + +#ifdef _WIN32 + chdir(".."); +#endif + + fs_rm(_clar_path); +} + +static int build_sandbox_path(void) +{ + const char path_tail[] = "clar_tmp_XXXXXX"; + size_t len; + + if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0) + return -1; + + len = strlen(_clar_path); + +#ifdef _WIN32 + { /* normalize path to POSIX forward slashes */ + size_t i; + for (i = 0; i < len; ++i) { + if (_clar_path[i] == '\\') + _clar_path[i] = '/'; + } + } +#endif + + if (_clar_path[len - 1] != '/') { + _clar_path[len++] = '/'; + } + + strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len); + +#ifdef _WIN32 + if (mktemp_s(_clar_path, sizeof(_clar_path)) != 0) + return -1; + + if (mkdir(_clar_path, 0700) != 0) + return -1; +#else + if (mkdtemp(_clar_path) == NULL) + return -1; +#endif + + return 0; +} + +static int clar_sandbox(void) +{ + if (_clar_path[0] == '\0' && build_sandbox_path() < 0) + return -1; + + if (chdir(_clar_path) != 0) + return -1; + + return 0; +} + + +static const char * +fixture_path(const char *base, const char *fixture_name) +{ + static char _path[4096]; + size_t root_len; + + root_len = strlen(base); + strncpy(_path, base, sizeof(_path)); + + if (_path[root_len - 1] != '/') + _path[root_len++] = '/'; + + if (fixture_name[0] == '/') + fixture_name++; + + strncpy(_path + root_len, + fixture_name, + sizeof(_path) - root_len); + + return _path; +} + +#ifdef CLAR_FIXTURE_PATH +const char *cl_fixture(const char *fixture_name) +{ + return fixture_path(CLAR_FIXTURE_PATH, fixture_name); +} + +void cl_fixture_sandbox(const char *fixture_name) +{ + fs_copy(cl_fixture(fixture_name), _clar_path); +} + +void cl_fixture_cleanup(const char *fixture_name) +{ + fs_rm(fixture_path(_clar_path, fixture_name)); +} +#endif + +#ifdef _WIN32 + +#define FOF_FLAGS (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) + +static char * +fileops_path(const char *_path) +{ + char *path = NULL; + size_t length, i; + + if (_path == NULL) + return NULL; + + length = strlen(_path); + path = malloc(length + 2); + + if (path == NULL) + return NULL; + + memcpy(path, _path, length); + path[length] = 0; + path[length + 1] = 0; + + for (i = 0; i < length; ++i) { + if (path[i] == '/') + path[i] = '\\'; + } + + return path; +} + +static void +fileops(int mode, const char *_source, const char *_dest) +{ + SHFILEOPSTRUCT fops; + + char *source = fileops_path(_source); + char *dest = fileops_path(_dest); + + ZeroMemory(&fops, sizeof(SHFILEOPSTRUCT)); + + fops.wFunc = mode; + fops.pFrom = source; + fops.pTo = dest; + fops.fFlags = FOF_FLAGS; + + cl_assert_( + SHFileOperation(&fops) == 0, + "Windows SHFileOperation failed" + ); + + free(source); + free(dest); +} + +static void +fs_rm(const char *_source) +{ + fileops(FO_DELETE, _source, NULL); +} + +static void +fs_copy(const char *_source, const char *_dest) +{ + fileops(FO_COPY, _source, _dest); +} + +void +cl_fs_cleanup(void) +{ + fs_rm(fixture_path(_clar_path, "*")); +} + +#else +static int +shell_out(char * const argv[]) +{ + int status; + pid_t pid; + + pid = fork(); + + if (pid < 0) { + fprintf(stderr, + "System error: `fork()` call failed.\n"); + exit(-1); + } + + if (pid == 0) { + execv(argv[0], argv); + } + + waitpid(pid, &status, 0); + return WEXITSTATUS(status); +} + +static void +fs_copy(const char *_source, const char *dest) +{ + char *argv[5]; + char *source; + size_t source_len; + + source = strdup(_source); + source_len = strlen(source); + + if (source[source_len - 1] == '/') + source[source_len - 1] = 0; + + argv[0] = "/bin/cp"; + argv[1] = "-R"; + argv[2] = source; + argv[3] = (char *)dest; + argv[4] = NULL; + + cl_must_pass_( + shell_out(argv), + "Failed to copy test fixtures to sandbox" + ); + + free(source); +} + +static void +fs_rm(const char *source) +{ + char *argv[4]; + + argv[0] = "/bin/rm"; + argv[1] = "-Rf"; + argv[2] = (char *)source; + argv[3] = NULL; + + cl_must_pass_( + shell_out(argv), + "Failed to cleanup the sandbox" + ); +} + +void +cl_fs_cleanup(void) +{ + clar_unsandbox(); + clar_sandbox(); +} +#endif + + +static void clar_print_init(int test_count, int suite_count, const char *suite_names) +{ + (void)test_count; + (void)suite_names; + (void)suite_count; + printf("TAP version 13\n"); +} + +static void clar_print_shutdown(int test_count, int suite_count, int error_count) +{ + (void)test_count; + (void)suite_count; + (void)error_count; + + if (!error_count) + printf("# passed all %d test(s)\n", test_count); + else + printf("# failed %d among %d test(s)\n", error_count, + test_count); + printf("1..%d\n", test_count); +} + +static void clar_print_error(int num, const struct clar_error *error) +{ + (void)num; + + printf(" ---\n"); + printf(" message : %s\n", error->error_msg); + printf(" severity: fail\n"); + printf(" suite : %s\n", error->suite); + printf(" test : %s\n", error->test); + printf(" file : %s\n", error->file); + printf(" line : %d\n", error->line_number); + + if (error->description != NULL) + printf(" description: %s\n", error->description); + + printf(" ...\n"); +} + +static void clar_print_ontest(const char *test_name, int test_number, int failed) +{ + printf("%s %d - %s\n", + failed ? "not ok" : "ok", + test_number, + test_name + ); + + clar_report_errors(); +} + +static void clar_print_onsuite(const char *suite_name) +{ + printf("# *** %s ***\n", suite_name); +} + +static void clar_print_onabort(const char *msg, ...) +{ + va_list argp; + va_start(argp, msg); + fprintf(stdout, "Bail out! "); + vfprintf(stdout, msg, argp); + va_end(argp); +} + + +int _MAIN_CC main(int argc, char *argv[]) +{ + return clar_test(argc, argv); +} |