diff --git a/.gitattributes b/.gitattributes index 700743c3f5ef99..38b1c52fe0e230 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,3 +17,4 @@ CODE_OF_CONDUCT.md -whitespace /Documentation/gitk.adoc conflict-marker-size=32 /Documentation/user-manual.adoc conflict-marker-size=32 /t/t????-*.sh conflict-marker-size=32 +/t/unit-tests/clar/test/expected/* whitespace=-blank-at-eof diff --git a/Documentation/RelNotes/2.53.0.adoc b/Documentation/RelNotes/2.53.0.adoc index 35a1ab91edc7fd..dcdebe8954f067 100644 --- a/Documentation/RelNotes/2.53.0.adoc +++ b/Documentation/RelNotes/2.53.0.adoc @@ -34,6 +34,10 @@ UI, Workflows & Features * More object database related information are shown in "git repo structure" output. + * Improve the error message when a bad argument is given to the + `--onto` option of "git replay". Test coverage of "git replay" has + been improved. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -92,6 +96,9 @@ Performance, Internal Implementation, Development Support etc. * Use hook API to replace ad-hoc invocation of hook scripts with the run_command() API. + * Import newer version of "clar", unit testing framework. + (merge 84071a6dea ps/clar-integers later to maint). + Fixes since v2.52 ----------------- diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 1f7af0328a0461..5adc4afd67488c 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,6 +1,6 @@ #!/bin/sh -DEF_VER=v2.52.GIT +DEF_VER=v2.53.0-rc0 LF=' ' diff --git a/builtin/gc.c b/builtin/gc.c index 92c6e7b954faff..17ff68cbd91037 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1130,8 +1130,10 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data) return 0; commit = lookup_commit(the_repository, maybe_peeled); - if (!commit) + if (!commit || commit->object.flags & SEEN) return 0; + commit->object.flags |= SEEN; + if (repo_parse_commit(the_repository, commit) || commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH) return 0; @@ -1141,7 +1143,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data) if (data->num_not_in_graph >= data->limit) return 1; - commit_list_append(commit, &stack); + commit_list_insert(commit, &stack); while (!result && stack) { struct commit_list *parent; @@ -1162,7 +1164,7 @@ static int dfs_on_ref(const struct reference *ref, void *cb_data) break; } - commit_list_append(parent->item, &stack); + commit_list_insert(parent->item, &stack); } } diff --git a/builtin/replay.c b/builtin/replay.c index 69c4c551297c03..1960bbbee8685d 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -33,14 +33,16 @@ static const char *short_commit_name(struct repository *repo, DEFAULT_ABBREV); } -static struct commit *peel_committish(struct repository *repo, const char *name) +static struct commit *peel_committish(struct repository *repo, + const char *name, + const char *mode) { struct object *obj; struct object_id oid; if (repo_get_oid(repo, name, &oid)) - return NULL; - obj = parse_object(repo, &oid); + die(_("'%s' is not a valid commit-ish for %s"), name, mode); + obj = parse_object_or_die(repo, &oid, name); return (struct commit *)repo_peel_to_type(repo, name, 0, obj, OBJ_COMMIT); } @@ -162,12 +164,12 @@ static void get_ref_information(struct repository *repo, } } -static void determine_replay_mode(struct repository *repo, - struct rev_cmdline_info *cmd_info, - const char *onto_name, - char **advance_name, - struct commit **onto, - struct strset **update_refs) +static void set_up_replay_mode(struct repository *repo, + struct rev_cmdline_info *cmd_info, + const char *onto_name, + char **advance_name, + struct commit **onto, + struct strset **update_refs) { struct ref_info rinfo; @@ -178,15 +180,20 @@ static void determine_replay_mode(struct repository *repo, die_for_incompatible_opt2(!!onto_name, "--onto", !!*advance_name, "--advance"); if (onto_name) { - *onto = peel_committish(repo, onto_name); + *onto = peel_committish(repo, onto_name, "--onto"); if (rinfo.positive_refexprs < strset_get_size(&rinfo.positive_refs)) die(_("all positive revisions given must be references")); - } else if (*advance_name) { + *update_refs = xcalloc(1, sizeof(**update_refs)); + **update_refs = rinfo.positive_refs; + memset(&rinfo.positive_refs, 0, sizeof(**update_refs)); + } else { struct object_id oid; char *fullname = NULL; - *onto = peel_committish(repo, *advance_name); + if (!*advance_name) + BUG("expected either onto_name or *advance_name in this function"); + if (repo_dwim_ref(repo, *advance_name, strlen(*advance_name), &oid, &fullname, 0) == 1) { free(*advance_name); @@ -194,53 +201,9 @@ static void determine_replay_mode(struct repository *repo, } else { die(_("argument to --advance must be a reference")); } + *onto = peel_committish(repo, *advance_name, "--advance"); if (rinfo.positive_refexprs > 1) die(_("cannot advance target with multiple sources because ordering would be ill-defined")); - } else { - int positive_refs_complete = ( - rinfo.positive_refexprs == - strset_get_size(&rinfo.positive_refs)); - int negative_refs_complete = ( - rinfo.negative_refexprs == - strset_get_size(&rinfo.negative_refs)); - /* - * We need either positive_refs_complete or - * negative_refs_complete, but not both. - */ - if (rinfo.negative_refexprs > 0 && - positive_refs_complete == negative_refs_complete) - die(_("cannot implicitly determine whether this is an --advance or --onto operation")); - if (negative_refs_complete) { - struct hashmap_iter iter; - struct strmap_entry *entry; - const char *last_key = NULL; - - if (rinfo.negative_refexprs == 0) - die(_("all positive revisions given must be references")); - else if (rinfo.negative_refexprs > 1) - die(_("cannot implicitly determine whether this is an --advance or --onto operation")); - else if (rinfo.positive_refexprs > 1) - die(_("cannot advance target with multiple source branches because ordering would be ill-defined")); - - /* Only one entry, but we have to loop to get it */ - strset_for_each_entry(&rinfo.negative_refs, - &iter, entry) { - last_key = entry->key; - } - - free(*advance_name); - *advance_name = xstrdup_or_null(last_key); - } else { /* positive_refs_complete */ - if (rinfo.negative_refexprs > 1) - die(_("cannot implicitly determine correct base for --onto")); - if (rinfo.negative_refexprs == 1) - *onto = rinfo.onto; - } - } - if (!*advance_name) { - *update_refs = xcalloc(1, sizeof(**update_refs)); - **update_refs = rinfo.positive_refs; - memset(&rinfo.positive_refs, 0, sizeof(**update_refs)); } strset_clear(&rinfo.negative_refs); strset_clear(&rinfo.positive_refs); @@ -451,11 +414,11 @@ int cmd_replay(int argc, revs.simplify_history = 0; } - determine_replay_mode(repo, &revs.cmdline, onto_name, &advance_name, - &onto, &update_refs); + set_up_replay_mode(repo, &revs.cmdline, + onto_name, &advance_name, + &onto, &update_refs); - if (!onto) /* FIXME: Should handle replaying down to root commit */ - die("Replaying down to root commit is not supported yet!"); + /* FIXME: Should allow replaying commits with the first as a root commit */ /* Build reflog message */ if (advance_name_opt) @@ -491,7 +454,7 @@ int cmd_replay(int argc, int hr; if (!commit->parents) - die(_("replaying down to root commit is not supported yet!")); + die(_("replaying down from root commit is not supported yet!")); if (commit->parents->next) die(_("replaying merge commits is not supported yet!")); diff --git a/odb.c b/odb.c index ffd78e1c467219..838aa2e53a08d7 100644 --- a/odb.c +++ b/odb.c @@ -1117,13 +1117,13 @@ void odb_free(struct object_database *o) oidmap_clear(&o->replace_map, 1); pthread_mutex_destroy(&o->replace_mutex); + odb_close(o); odb_free_sources(o); for (size_t i = 0; i < o->cached_object_nr; i++) free((char *) o->cached_objects[i].value.buf); free(o->cached_objects); - odb_close(o); packfile_store_free(o->packfiles); string_list_clear(&o->submodule_source_paths, 0); diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index cf3aacf3551f8e..307101eeb911f7 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -43,6 +43,13 @@ test_expect_success 'setup' ' test_commit L && test_commit M && + git switch --detach topic4 && + test_commit N && + test_commit O && + git switch -c topic-with-merge topic4 && + test_merge P O --no-ff && + git switch main && + git switch -c conflict B && test_commit C.conflict C.t conflict ' @@ -51,6 +58,53 @@ test_expect_success 'setup bare' ' git clone --bare . bare ' +test_expect_success 'argument to --advance must be a reference' ' + echo "fatal: argument to --advance must be a reference" >expect && + oid=$(git rev-parse main) && + test_must_fail git replay --advance=$oid topic1..topic2 2>actual && + test_cmp expect actual +' + +test_expect_success '--onto with invalid commit-ish' ' + printf "fatal: ${SQ}refs/not-valid${SQ} is not " >expect && + printf "a valid commit-ish for --onto\n" >>expect && + test_must_fail git replay --onto=refs/not-valid topic1..topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'option --onto or --advance is mandatory' ' + echo "error: option --onto or --advance is mandatory" >expect && + test_might_fail git replay -h >>expect && + test_must_fail git replay topic1..topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'no base or negative ref gives no-replaying down to root error' ' + echo "fatal: replaying down from root commit is not supported yet!" >expect && + test_must_fail git replay --onto=topic1 topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'options --advance and --contained cannot be used together' ' + printf "fatal: options ${SQ}--advance${SQ} " >expect && + printf "and ${SQ}--contained${SQ} cannot be used together\n" >>expect && + test_must_fail git replay --advance=main --contained \ + topic1..topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'cannot advance target ... ordering would be ill-defined' ' + echo "fatal: cannot advance target with multiple sources because ordering would be ill-defined" >expect && + test_must_fail git replay --advance=main main topic1 topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'replaying merge commits is not supported yet' ' + echo "fatal: replaying merge commits is not supported yet!" >expect && + test_must_fail git replay --advance=main main..topic-with-merge 2>actual && + test_cmp expect actual +' + test_expect_success 'using replay to rebase two branches, one on top of other' ' git replay --ref-action=print --onto main topic1..topic2 >result && diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index bf0f67378dbb23..8a91ff3603ff92 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -647,21 +647,21 @@ test_expect_success SYMLINKS 'difftool --dir-diff --symlinks without unstaged ch ' write_script modify-right-file <<\EOF -echo "new content" >"$2/file" +echo "modified content" >"$2/file" EOF run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' ' test_when_finished git reset --hard && echo "orig content" >file && git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch && - echo "new content" >expect && + echo "modified content" >expect && test_cmp expect file ' run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' ' test_when_finished git reset --hard && git difftool -d $symlinks --extcmd "$PWD/modify-right-file" branch && - echo "new content" >expect && + echo "modified content" >expect && test_cmp expect file ' diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 6b36f52df7c95d..7cc0ce57f8f320 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -206,6 +206,31 @@ test_expect_success 'commit-graph auto condition' ' test_subcommand $COMMIT_GRAPH_WRITE err && test_grep "is not a valid task" err diff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml index 4d4724222c3e89..14cb4ed1d4598a 100644 --- a/t/unit-tests/clar/.github/workflows/ci.yml +++ b/t/unit-tests/clar/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: if: matrix.platform.image == 'i386/debian:latest' run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3 - name: Check out - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Build shell: bash run: | diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c index d6176e50b2214b..e959a5ae028b5a 100644 --- a/t/unit-tests/clar/clar.c +++ b/t/unit-tests/clar/clar.c @@ -24,6 +24,14 @@ #include #include +#ifndef va_copy +# ifdef __va_copy +# define va_copy(dst, src) __va_copy(dst, src) +# else +# define va_copy(dst, src) ((dst) = (src)) +# endif +#endif + #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__) /* * uClibc can optionally be built without wchar support, in which case @@ -76,8 +84,10 @@ # define S_ISDIR(x) ((x & _S_IFDIR) != 0) # endif # define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__) +# define p_vsnprintf _vsnprintf # else # define p_snprintf snprintf +# define p_vsnprintf vsnprintf # endif # define localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL) @@ -86,6 +96,7 @@ # include # define _MAIN_CC # define p_snprintf snprintf +# define p_vsnprintf vsnprintf typedef struct stat STAT_T; #endif @@ -699,13 +710,14 @@ void clar__skip(void) abort_test(); } -void clar__fail( +static void clar__failv( const char *file, const char *function, size_t line, + int should_abort, const char *error_msg, const char *description, - int should_abort) + va_list args) { struct clar_error *error; @@ -725,9 +737,19 @@ void clar__fail( error->line_number = _clar.invoke_line ? _clar.invoke_line : line; error->error_msg = error_msg; - if (description != NULL && - (error->description = strdup(description)) == NULL) - clar_abort("Failed to allocate description.\n"); + if (description != NULL) { + va_list args_copy; + int len; + + va_copy(args_copy, args); + if ((len = p_vsnprintf(NULL, 0, description, args_copy)) < 0) + clar_abort("Failed to compute description."); + va_end(args_copy); + + if ((error->description = calloc(1, len + 1)) == NULL) + clar_abort("Failed to allocate buffer."); + p_vsnprintf(error->description, len + 1, description, args); + } _clar.total_errors++; _clar.last_report->status = CL_TEST_FAILURE; @@ -736,6 +758,34 @@ void clar__fail( abort_test(); } +void clar__failf( + const char *file, + const char *function, + size_t line, + int should_abort, + const char *error_msg, + const char *description, + ...) +{ + va_list args; + va_start(args, description); + clar__failv(file, function, line, should_abort, error_msg, + description, args); + va_end(args); +} + +void clar__fail( + const char *file, + const char *function, + size_t line, + const char *error_msg, + const char *description, + int should_abort) +{ + clar__failf(file, function, line, should_abort, error_msg, + description ? "%s" : NULL, description); +} + void clar__assert( int condition, const char *file, @@ -889,6 +939,92 @@ void clar__assert_equal( clar__fail(file, function, line, err, buf, should_abort); } +void clar__assert_compare_i( + const char *file, + const char *func, + size_t line, + int should_abort, + enum clar_comparison cmp, + intmax_t value1, + intmax_t value2, + const char *error, + const char *description, + ...) +{ + int fulfilled; + switch (cmp) { + case CLAR_COMPARISON_EQ: + fulfilled = value1 == value2; + break; + case CLAR_COMPARISON_LT: + fulfilled = value1 < value2; + break; + case CLAR_COMPARISON_LE: + fulfilled = value1 <= value2; + break; + case CLAR_COMPARISON_GT: + fulfilled = value1 > value2; + break; + case CLAR_COMPARISON_GE: + fulfilled = value1 >= value2; + break; + default: + cl_assert(0); + return; + } + + if (!fulfilled) { + va_list args; + va_start(args, description); + clar__failv(file, func, line, should_abort, error, + description, args); + va_end(args); + } +} + +void clar__assert_compare_u( + const char *file, + const char *func, + size_t line, + int should_abort, + enum clar_comparison cmp, + uintmax_t value1, + uintmax_t value2, + const char *error, + const char *description, + ...) +{ + int fulfilled; + switch (cmp) { + case CLAR_COMPARISON_EQ: + fulfilled = value1 == value2; + break; + case CLAR_COMPARISON_LT: + fulfilled = value1 < value2; + break; + case CLAR_COMPARISON_LE: + fulfilled = value1 <= value2; + break; + case CLAR_COMPARISON_GT: + fulfilled = value1 > value2; + break; + case CLAR_COMPARISON_GE: + fulfilled = value1 >= value2; + break; + default: + cl_assert(0); + return; + } + + if (!fulfilled) { + va_list args; + va_start(args, description); + clar__failv(file, func, line, should_abort, error, + description, args); + va_end(args); + } +} + void cl_set_cleanup(void (*cleanup)(void *), void *opaque) { _clar.local_cleanup = cleanup; diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h index ca72292ae918da..f7e43630226434 100644 --- a/t/unit-tests/clar/clar.h +++ b/t/unit-tests/clar/clar.h @@ -7,6 +7,7 @@ #ifndef __CLAR_TEST_H__ #define __CLAR_TEST_H__ +#include #include #include @@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name); * Forced failure/warning */ #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1) +#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, "Test failed.", desc, __VA_ARGS__) #define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0) #define cl_skip() clar__skip() @@ -168,9 +170,42 @@ const char *cl_fixture_basename(const char *fixture_name); #define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) #define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) -#define cl_assert_equal_i(i1,i2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) -#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) -#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) +#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \ + (i1), (i2), "Expected comparison to hold: " error, __VA_ARGS__) +#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \ + intmax_t v1 = (i1), v2 = (i2); \ + clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \ + v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \ +} while (0) +#define cl_assert_equal_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, __VA_ARGS__) +#define cl_assert_equal_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, "%"PRIdMAX " != %"PRIdMAX) +#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, fmt " != " fmt, (int)(i1), (int)(i2)) +#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, __VA_ARGS__) +#define cl_assert_lt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, "%"PRIdMAX " >= %"PRIdMAX) +#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, __VA_ARGS__) +#define cl_assert_le_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, "%"PRIdMAX " > %"PRIdMAX) +#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, __VA_ARGS__) +#define cl_assert_gt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, "%"PRIdMAX " <= %"PRIdMAX) +#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, __VA_ARGS__) +#define cl_assert_ge_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, "%"PRIdMAX " < %"PRIdMAX) + +#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \ + (u1), (u2), "Expected comparison to hold: " error, __VA_ARGS__) +#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \ + uintmax_t v1 = (u1), v2 = (u2); \ + clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \ + v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \ +} while (0) +#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, __VA_ARGS__) +#define cl_assert_equal_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, "%"PRIuMAX " != %"PRIuMAX) +#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, __VA_ARGS__) +#define cl_assert_lt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, "%"PRIuMAX " >= %"PRIuMAX) +#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, __VA_ARGS__) +#define cl_assert_le_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, "%"PRIuMAX " > %"PRIuMAX) +#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, __VA_ARGS__) +#define cl_assert_gt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, "%"PRIuMAX " <= %"PRIuMAX) +#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, __VA_ARGS__) +#define cl_assert_ge_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, "%"PRIuMAX " < %"PRIuMAX) #define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) @@ -186,6 +221,15 @@ void clar__fail( const char *description, int should_abort); +void clar__failf( + const char *file, + const char *func, + size_t line, + int should_abort, + const char *error, + const char *description, + ...); + void clar__assert( int condition, const char *file, @@ -204,6 +248,38 @@ void clar__assert_equal( const char *fmt, ...); +enum clar_comparison { + CLAR_COMPARISON_EQ, + CLAR_COMPARISON_LT, + CLAR_COMPARISON_LE, + CLAR_COMPARISON_GT, + CLAR_COMPARISON_GE, +}; + +void clar__assert_compare_i( + const char *file, + const char *func, + size_t line, + int should_abort, + enum clar_comparison cmp, + intmax_t value1, + intmax_t value2, + const char *error, + const char *description, + ...); + +void clar__assert_compare_u( + const char *file, + const char *func, + size_t line, + int should_abort, + enum clar_comparison cmp, + uintmax_t value1, + uintmax_t value2, + const char *error, + const char *description, + ...); + void clar__set_invokepoint( const char *file, const char *func, diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h index 89b66591d7556d..6a2321b399d192 100644 --- a/t/unit-tests/clar/clar/print.h +++ b/t/unit-tests/clar/clar/print.h @@ -164,7 +164,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name, printf(" file: '"); print_escaped(error->file); printf("'\n"); printf(" line: %" PRIuMAX "\n", error->line_number); printf(" function: '%s'\n", error->function); - printf(" ---\n"); + printf(" ...\n"); } break; diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet index 280c99d8ad5eba..a93273b5a23003 100644 --- a/t/unit-tests/clar/test/expected/quiet +++ b/t/unit-tests/clar/test/expected/quiet @@ -18,27 +18,57 @@ combined::strings_with_length [file:42] 5) Failure: combined::int [file:42] - 101 != value ("extra note on failing test") + Expected comparison to hold: 101 == value 101 != 100 6) Failure: +combined::int_note [file:42] + Expected comparison to hold: 101 == value + extra note on failing test + + 7) Failure: combined::int_fmt [file:42] - 022 != value + Expected comparison to hold: 022 == value 0022 != 0144 - 7) Failure: + 8) Failure: combined::bool [file:42] 0 != value 0 != 1 - 8) Failure: + 9) Failure: combined::multiline_description [file:42] Function call failed: -1 description line 1 description line 2 - 9) Failure: + 10) Failure: combined::null_string [file:42] String mismatch: "expected" != actual ("this one fails") 'expected' != NULL + 11) Failure: +combined::failf [file:42] + Test failed. + some reason: foo + + 12) Failure: +combined::compare_i [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 13) Failure: +combined::compare_i_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + + 14) Failure: +combined::compare_u [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 15) Failure: +combined::compare_u_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + diff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename index 460160791d14c0..a9471cc7d51762 100644 --- a/t/unit-tests/clar/test/expected/summary_with_filename +++ b/t/unit-tests/clar/test/expected/summary_with_filename @@ -1,6 +1,6 @@ Loaded 1 suites: Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') -FFFFFFFFF +FFFFFFFFFFFFFFF 1) Failure: combined::1 [file:42] @@ -22,28 +22,58 @@ combined::strings_with_length [file:42] 5) Failure: combined::int [file:42] - 101 != value ("extra note on failing test") + Expected comparison to hold: 101 == value 101 != 100 6) Failure: +combined::int_note [file:42] + Expected comparison to hold: 101 == value + extra note on failing test + + 7) Failure: combined::int_fmt [file:42] - 022 != value + Expected comparison to hold: 022 == value 0022 != 0144 - 7) Failure: + 8) Failure: combined::bool [file:42] 0 != value 0 != 1 - 8) Failure: + 9) Failure: combined::multiline_description [file:42] Function call failed: -1 description line 1 description line 2 - 9) Failure: + 10) Failure: combined::null_string [file:42] String mismatch: "expected" != actual ("this one fails") 'expected' != NULL + 11) Failure: +combined::failf [file:42] + Test failed. + some reason: foo + + 12) Failure: +combined::compare_i [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 13) Failure: +combined::compare_i_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + + 14) Failure: +combined::compare_u [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 15) Failure: +combined::compare_u_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + written summary file to different.xml diff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename index 7874c1d98bc01b..83ba770d006e78 100644 --- a/t/unit-tests/clar/test/expected/summary_without_filename +++ b/t/unit-tests/clar/test/expected/summary_without_filename @@ -1,6 +1,6 @@ Loaded 1 suites: Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') -FFFFFFFFF +FFFFFFFFFFFFFFF 1) Failure: combined::1 [file:42] @@ -22,28 +22,58 @@ combined::strings_with_length [file:42] 5) Failure: combined::int [file:42] - 101 != value ("extra note on failing test") + Expected comparison to hold: 101 == value 101 != 100 6) Failure: +combined::int_note [file:42] + Expected comparison to hold: 101 == value + extra note on failing test + + 7) Failure: combined::int_fmt [file:42] - 022 != value + Expected comparison to hold: 022 == value 0022 != 0144 - 7) Failure: + 8) Failure: combined::bool [file:42] 0 != value 0 != 1 - 8) Failure: + 9) Failure: combined::multiline_description [file:42] Function call failed: -1 description line 1 description line 2 - 9) Failure: + 10) Failure: combined::null_string [file:42] String mismatch: "expected" != actual ("this one fails") 'expected' != NULL + 11) Failure: +combined::failf [file:42] + Test failed. + some reason: foo + + 12) Failure: +combined::compare_i [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 13) Failure: +combined::compare_i_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + + 14) Failure: +combined::compare_u [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 15) Failure: +combined::compare_u_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + written summary file to summary.xml diff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap index bddbd5dfe98b61..e67118d3aedbf8 100644 --- a/t/unit-tests/clar/test/expected/tap +++ b/t/unit-tests/clar/test/expected/tap @@ -8,7 +8,7 @@ not ok 1 - combined::1 file: 'file' line: 42 function: 'func' - --- + ... not ok 2 - combined::2 --- reason: | @@ -17,7 +17,7 @@ not ok 2 - combined::2 file: 'file' line: 42 function: 'func' - --- + ... not ok 3 - combined::strings --- reason: | @@ -27,7 +27,7 @@ not ok 3 - combined::strings file: 'file' line: 42 function: 'func' - --- + ... not ok 4 - combined::strings_with_length --- reason: | @@ -37,28 +37,38 @@ not ok 4 - combined::strings_with_length file: 'file' line: 42 function: 'func' - --- + ... not ok 5 - combined::int --- reason: | - 101 != value ("extra note on failing test") + Expected comparison to hold: 101 == value 101 != 100 at: file: 'file' line: 42 function: 'func' + ... +not ok 6 - combined::int_note --- -not ok 6 - combined::int_fmt + reason: | + Expected comparison to hold: 101 == value + extra note on failing test + at: + file: 'file' + line: 42 + function: 'func' + ... +not ok 7 - combined::int_fmt --- reason: | - 022 != value + Expected comparison to hold: 022 == value 0022 != 0144 at: file: 'file' line: 42 function: 'func' - --- -not ok 7 - combined::bool + ... +not ok 8 - combined::bool --- reason: | 0 != value @@ -67,8 +77,8 @@ not ok 7 - combined::bool file: 'file' line: 42 function: 'func' - --- -not ok 8 - combined::multiline_description + ... +not ok 9 - combined::multiline_description --- reason: | Function call failed: -1 @@ -78,8 +88,8 @@ not ok 8 - combined::multiline_description file: 'file' line: 42 function: 'func' - --- -not ok 9 - combined::null_string + ... +not ok 10 - combined::null_string --- reason: | String mismatch: "expected" != actual ("this one fails") @@ -88,5 +98,55 @@ not ok 9 - combined::null_string file: 'file' line: 42 function: 'func' + ... +not ok 11 - combined::failf + --- + reason: | + Test failed. + some reason: foo + at: + file: 'file' + line: 42 + function: 'func' + ... +not ok 12 - combined::compare_i --- -1..9 + reason: | + Expected comparison to hold: two < 1 + 2 >= 1 + at: + file: 'file' + line: 42 + function: 'func' + ... +not ok 13 - combined::compare_i_with_format + --- + reason: | + Expected comparison to hold: two < 1 + foo: bar + at: + file: 'file' + line: 42 + function: 'func' + ... +not ok 14 - combined::compare_u + --- + reason: | + Expected comparison to hold: two < 1 + 2 >= 1 + at: + file: 'file' + line: 42 + function: 'func' + ... +not ok 15 - combined::compare_u_with_format + --- + reason: | + Expected comparison to hold: two < 1 + foo: bar + at: + file: 'file' + line: 42 + function: 'func' + ... +1..15 diff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments index 1111d418a060f7..9891f45a703984 100644 --- a/t/unit-tests/clar/test/expected/without_arguments +++ b/t/unit-tests/clar/test/expected/without_arguments @@ -1,6 +1,6 @@ Loaded 1 suites: Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') -FFFFFFFFF +FFFFFFFFFFFFFFF 1) Failure: combined::1 [file:42] @@ -22,27 +22,57 @@ combined::strings_with_length [file:42] 5) Failure: combined::int [file:42] - 101 != value ("extra note on failing test") + Expected comparison to hold: 101 == value 101 != 100 6) Failure: +combined::int_note [file:42] + Expected comparison to hold: 101 == value + extra note on failing test + + 7) Failure: combined::int_fmt [file:42] - 022 != value + Expected comparison to hold: 022 == value 0022 != 0144 - 7) Failure: + 8) Failure: combined::bool [file:42] 0 != value 0 != 1 - 8) Failure: + 9) Failure: combined::multiline_description [file:42] Function call failed: -1 description line 1 description line 2 - 9) Failure: + 10) Failure: combined::null_string [file:42] String mismatch: "expected" != actual ("this one fails") 'expected' != NULL + 11) Failure: +combined::failf [file:42] + Test failed. + some reason: foo + + 12) Failure: +combined::compare_i [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 13) Failure: +combined::compare_i_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + + 14) Failure: +combined::compare_u [file:42] + Expected comparison to hold: two < 1 + 2 >= 1 + + 15) Failure: +combined::compare_u_with_format [file:42] + Expected comparison to hold: two < 1 + foo: bar + diff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c index eed83e4512006d..6eadc64c4813b8 100644 --- a/t/unit-tests/clar/test/selftest.c +++ b/t/unit-tests/clar/test/selftest.c @@ -298,7 +298,7 @@ void test_selftest__help(void) void test_selftest__without_arguments(void) { - cl_invoke(assert_output("combined", "without_arguments", 9, NULL)); + cl_invoke(assert_output("combined", "without_arguments", 15, NULL)); } void test_selftest__specific_test(void) @@ -313,12 +313,12 @@ void test_selftest__stop_on_failure(void) void test_selftest__quiet(void) { - cl_invoke(assert_output("combined", "quiet", 9, "-q", NULL)); + cl_invoke(assert_output("combined", "quiet", 15, "-q", NULL)); } void test_selftest__tap(void) { - cl_invoke(assert_output("combined", "tap", 9, "-t", NULL)); + cl_invoke(assert_output("combined", "tap", 15, "-t", NULL)); } void test_selftest__suite_names(void) @@ -329,7 +329,7 @@ void test_selftest__suite_names(void) void test_selftest__summary_without_filename(void) { struct stat st; - cl_invoke(assert_output("combined", "summary_without_filename", 9, "-r", NULL)); + cl_invoke(assert_output("combined", "summary_without_filename", 15, "-r", NULL)); /* The summary contains timestamps, so we cannot verify its contents. */ cl_must_pass(stat("summary.xml", &st)); } @@ -337,7 +337,7 @@ void test_selftest__summary_without_filename(void) void test_selftest__summary_with_filename(void) { struct stat st; - cl_invoke(assert_output("combined", "summary_with_filename", 9, "-rdifferent.xml", NULL)); + cl_invoke(assert_output("combined", "summary_with_filename", 15, "-rdifferent.xml", NULL)); /* The summary contains timestamps, so we cannot verify its contents. */ cl_must_pass(stat("different.xml", &st)); } diff --git a/t/unit-tests/clar/test/suites/combined.c b/t/unit-tests/clar/test/suites/combined.c index e8b41c98c37fa2..9e9dbc2fb1f180 100644 --- a/t/unit-tests/clar/test/suites/combined.c +++ b/t/unit-tests/clar/test/suites/combined.c @@ -55,7 +55,12 @@ void test_combined__strings_with_length(void) void test_combined__int(void) { int value = 100; - cl_assert_equal_i(100, value); + cl_assert_equal_i(101, value); +} + +void test_combined__int_note(void) +{ + int value = 100; cl_assert_equal_i_(101, value, "extra note on failing test"); } @@ -83,3 +88,61 @@ void test_combined__null_string(void) cl_assert_equal_s(actual, actual); cl_assert_equal_s_("expected", actual, "this one fails"); } + +void test_combined__failf(void) +{ + cl_failf("some reason: %s", "foo"); +} + +void test_combined__compare_i(void) +{ + int one = 1, two = 2; + + cl_assert_equal_i(one, 1); + cl_assert_equal_i(one, 1); + cl_assert_equal_i_(one, 1, "format"); + cl_assert_lt_i(one, 2); + cl_assert_lt_i_(one, 2, "format"); + cl_assert_le_i(one, 2); + cl_assert_le_i(two, 2); + cl_assert_le_i_(two, 2, "format"); + cl_assert_gt_i(two, 1); + cl_assert_gt_i_(two, 1, "format"); + cl_assert_ge_i(two, 2); + cl_assert_ge_i(3, two); + cl_assert_ge_i_(3, two, "format"); + + cl_assert_lt_i(two, 1); /* this one fails */ +} + +void test_combined__compare_i_with_format(void) +{ + int two = 2; + cl_assert_lt_i_(two, 1, "foo: %s", "bar"); +} + +void test_combined__compare_u(void) +{ + unsigned one = 1, two = 2; + + cl_assert_equal_u(one, 1); + cl_assert_equal_u_(one, 1, "format"); + cl_assert_lt_u(one, 2); + cl_assert_lt_u_(one, 2, "format"); + cl_assert_le_u(one, 2); + cl_assert_le_u(two, 2); + cl_assert_le_u_(two, 2, "format"); + cl_assert_gt_u(two, 1); + cl_assert_gt_u_(two, 1, "format"); + cl_assert_ge_u(two, 2); + cl_assert_ge_u(3, two); + cl_assert_ge_u_(3, two, "format"); + + cl_assert_lt_u(two, 1); /* this one fails */ +} + +void test_combined__compare_u_with_format(void) +{ + unsigned two = 2; + cl_assert_lt_u_(two, 1, "foo: %s", "bar"); +} diff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c index 6c8c0d5374a6a2..1bf2e170dc96a0 100644 --- a/t/unit-tests/u-reftable-record.c +++ b/t/unit-tests/u-reftable-record.c @@ -51,10 +51,10 @@ void test_reftable_record__varint_roundtrip(void) int n = put_var_int(&out, in); uint64_t got = 0; - cl_assert(n > 0); + cl_assert_gt_i(n, 0); out.len = n; n = get_var_int(&got, &out); - cl_assert(n > 0); + cl_assert_gt_i(n, 0); cl_assert_equal_i(got, in); } @@ -110,7 +110,7 @@ void test_reftable_record__ref_record_comparison(void) cl_assert(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1) == 0); cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); - cl_assert(cmp > 0); + cl_assert_gt_i(cmp, 0); in[1].u.ref.value_type = in[0].u.ref.value_type; cl_assert(reftable_record_equal(&in[0], &in[1], @@ -184,7 +184,7 @@ void test_reftable_record__ref_record_roundtrip(void) reftable_record_key(&in, &key); n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1); - cl_assert(n > 0); + cl_assert_gt_i(n, 0); /* decode into a non-zero reftable_record to test for leaks. */ m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); @@ -228,11 +228,11 @@ void test_reftable_record__log_record_comparison(void) cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1), 0); cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); - cl_assert(cmp > 0); + cl_assert_gt_i(cmp, 0); /* comparison should be reversed for equal keys, because * comparison is now performed on the basis of update indices */ cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); - cl_assert(cmp < 0); + cl_assert_lt_i(cmp, 0); in[1].u.log.update_index = in[0].u.log.update_index; cl_assert(reftable_record_equal(&in[0], &in[1], @@ -344,7 +344,7 @@ void test_reftable_record__log_record_roundtrip(void) reftable_record_key(&rec, &key); n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1); - cl_assert(n >= 0); + cl_assert_ge_i(n, 0); valtype = reftable_record_val_type(&rec); m = reftable_record_decode(&out, key, valtype, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); @@ -382,7 +382,7 @@ void test_reftable_record__key_roundtrip(void) extra = 6; n = reftable_encode_key(&restart, dest, last_key, key, extra); cl_assert(!restart); - cl_assert(n > 0); + cl_assert_gt_i(n, 0); cl_assert_equal_i(reftable_buf_addstr(&roundtrip, "refs/heads/master"), 0); @@ -432,7 +432,7 @@ void test_reftable_record__obj_record_comparison(void) cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1), 0); cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); - cl_assert(cmp > 0); + cl_assert_gt_i(cmp, 0); in[1].u.obj.offset_len = in[0].u.obj.offset_len; cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0); @@ -485,7 +485,7 @@ void test_reftable_record__obj_record_roundtrip(void) t_copy(&in); reftable_record_key(&in, &key); n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1); - cl_assert(n > 0); + cl_assert_gt_i(n, 0); extra = reftable_record_val_type(&in); m = reftable_record_decode(&out, key, extra, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); @@ -535,7 +535,7 @@ void test_reftable_record__index_record_comparison(void) cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1), 0); cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); - cl_assert(cmp > 0); + cl_assert_gt_i(cmp, 0); in[1].u.idx.offset = in[0].u.idx.offset; cl_assert(reftable_record_equal(&in[0], &in[1], diff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h index 39a0b72a05dec3..5398b449171560 100644 --- a/t/unit-tests/unit-test.h +++ b/t/unit-tests/unit-test.h @@ -7,9 +7,3 @@ #else # include GIT_CLAR_DECLS_H #endif - -#define cl_failf(fmt, ...) do { \ - char desc[4096]; \ - snprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \ - clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1); \ -} while (0)