Skip to content

Commit d0aaa9d

Browse files
committed
add benchmark mode
An application can provide _benchmarks_ instead of _tests_. Benchmarks can run multiple times, will calculate the times of each run, and some simple additional data (mean, min, max, etc). This information will be displayed and will optionally be emitted in the summary output. Test hosts can indicate that they're benchmarks (not tests) by setting the mode before parsing the arguments. This will switch the output and summary format types.
1 parent d75edb6 commit d0aaa9d

File tree

4 files changed

+337
-37
lines changed

4 files changed

+337
-37
lines changed

clar.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ struct clar_summary {
146146
};
147147

148148
static struct {
149+
enum cl_test_mode test_mode;
149150
enum cl_test_status test_status;
150151

151152
const char *active_suite;
@@ -159,6 +160,7 @@ static struct {
159160
int suites_ran;
160161

161162
enum cl_output_format output_format;
163+
enum cl_summary_format summary_format;
162164

163165
int report_errors_only;
164166
int exit_on_error;
@@ -639,6 +641,14 @@ clar_test_init(int argc, char **argv)
639641
{
640642
const char *summary_env;
641643

644+
if (_clar.test_mode == CL_TEST_BENCHMARK) {
645+
_clar.output_format = CL_OUTPUT_TIMING;
646+
_clar.summary_format = CL_SUMMARY_JSON;
647+
} else {
648+
_clar.output_format = CL_OUTPUT_CLAP;
649+
_clar.summary_format = CL_SUMMARY_JUNIT;
650+
}
651+
642652
if (argc > 1)
643653
clar_parse_args(argc, argv);
644654

@@ -665,6 +675,12 @@ clar_test_init(int argc, char **argv)
665675
clar_tempdir_init();
666676
}
667677

678+
void
679+
clar_test_set_mode(enum cl_test_mode mode)
680+
{
681+
_clar.test_mode = mode;
682+
}
683+
668684
int
669685
clar_test_run(void)
670686
{

clar.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
# define CLAR_CURRENT_FUNC "func"
2929
#endif
3030

31+
enum cl_test_mode {
32+
CL_TEST_STANDARD,
33+
CL_TEST_BENCHMARK,
34+
};
35+
3136
enum cl_test_status {
3237
CL_TEST_OK,
3338
CL_TEST_FAILURE,
@@ -38,10 +43,17 @@ enum cl_test_status {
3843
enum cl_output_format {
3944
CL_OUTPUT_CLAP,
4045
CL_OUTPUT_TAP,
46+
CL_OUTPUT_TIMING,
47+
};
48+
49+
enum cl_summary_format {
50+
CL_SUMMARY_JUNIT,
51+
CL_SUMMARY_JSON,
4152
};
4253

4354
/** Setup clar environment */
4455
void clar_test_init(int argc, char *argv[]);
56+
void clar_test_set_mode(enum cl_test_mode mode);
4557
int clar_test_run(void);
4658
void clar_test_shutdown(void);
4759

clar/print.h

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,115 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg)
194194
fflush(stdout);
195195
}
196196

197+
/* timings format: useful for benchmarks */
198+
199+
static void clar_print_timing_init(int test_count, int suite_count, const char *suite_names)
200+
{
201+
(void)test_count;
202+
(void)suite_count;
203+
(void)suite_names;
204+
205+
printf("Started benchmarks (mean time ± stddev / min time … max time):\n\n");
206+
}
207+
208+
static void clar_print_timing_shutdown(int test_count, int suite_count, int error_count)
209+
{
210+
(void)test_count;
211+
(void)suite_count;
212+
(void)error_count;
213+
}
214+
215+
static void clar_print_timing_error(int num, const struct clar_report *report, const struct clar_error *error)
216+
{
217+
(void)num;
218+
(void)report;
219+
(void)error;
220+
}
221+
222+
static void clar_print_timing_test_start(const char *suite_name, const char *test_name, int test_number)
223+
{
224+
(void)test_number;
225+
226+
printf("%s::%s: ", suite_name, test_name);
227+
fflush(stdout);
228+
}
229+
230+
static void clar_print_timing_time(double t)
231+
{
232+
static const char *units[] = { "sec", "ms", "μs", "ns" };
233+
static const int units_len = sizeof(units) / sizeof(units[0]);
234+
int unit = 0, exponent = 0, digits;
235+
236+
while (t < 1.0 && unit < units_len - 1) {
237+
t *= 1000.0;
238+
unit++;
239+
}
240+
241+
while (t > 0.0 && t < 1.0 && exponent < 10) {
242+
t *= 10.0;
243+
exponent++;
244+
}
245+
246+
digits = (t < 10.0) ? 3 : ((t < 100.0) ? 2 : 1);
247+
248+
printf("%.*f", digits, t);
249+
250+
if (exponent > 0)
251+
printf("e-%d", exponent);
252+
253+
printf(" %s", units[unit]);
254+
}
255+
256+
static void clar_print_timing_test_finish(const char *suite_name, const char *test_name, int test_number, const struct clar_report *report)
257+
{
258+
const struct clar_error *error = _clar.last_report->errors;
259+
260+
(void)suite_name;
261+
(void)test_name;
262+
(void)test_number;
263+
264+
switch(report->status) {
265+
case CL_TEST_OK:
266+
clar_print_timing_time(report->time_mean);
267+
268+
if (report->runs > 1) {
269+
printf(" ± ");
270+
clar_print_timing_time(report->time_stddev);
271+
272+
printf(" / range: ");
273+
clar_print_timing_time(report->time_min);
274+
printf(" … ");
275+
clar_print_timing_time(report->time_max);
276+
printf(" (%d runs)", report->runs);
277+
}
278+
279+
printf("\n");
280+
break;
281+
case CL_TEST_FAILURE:
282+
printf("failed: %s\n", error->error_msg);
283+
break;
284+
case CL_TEST_SKIP:
285+
case CL_TEST_NOTRUN:
286+
printf("skipped\n");
287+
break;
288+
}
289+
290+
fflush(stdout);
291+
}
292+
293+
static void clar_print_timing_suite_start(const char *suite_name, int suite_index)
294+
{
295+
if (_clar.verbosity == 1)
296+
printf("\n%s", suite_name);
297+
298+
(void)suite_index;
299+
}
300+
301+
static void clar_print_timing_onabort(const char *fmt, va_list arg)
302+
{
303+
vfprintf(stderr, fmt, arg);
304+
}
305+
197306
/* indirection between protocol output selection */
198307

199308
#define PRINT(FN, ...) do { \
@@ -204,6 +313,9 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg)
204313
case CL_OUTPUT_TAP: \
205314
clar_print_tap_##FN (__VA_ARGS__); \
206315
break; \
316+
case CL_OUTPUT_TIMING: \
317+
clar_print_timing_##FN (__VA_ARGS__); \
318+
break; \
207319
default: \
208320
abort(); \
209321
} \

0 commit comments

Comments
 (0)