Skip to content

Commit

Permalink
Merge pull request #111 from clar-test/ethomson/invoke
Browse files Browse the repository at this point in the history
invoke: preserve line numbers during helper methods
  • Loading branch information
ethomson authored Jan 22, 2025
2 parents a23a73e + baa977e commit 1e421ff
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 25 deletions.
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,16 @@ suite.

- `cl_fixture(const char *)`: Gets the full path to a fixture file.

Please do note that these methods are *always* available whilst running a
test, even when calling auxiliary/static functions inside the same file.
### Auxiliary / helper functions

It's strongly encouraged to perform test assertions in auxiliary methods,
instead of returning error values. This is considered good Clar style.
The clar API is always available while running a test, even when calling
"auxiliary" (helper) functions.

You're encouraged to perform test assertions in those auxiliary
methods, instead of returning error values. This is considered good
Clar style. _However_, when you do this, you need to call `cl_invoke`
to preserve the current state; this ensures that failures are reported
as coming from the actual test, instead of the auxiliary method.
Style Example:
Expand Down Expand Up @@ -309,20 +314,19 @@ static void check_string(const char *str)
void test_example__a_test_with_auxiliary_methods(void)
{
check_string("foo");
check_string("bar");
cl_invoke(check_string("foo"));
cl_invoke(check_string("bar"));
}
~~~~
About Clar
==========
Clar has been written from scratch by [Vicent Martí](https://github.com/vmg),
to replace the old testing framework in [libgit2][libgit2].
Do you know what languages are *in* on the SF startup scene? Node.js *and*
Latin. Follow [@vmg](https://www.twitter.com/vmg) on Twitter to
receive more lessons on word etymology. You can be hip too.
Clar was originally written by [Vicent Martí](https://github.com/vmg),
to replace the old testing framework in [libgit2][libgit2]. It is
currently maintained by [Edward Thomson](https://github.com/ethomson),
and used by the [libgit2][libgit2] and [git][git] projects, amongst
others.
[libgit2]: https://github.com/libgit2/libgit2
[git]: https://github.com/git/git
29 changes: 26 additions & 3 deletions clar.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ static struct {
struct clar_report *reports;
struct clar_report *last_report;

const char *invoke_file;
const char *invoke_func;
size_t invoke_line;

void (*local_cleanup)(void *);
void *local_cleanup_payload;

Expand Down Expand Up @@ -328,6 +332,8 @@ clar_run_test(
if (_clar.local_cleanup != NULL)
_clar.local_cleanup(_clar.local_cleanup_payload);

clar__clear_invokepoint();

if (cleanup->ptr != NULL)
cleanup->ptr();

Expand Down Expand Up @@ -716,9 +722,9 @@ void clar__fail(

_clar.last_report->last_error = error;

error->file = file;
error->function = function;
error->line_number = line;
error->file = _clar.invoke_file ? _clar.invoke_file : file;
error->function = _clar.invoke_func ? _clar.invoke_func : function;
error->line_number = _clar.invoke_line ? _clar.invoke_line : line;
error->error_msg = error_msg;

if (description != NULL &&
Expand Down Expand Up @@ -872,6 +878,23 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
_clar.local_cleanup_payload = opaque;
}

void clar__set_invokepoint(
const char *file,
const char *func,
size_t line)
{
_clar.invoke_file = file;
_clar.invoke_func = func;
_clar.invoke_line = line;
}

void clar__clear_invokepoint(void)
{
_clar.invoke_file = NULL;
_clar.invoke_func = NULL;
_clar.invoke_line = 0;
}

#include "clar/sandbox.h"
#include "clar/fixtures.h"
#include "clar/fs.h"
Expand Down
21 changes: 21 additions & 0 deletions clar.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ void cl_fixture_cleanup(const char *fixture_name);
const char *cl_fixture_basename(const char *fixture_name);
#endif

/**
* Invoke a helper function, which itself will use `cl_assert`
* constructs. This will preserve the stack information of the
* current call point, so that function name and line number
* information is shown from the line of the test, instead of
* the helper function.
*/
#define cl_invoke(expr) \
do { \
clar__set_invokepoint(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE); \
expr; \
clar__clear_invokepoint(); \
} while(0)

/**
* Assertion macros with explicit error message
*/
Expand Down Expand Up @@ -180,4 +194,11 @@ void clar__assert_equal(
const char *fmt,
...);

void clar__set_invokepoint(
const char *file,
const char *func,
size_t line);

void clar__clear_invokepoint(void);

#endif
18 changes: 9 additions & 9 deletions test/selftest.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,51 +239,51 @@ static void run(const char *expected_output_file, int expected_error_code, ...)

void test_selftest__help(void)
{
run("help", 1, "-h", NULL);
cl_invoke(run("help", 1, "-h", NULL));
}

void test_selftest__without_arguments(void)
{
run("without_arguments", 8, NULL);
cl_invoke(run("without_arguments", 8, NULL));
}

void test_selftest__specific_test(void)
{
run("specific_test", 1, "-sselftest::suite::bool", NULL);
cl_invoke(run("specific_test", 1, "-sselftest::suite::bool", NULL));
}

void test_selftest__stop_on_failure(void)
{
run("stop_on_failure", 1, "-Q", NULL);
cl_invoke(run("stop_on_failure", 1, "-Q", NULL));
}

void test_selftest__quiet(void)
{
run("quiet", 8, "-q", NULL);
cl_invoke(run("quiet", 8, "-q", NULL));
}

void test_selftest__tap(void)
{
run("tap", 8, "-t", NULL);
cl_invoke(run("tap", 8, "-t", NULL));
}

void test_selftest__suite_names(void)
{
run("suite_names", 0, "-l", NULL);
cl_invoke(run("suite_names", 0, "-l", NULL));
}

void test_selftest__summary_without_filename(void)
{
struct stat st;
run("summary_without_filename", 8, "-r", NULL);
cl_invoke(run("summary_without_filename", 8, "-r", NULL));
/* The summary contains timestamps, so we cannot verify its contents. */
cl_must_pass(stat("summary.xml", &st));
}

void test_selftest__summary_with_filename(void)
{
struct stat st;
run("summary_with_filename", 8, "-rdifferent.xml", NULL);
cl_invoke(run("summary_with_filename", 8, "-rdifferent.xml", NULL));
/* The summary contains timestamps, so we cannot verify its contents. */
cl_must_pass(stat("different.xml", &st));
}

0 comments on commit 1e421ff

Please sign in to comment.