Skip to content

Commit 045efb7

Browse files
authored
Merge pull request libgit2#5509 from libgit2/ethomson/assert_macros
Introduce GIT_ASSERT macros
2 parents 31ddf16 + cbae1c2 commit 045efb7

File tree

4 files changed

+155
-1
lines changed

4 files changed

+155
-1
lines changed

include/git2/errors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ typedef enum {
107107
GIT_ERROR_PATCH,
108108
GIT_ERROR_WORKTREE,
109109
GIT_ERROR_SHA1,
110-
GIT_ERROR_HTTP
110+
GIT_ERROR_HTTP,
111+
GIT_ERROR_INTERNAL
111112
} git_error_t;
112113

113114
/**

src/assert_safe.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
#ifndef INCLUDE_assert_safe_h__
8+
#define INCLUDE_assert_safe_h__
9+
10+
/*
11+
* In a debug build, we'll assert(3) for aide in debugging. In release
12+
* builds, we will provide macros that will set an error message that
13+
* indicate a failure and return. Note that memory leaks can occur in
14+
* a release-mode assertion failure -- it is impractical to provide
15+
* safe clean up routines in these very extreme failures, but care
16+
* should be taken to not leak very large objects.
17+
*/
18+
19+
#if (defined(_DEBUG) || defined(GIT_ASSERT_HARD)) && GIT_ASSERT_HARD != 0
20+
# include <assert.h>
21+
22+
# define GIT_ASSERT(expr) assert(expr)
23+
# define GIT_ASSERT_ARG(expr) assert(expr)
24+
25+
# define GIT_ASSERT_WITH_RETVAL(expr, fail) assert(expr)
26+
# define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) assert(expr)
27+
#else
28+
29+
/** Internal consistency check to stop the function. */
30+
# define GIT_ASSERT(expr) GIT_ASSERT_WITH_RETVAL(expr, -1)
31+
32+
/**
33+
* Assert that a consumer-provided argument is valid, setting an
34+
* actionable error message and returning -1 if it is not.
35+
*/
36+
# define GIT_ASSERT_ARG(expr) GIT_ASSERT_ARG_WITH_RETVAL(expr, -1)
37+
38+
/** Internal consistency check to return the `fail` param on failure. */
39+
# define GIT_ASSERT_WITH_RETVAL(expr, fail) \
40+
GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", fail)
41+
42+
/**
43+
* Assert that a consumer-provided argument is valid, setting an
44+
* actionable error message and returning the `fail` param if not.
45+
*/
46+
# define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) \
47+
GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INVALID, "invalid argument", fail)
48+
49+
# define GIT_ASSERT__WITH_RETVAL(expr, code, msg, fail) do { \
50+
if (!(expr)) { \
51+
git_error_set(code, "%s: '%s'", msg, #expr); \
52+
return fail; \
53+
} \
54+
} while(0)
55+
56+
#endif /* GIT_ASSERT_HARD */
57+
58+
#endif

src/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include "errors.h"
8181
#include "thread-utils.h"
8282
#include "integer.h"
83+
#include "assert_safe.h"
8384

8485
/*
8586
* Include the declarations for deprecated functions; this ensures

tests/core/assert.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#ifdef GIT_ASSERT_HARD
2+
# undef GIT_ASSERT_HARD
3+
#endif
4+
5+
#define GIT_ASSERT_HARD 0
6+
7+
#include "clar_libgit2.h"
8+
9+
static const char *hello_world = "hello, world";
10+
static const char *fail = "FAIL";
11+
12+
static int dummy_fn(const char *myarg)
13+
{
14+
GIT_ASSERT_ARG(myarg);
15+
GIT_ASSERT_ARG(myarg != hello_world);
16+
return 0;
17+
}
18+
19+
static const char *fn_returns_string(const char *myarg)
20+
{
21+
GIT_ASSERT_ARG_WITH_RETVAL(myarg, fail);
22+
GIT_ASSERT_ARG_WITH_RETVAL(myarg != hello_world, fail);
23+
24+
return myarg;
25+
}
26+
27+
static int bad_math(void)
28+
{
29+
GIT_ASSERT(1 + 1 == 3);
30+
return 42;
31+
}
32+
33+
static const char *bad_returns_string(void)
34+
{
35+
GIT_ASSERT_WITH_RETVAL(1 + 1 == 3, NULL);
36+
return hello_world;
37+
}
38+
39+
void test_core_assert__argument(void)
40+
{
41+
cl_git_fail(dummy_fn(NULL));
42+
cl_assert(git_error_last());
43+
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
44+
cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message);
45+
46+
cl_git_fail(dummy_fn(hello_world));
47+
cl_assert(git_error_last());
48+
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
49+
cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
50+
51+
cl_git_pass(dummy_fn("foo"));
52+
}
53+
54+
void test_core_assert__argument_with_non_int_return_type(void)
55+
{
56+
const char *foo = "foo";
57+
58+
cl_assert_equal_p(fail, fn_returns_string(NULL));
59+
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
60+
cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message);
61+
62+
cl_assert_equal_p(fail, fn_returns_string(hello_world));
63+
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
64+
cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
65+
66+
cl_assert_equal_p(foo, fn_returns_string(foo));
67+
}
68+
69+
void test_core_assert__argument_with_void_return_type(void)
70+
{
71+
const char *foo = "foo";
72+
73+
git_error_clear();
74+
fn_returns_string(hello_world);
75+
cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
76+
cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
77+
78+
git_error_clear();
79+
cl_assert_equal_p(foo, fn_returns_string(foo));
80+
cl_assert_equal_p(NULL, git_error_last());
81+
}
82+
83+
void test_core_assert__internal(void)
84+
{
85+
cl_git_fail(bad_math());
86+
cl_assert(git_error_last());
87+
cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass);
88+
cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message);
89+
90+
cl_assert_equal_p(NULL, bad_returns_string());
91+
cl_assert(git_error_last());
92+
cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass);
93+
cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message);
94+
}

0 commit comments

Comments
 (0)