From bb3a3a61fe7b971ad8ef326ec7e4d5903e6860ee Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Mon, 27 Jan 2014 15:15:43 -0800 Subject: Functional tests for C library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While gearing up to make an addition to the C library, I became concerned at the lack of test infrastructure for the project. The build system now integrates Check , a lightweight testing framework for C, and there are a few tests to ensure no serious regressions occur. You can run the tests with “make check”. --- test/Makefile.am | 41 +++++++++ test/check_ppamltracer.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++ test/tmpdir.c | 92 +++++++++++++++++++ test/tmpdir.h | 51 +++++++++++ test/trace_invalid.c | 70 +++++++++++++++ test/trace_invalid.h | 55 ++++++++++++ test/trace_valid.c | 76 ++++++++++++++++ test/trace_valid.h | 55 ++++++++++++ 8 files changed, 670 insertions(+) create mode 100644 test/Makefile.am create mode 100644 test/check_ppamltracer.c create mode 100644 test/tmpdir.c create mode 100644 test/tmpdir.h create mode 100644 test/trace_invalid.c create mode 100644 test/trace_invalid.h create mode 100644 test/trace_valid.c create mode 100644 test/trace_valid.h (limited to 'test') diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..b5e30d5 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,41 @@ +# Makefile.am -- automake script for ppamltracer tests +# Copyright (C) 2013 Galois, Inc. +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +# +# To contact Galois, complete the Web form at +# or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland, +# Oregon, 97204-1622. + +TESTS = check_ppamltracer +check_PROGRAMS = check_ppamltracer +check_ppamltracer_SOURCES = \ + check_ppamltracer.c \ + tmpdir.c \ + tmpdir.h \ + trace_invalid.c \ + trace_invalid.h \ + trace_valid.c \ + trace_valid.h +check_ppamltracer_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src \ + -std=c99 \ + -Wall \ + -Wextra \ + -pedantic \ + @check_CFLAGS@ +check_ppamltracer_LDADD = \ + $(top_builddir)/src/libppamltracer.la \ + @check_LIBS@ diff --git a/test/check_ppamltracer.c b/test/check_ppamltracer.c new file mode 100644 index 0000000..73a4631 --- /dev/null +++ b/test/check_ppamltracer.c @@ -0,0 +1,230 @@ +/* check_ppamltracer.c -- test program for ppamltracer + * Copyright (C) 2014 Galois, Inc. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define _POSIX_C_SOURCE 199309L + +#include +#include +#include + +#if HAVE_TIME_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif + +#include + +#include +#include + +#include "tmpdir.h" +#include "trace_invalid.h" +#include "trace_valid.h" + +#define ignore(x) ((void)(x)) + +#if (HAVE_FIXTURE_tmpdir && HAVE_FIXTURE_trace_invalid && HAVE_UNISTD_H) +# define HAVE_TEST_init_creates_trace + START_TEST(test_init_creates_trace) + { + char index_file[128]; + strcpy(index_file, trace_invalid_path()); + strcat(index_file, ".otf"); + ck_assert(access(index_file, F_OK) == 0); + } + END_TEST +#endif + +#if (HAVE_FIXTURE_tmpdir && HAVE_FIXTURE_trace_valid) +# define HAVE_TEST_simple_creates_valid_trace + START_TEST(test_simple_creates_valid_trace) + { + OTF_FileManager *const manager = OTF_FileManager_open(16); + ck_assert_msg(manager != NULL, otf_strerr); + OTF_Reader *const reader = + OTF_Reader_open(trace_valid_path(), manager); + ck_assert_msg(reader != NULL, otf_strerr); + ck_assert_msg(OTF_Reader_close(reader) == 1, otf_strerr); + OTF_FileManager_close(manager); + } + END_TEST + +# define HAVE_TEST_simple_defines_main + START_TEST(test_simple_defines_main) + { + OTF_FileManager *const manager = OTF_FileManager_open(16); + ck_assert_msg(manager != NULL, otf_strerr); + OTF_Reader *const reader = + OTF_Reader_open(trace_valid_path(), manager); + ck_assert_msg(reader != NULL, otf_strerr); + ck_assert_msg(OTF_Reader_close(reader) == 1, otf_strerr); + OTF_FileManager_close(manager); + } + END_TEST +#endif + +#if (HAVE_FIXTURE_tmpdir && HAVE_TIME_H) +# define HAVE_TEST_trace_accurate_timing + static uint64_t test_trace_accurate_timing_delta; + + static int test_trace_accurate_timing_handle_enter( + void *const userData, + const uint64_t time, + const uint32_t function, + const uint32_t process, + const uint32_t source) + { + ignore(userData); + ignore(function); + ignore(process); + ignore(source); + test_trace_accurate_timing_delta = time; + return OTF_RETURN_OK; + } + + static int test_trace_accurate_timing_handle_exit( + void *const userData, + const uint64_t time, + const uint32_t function, + const uint32_t process, + const uint32_t source) + { + ignore(userData); + ignore(function); + ignore(process); + ignore(source); + test_trace_accurate_timing_delta = + time - test_trace_accurate_timing_delta; + return OTF_RETURN_OK; + } + + START_TEST(test_trace_accurate_timing) + { + // Create the trace. + char trace_base[128]; + strcpy(trace_base, tmpdir_name()); + strcat(trace_base, "/trace"); + ppaml_tracer_t tracer; + ck_assert( + ppaml_tracer_init(&tracer, trace_base) == 0); + ppaml_phase_t phase; + ck_assert( + ppaml_phase_init(&tracer, &phase, "test phase") == 0); + // Time a single phase. + ck_assert(ppaml_phase_start(&phase) == 0); + const struct timespec hundred_ms = + { .tv_sec = 0, .tv_nsec = 100000000 }; + nanosleep(&hundred_ms, NULL); + ck_assert(ppaml_phase_stop(&phase) == 0); + // Clean up and close the trace. + ck_assert(ppaml_phase_done(&phase) == 0); + ck_assert(ppaml_tracer_done(&tracer) == 0); + /* Verify that the delta is about 100 ms (100,000 ticks of the + * tracer timer). */ + OTF_FileManager *const manager = OTF_FileManager_open(16); + ck_assert_msg(manager != NULL, otf_strerr); + OTF_Reader *const reader = + OTF_Reader_open(trace_base, manager); + ck_assert_msg(reader != NULL, otf_strerr); + OTF_HandlerArray *const handlers = OTF_HandlerArray_open(); + ck_assert_msg(handlers != NULL, otf_strerr); + ck_assert_msg( + OTF_HandlerArray_setHandler( + handlers, + (OTF_FunctionPointer *)test_trace_accurate_timing_handle_enter, + OTF_ENTER_RECORD) + == 1, + otf_strerr); + ck_assert_msg( + OTF_HandlerArray_setHandler( + handlers, + (OTF_FunctionPointer *)test_trace_accurate_timing_handle_exit, + OTF_LEAVE_RECORD) + == 1, + otf_strerr); + ck_assert_msg( + OTF_Reader_readEvents(reader, handlers) != OTF_READ_ERROR, + otf_strerr); + /* Require less than +/- 10% to pass. Yes, this is an + * arbitrary metric, but it should trigger if there's a serious + * regression. */ + ck_assert_msg( + 90000 <= test_trace_accurate_timing_delta, + "timed interval (%d us) is more than 10%% fast", + test_trace_accurate_timing_delta); + ck_assert_msg(test_trace_accurate_timing_delta < 110000, + "timed interval (%d us) is more than 10%% slow", + test_trace_accurate_timing_delta); + // All done. + ck_assert_msg( + OTF_HandlerArray_close(handlers) == 1, otf_strerr); + ck_assert_msg(OTF_Reader_close(reader) == 1, otf_strerr); + OTF_FileManager_close(manager); + } + END_TEST +#endif + +Suite *ppamltracer_suite() +{ + Suite *const s = suite_create("ppamltracer"); + // Test case: init + TCase *tc = tcase_create("init"); + tmpdir_add_checked(tc); + trace_invalid_add_checked(tc); +# ifdef HAVE_TEST_init_creates_trace + tcase_add_test(tc, test_init_creates_trace); +# endif + suite_add_tcase(s, tc); + // Test case: valid + tc = tcase_create("valid"); + tmpdir_add_checked(tc); + trace_valid_add_checked(tc); +# ifdef HAVE_TEST_simple_creates_valid_trace + tcase_add_test(tc, test_simple_creates_valid_trace); +# endif +# ifdef HAVE_TEST_simple_defines_main + tcase_add_test(tc, test_simple_defines_main); +# endif + suite_add_tcase(s, tc); + // Test case: timing-accurate + tc = tcase_create("accurate"); + tmpdir_add_checked(tc); +# ifdef HAVE_TEST_trace_accurate_timing + tcase_add_test(tc, test_trace_accurate_timing); +# endif + suite_add_tcase(s, tc); + return s; +} + +int main() +{ + Suite *const s = ppamltracer_suite(); + SRunner *const runner = srunner_create(s); + srunner_run_all(runner, CK_NORMAL); + const int n_failed = srunner_ntests_failed(runner); + srunner_free(runner); + return (n_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/test/tmpdir.c b/test/tmpdir.c new file mode 100644 index 0000000..5d0c4d0 --- /dev/null +++ b/test/tmpdir.c @@ -0,0 +1,92 @@ +/* tmpdir.c -- fixture to create a temporary directory + * Copyright (C) 2010 caf + * Copyright (C) 2014 Galois, Inc. + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this library. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define _POSIX_C_SOURCE 200809L // mkdtemp(3) + +/* This is a bit out of order, but it needs to go here, before *any* includes + * are done, so that they can catch _XOPEN_SOURCE. */ +#if HAVE_FTW_H +# define _XOPEN_SOURCE 500 +# include +#endif + +#include +#include +#include +#include + +#if HAVE_SYS_STAT_H +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#include + +#include "tmpdir.h" + +#define ignore(x) ((void)(x)) + +#if (HAVE_STRUCT_FTW && HAVE_MKDTEMP && HAVE_NFTW) + static int unlink_cb( + const char *fpath, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) + { + ignore(sb); + ignore(typeflag); + ignore(ftwbuf); + return remove(fpath); + } + + static inline int rm_rf(char *path) + { + return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); + } + + static char tmpdir_buf[64]; + + void tmpdir_make() { + const char template[] = "scratch.XXXXXX"; + strcpy(tmpdir_buf, template); + ck_assert(mkdtemp(tmpdir_buf) != NULL); + } + + const char *tmpdir_name() { + if (strlen(tmpdir_buf) == 0) { + return NULL; + } else { + return tmpdir_buf; + } + } + + void tmpdir_clean() { + rm_rf(tmpdir_buf); + tmpdir_buf[0] = '\0'; + } +#endif diff --git a/test/tmpdir.h b/test/tmpdir.h new file mode 100644 index 0000000..43db62f --- /dev/null +++ b/test/tmpdir.h @@ -0,0 +1,51 @@ +/* tmpdir.h -- fixture to create a temporary directory + * Copyright (C) 2014 Galois, Inc. + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this library. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. + * + * + * This fixture creates and cleans a temporary subdirectory of the current + * directory. While the subdirectory is active, you can call 'tmpdir_name' to + * get its name. */ + +#ifndef PPAML_TRACER_TEST_TMPDIR_H +#define PPAML_TRACER_TEST_TMPDIR_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#define HAVE_FIXTURE_tmpdir (HAVE_STRUCT_FTW && HAVE_MKDTEMP && HAVE_NFTW) + +#if HAVE_FIXTURE_tmpdir + void tmpdir_make(); + void tmpdir_clean(); + + /* Returns the name of the currently-active temporary directory. If no + * directory is active, returns NULL. */ + const char *tmpdir_name(); + + // Registers the fixture. + inline void tmpdir_add_checked(TCase *const tc) { + tcase_add_checked_fixture(tc, tmpdir_make, tmpdir_clean); + } +#endif + +#endif diff --git a/test/trace_invalid.c b/test/trace_invalid.c new file mode 100644 index 0000000..cc93a03 --- /dev/null +++ b/test/trace_invalid.c @@ -0,0 +1,70 @@ +/* trace_invalid.c -- fixture to create an invalid OTF trace + * Copyright (C) 2014 Galois, Inc. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include + +#include "trace_invalid.h" +#include "tmpdir.h" + +#if HAVE_FIXTURE_tmpdir + + static const char name[] = "trace"; + static bool active = false; + + void trace_invalid_make() + { + active = true; + ppaml_tracer_t tracer; + ck_assert( + ppaml_tracer_init(&tracer, trace_invalid_path()) == 0); + ck_assert(ppaml_tracer_done(&tracer) == 0); + } + + const char *trace_invalid_path() + { + static char path[128]; + if (active) { + strcpy(path, tmpdir_name()); + strcat(path, "/"); + strcat(path, name); + return path; + } else { + return NULL; + } + + } + + void trace_invalid_clean() + { + active = false; + } + +#endif diff --git a/test/trace_invalid.h b/test/trace_invalid.h new file mode 100644 index 0000000..73881e1 --- /dev/null +++ b/test/trace_invalid.h @@ -0,0 +1,55 @@ +/* trace_invalid.h -- fixture to create an invalid trace + * Copyright (C) 2014 Galois, Inc. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. + * + * + * This fixture creates an invalid OTF trace in the currently-active temporary + * directory. It does not remove it, as it will be removed when the temporary + * directory is cleaned. + * + * Runtime dependency: tmpdir */ + +#ifndef PPAML_TRACER_TEST_TRACE_INVALID_H +#define PPAML_TRACER_TEST_TRACE_INVALID_H + +#include "tmpdir.h" + +#define HAVE_FIXTURE_trace_invalid HAVE_FIXTURE_tmpdir + +#if HAVE_FIXTURE_trace_invalid + void trace_invalid_make(); + + /* Despite its name, this function does not actually clean anything. + * It's provided to make it easier to interface with Check. */ + void trace_invalid_clean(); + + /* Returns the path to the trace. If no trace is active, returns + * NULL. */ + const char *trace_invalid_path(); + + // Registers the fixture. + inline void trace_invalid_add_checked(TCase *const tc) { + tcase_add_checked_fixture( + tc, + trace_invalid_make, + trace_invalid_clean); + } +#endif + +#endif diff --git a/test/trace_valid.c b/test/trace_valid.c new file mode 100644 index 0000000..8c93980 --- /dev/null +++ b/test/trace_valid.c @@ -0,0 +1,76 @@ +/* trace_valid.c -- fixture to create an valid OTF trace + * Copyright (C) 2014 Galois, Inc. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include + +#include "trace_valid.h" +#include "tmpdir.h" + +#if HAVE_FIXTURE_tmpdir + + static const char name[] = "trace"; + static bool active = false; + + void trace_valid_make() + { + active = true; + ppaml_tracer_t tracer; + ck_assert( + ppaml_tracer_init(&tracer, trace_valid_path()) == 0); + ppaml_phase_t phase; + ck_assert( + ppaml_phase_init(&tracer, &phase, "test phase") == 0); + ck_assert(ppaml_phase_start(&phase) == 0); + ck_assert(ppaml_phase_stop(&phase) == 0); + ck_assert(ppaml_phase_done(&phase) == 0); + ck_assert(ppaml_tracer_done(&tracer) == 0); + } + + const char *trace_valid_path() + { + static char path[128]; + if (active) { + strcpy(path, tmpdir_name()); + strcat(path, "/"); + strcat(path, name); + return path; + } else { + return NULL; + } + + } + + void trace_valid_clean() + { + active = false; + } + +#endif diff --git a/test/trace_valid.h b/test/trace_valid.h new file mode 100644 index 0000000..32e49ae --- /dev/null +++ b/test/trace_valid.h @@ -0,0 +1,55 @@ +/* trace_valid.h -- fixture to create an valid trace + * Copyright (C) 2014 Galois, Inc. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * To contact Galois, complete the Web form at + * or write to Galois, Inc., 421 Southwest + * 6th Avenue, Suite 300, Portland, Oregon, 97204-1622. + * + * + * This fixture creates an valid OTF trace in the currently-active temporary + * directory. It does not remove it, as it will be removed when the temporary + * directory is cleaned. + * + * Runtime dependency: tmpdir */ + +#ifndef PPAML_TRACER_TEST_TRACE_VALID_H +#define PPAML_TRACER_TEST_TRACE_VALID_H + +#include "tmpdir.h" + +#define HAVE_FIXTURE_trace_valid HAVE_FIXTURE_tmpdir + +#if HAVE_FIXTURE_trace_valid + void trace_valid_make(); + + /* Despite its name, this function does not actually clean anything. + * It's provided to make it easier to interface with Check. */ + void trace_valid_clean(); + + /* Returns the path to the trace. If no trace is active, returns + * NULL. */ + const char *trace_valid_path(); + + // Registers the fixture. + inline void trace_valid_add_checked(TCase *const tc) { + tcase_add_checked_fixture( + tc, + trace_valid_make, + trace_valid_clean); + } +#endif + +#endif -- cgit v1.2.3