configure
dev.mk
perfdir.*
+test/main
+test/suites.h
testdir.*
version.c
libs = @LIBS@ -lm
-sources = \
+base_sources = \
ccache.c mdfour.c hash.c execute.c util.c args.c stats.c version.c \
cleanup.c snprintf.c unify.c manifest.c hashtable.c hashtable_itr.c \
- murmurhashneutral2.c hashutil.c getopt_long.c exitfn.c main.c
-all_sources = $(sources) @extra_sources@
+ murmurhashneutral2.c hashutil.c getopt_long.c exitfn.c
+base_objs = $(base_sources:.c=.o)
-headers = \
- ccache.h hashtable.h hashtable_itr.h hashtable_private.h hashutil.h \
- manifest.h mdfour.h murmurhashneutral2.h getopt_long.h
+ccache_sources = main.c $(base_sources) @extra_sources@
+ccache_objs = $(ccache_sources:.c=.o)
-objs = $(all_sources:.c=.o)
+test_suites = test/test_util.c
+test_sources = test/main.c test/framework.c $(test_suites)
+test_objs = $(test_sources:.c=.o)
+
+all_sources = $(ccache_sources) $(test_sources)
+all_objs = $(ccache_objs) $(test_objs)
generated_docs = ccache.1 INSTALL.html manual.html NEWS.html README.html
-files_to_clean = $(objs) ccache$(EXEEXT) *~
+files_to_clean = $(all_objs) ccache$(EXEEXT) test/main$(EXEEXT) *~
.PHONY: all
all: ccache$(EXEEXT)
.PHONY: docs
docs: $(generated_docs)
-ccache$(EXEEXT): $(objs)
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(objs) $(libs)
+ccache$(EXEEXT): $(ccache_objs)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(libs)
.PHONY: install
install: all
$(srcdir)/perf.py --ccache ccache$(EXEEXT) $(CC) $(CFLAGS) $(CPPFLAGS) $(srcdir)/ccache.c
.PHONY: test
-test: ccache$(EXEEXT)
+test: ccache$(EXEEXT) test/main$(EXEEXT)
+ test/main$(EXEEXT)
CC='$(CC)' $(srcdir)/test.sh
+test/main$(EXEEXT): $(base_objs) $(test_objs)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(libs)
+
+test/main.o: test/suites.h
+
+test/suites.h: $(test_suites)
+ sed -n 's/TEST_SUITE(\(.*\))/SUITE(\1)/p' $^ >$@
+
.PHONY: check
check: test
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(ctype.h pwd.h stdlib.h string.h strings.h sys/time.h)
+AC_CHECK_HEADERS(sys/ioctl.h termios.h)
AC_CHECK_FUNCS(asprintf)
AC_CHECK_FUNCS(gethostname)
echo '#endif' >>config.h.tmp
mv config.h.tmp config.h
-mkdir -p .deps
+mkdir -p .deps test
if test x$use_bundled_zlib = xyes; then
AC_MSG_WARN(using bundled zlib)
generated_docs = ccache.1 INSTALL.html manual.html NEWS.html README.html
built_dist_files = $(generated_docs)
+headers = \
+ ccache.h hashtable.h hashtable_itr.h hashtable_private.h hashutil.h \
+ manifest.h mdfour.h murmurhashneutral2.h getopt_long.h \
+ test/framework.h test/suites.h
+
files_to_clean += $(dist_archive_tar_bz2) $(dist_archive_tar_gz) .deps/*
-files_to_clean += $(built_dist_files) version.c
+files_to_clean += $(built_dist_files) version.c test/suites.h
files_to_clean += *.xml
source_dist_files = \
- $(sources) $(headers) zlib/*.c zlib/*.h \
+ main.c $(base_sources) $(test_sources) $(headers) zlib/*.c zlib/*.h \
config.h.in configure install-sh Makefile.in \
test.sh COPYING INSTALL.txt NEWS.txt README.txt
dist_files = \
mkdir -p $(dist_dir)/build && \
cd $(dist_dir)/build && \
../configure --prefix=$$tmpdir/root && \
+ $(MAKE) test && \
$(MAKE) install && \
$(MAKE) installcheck) && \
rm -rf $$tmpdir
--- /dev/null
+/*
+ * Copyright (C) 2010 Joel Rosdahl
+ *
+ * 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, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "framework.h"
+#include <stdio.h>
+#if defined(HAVE_TERMIOS_H) && defined(HAVE_SYS_IOCTL_H)
+#define USE_COLOR
+#include <termios.h>
+#include <sys/ioctl.h>
+#endif
+
+static unsigned passed_asserts;
+static unsigned failed_asserts;
+static unsigned passed_tests;
+static unsigned failed_tests;
+static unsigned passed_suites;
+static unsigned failed_suites;
+static unsigned failed_asserts_before_suite;
+static unsigned failed_asserts_before_test;
+static const char *current_suite;
+static const char *current_test;
+static int verbose;
+
+static const char COLOR_END[] = "\x1b[m";
+static const char COLOR_GREEN[] = "\x1b[32m";
+static const char COLOR_RED[] = "\x1b[31m";
+
+#define COLOR(tty, color) ((tty) ? COLOR_##color : "")
+
+static int
+is_tty(int fd)
+{
+#ifdef USE_COLOR
+ struct termios t;
+ return ioctl(fd, TCGETS, &t) == 0;
+#else
+ (void)fd;
+ return 0;
+#endif
+}
+
+int
+cct_run(suite_fn *suites, int verbose_output)
+{
+ suite_fn *suite;
+ int tty = is_tty(1);
+
+ verbose = verbose_output;
+
+ for (suite = suites; *suite; suite++) {
+ unsigned test_index = 0;
+ do {
+ test_index = (*suite)(test_index + 1);
+ } while (test_index != 0);
+ }
+
+ if (failed_asserts == 0) {
+ printf("%sPASSED%s: %u assertions, %u tests, %u suites\n",
+ COLOR(tty, GREEN), COLOR(tty, END),
+ passed_asserts, passed_tests, passed_suites);
+ } else {
+ printf("%sFAILED%s: %u assertions, %u tests, %u suites\n",
+ COLOR(tty, RED), COLOR(tty, END),
+ failed_asserts, failed_tests, failed_suites);
+ }
+ return failed_asserts > 0 ? 1 : 0;
+}
+
+void cct_suite_begin(const char *name)
+{
+ if (verbose) {
+ printf("Running suite %s...\n", name);
+ }
+ current_suite = name;
+ failed_asserts_before_suite = failed_asserts;
+ failed_asserts_before_test = failed_tests; /* For first cct_test_end(). */
+}
+
+void cct_suite_end()
+{
+ if (failed_asserts > failed_asserts_before_suite) {
+ ++failed_suites;
+ } else {
+ ++passed_suites;
+ }
+}
+
+void cct_test_begin(const char *name)
+{
+ if (verbose) {
+ printf("Running test %s...\n", name);
+ }
+ current_test = name;
+ failed_asserts_before_test = failed_asserts;
+}
+
+void cct_test_end()
+{
+ if (failed_asserts > failed_asserts_before_test) {
+ ++failed_tests;
+ } else {
+ ++passed_tests;
+ }
+}
+
+void
+cct_check_passed(void)
+{
+ ++passed_asserts;
+}
+
+void
+cct_check_failed(const char *file, int line, const char *what,
+ const char *expected, const char *actual)
+{
+ ++failed_asserts;
+ fprintf(stderr, "%s:%u: Failed assertion:\n", file, line);
+ fprintf(stderr, " Suite: %s\n", current_suite);
+ fprintf(stderr, " Test: %s\n", current_test);
+ if (expected && actual) {
+ fprintf(stderr, " Expression: %s\n", what);
+ fprintf(stderr, " Expected: %s\n", expected);
+ fprintf(stderr, " Actual: %s\n", actual);
+ } else {
+ fprintf(stderr, " Assertion: %s\n", what);
+ }
+ fprintf(stderr, "\n");
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Joel Rosdahl
+ *
+ * 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, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TEST_FRAMEWORK_H
+#define TEST_FRAMEWORK_H
+
+#include <string.h>
+#include <stdlib.h>
+
+/*****************************************************************************/
+
+#define TEST_SUITE(name) \
+ unsigned suite_##name(unsigned _start_point) \
+ { \
+ unsigned _test_counter = 0; \
+ cct_suite_begin(#name); \
+ { \
+ /* Empty due to macro trickery. */
+
+#define TEST(name) \
+ cct_test_end(); \
+ } \
+ ++_test_counter; \
+ if (_test_counter >= _start_point) { \
+ cct_test_begin(#name);
+
+#define TEST_SUITE_END \
+ cct_test_end(); \
+ } \
+ cct_suite_end(); \
+ return 0; /* We have reached the end. */ \
+ }
+
+/*****************************************************************************/
+
+#define CHECK(assertion) \
+ do { \
+ int ok = (assertion); \
+ if (ok) { \
+ cct_check_passed(); \
+ } else { \
+ cct_check_failed(__FILE__, __LINE__, #assertion, NULL, NULL); \
+ return _test_counter; \
+ } \
+ } while (0)
+
+#define CHECK_UNS_EQ(expected, actual) \
+ do { \
+ unsigned exp = (expected); \
+ unsigned act = (actual); \
+ if (exp == act) { \
+ cct_check_passed(); \
+ } else { \
+ char *exp_str, *act_str; \
+ x_asprintf(&exp_str, "%u", exp); \
+ x_asprintf(&act_str, "%u", act); \
+ cct_check_failed(__FILE__, __LINE__, #actual, exp_str, act_str); \
+ free(exp_str); \
+ free(act_str); \
+ return _test_counter; \
+ } \
+ } while (0)
+
+#define CHECK_INT_EQ(expected, actual) \
+ do { \
+ int exp = (expected); \
+ int act = (actual); \
+ if (exp == act) { \
+ cct_check_passed(); \
+ } else { \
+ char *exp_str, *act_str; \
+ x_asprintf(&exp_str, "%u", exp); \
+ x_asprintf(&act_str, "%u", act); \
+ cct_check_failed(__FILE__, __LINE__, #actual, exp_str, act_str); \
+ free(exp_str); \
+ free(act_str); \
+ return _test_counter; \
+ } \
+ } while (0)
+
+#define CHECK_STR_EQ_BASE(expected, actual, free1, free2) \
+ do { \
+ char *exp = (expected); \
+ char *act = (actual); \
+ if (exp && act && strcmp(act, exp) == 0) { \
+ cct_check_passed(); \
+ if (free1) { \
+ free(exp); \
+ } \
+ if (free2) { \
+ free(act); \
+ } \
+ } else { \
+ char *exp_str, *act_str; \
+ if (exp) { \
+ x_asprintf(&exp_str, "\"%s\"", exp); \
+ } else { \
+ exp_str = x_strdup("(null)"); \
+ } \
+ if (act) { \
+ x_asprintf(&act_str, "\"%s\"", act); \
+ } else { \
+ act_str = x_strdup("(null)"); \
+ } \
+ cct_check_failed(__FILE__, __LINE__, #actual, exp_str, act_str); \
+ free(exp_str); \
+ free(act_str); \
+ return _test_counter; \
+ } \
+ } while (0)
+
+#define CHECK_STR_EQ(expected, actual) \
+ CHECK_STR_EQ_BASE(expected, actual, 0, 0)
+
+#define CHECK_STR_EQ_FREE1(expected, actual) \
+ CHECK_STR_EQ_BASE(expected, actual, 1, 0)
+
+#define CHECK_STR_EQ_FREE2(expected, actual) \
+ CHECK_STR_EQ_BASE(expected, actual, 0, 1)
+
+#define CHECK_STR_EQ_FREE12(expected, actual) \
+ CHECK_STR_EQ_BASE(expected, actual, 1, 1)
+
+/*****************************************************************************/
+
+typedef unsigned (*suite_fn)(unsigned);
+int cct_run(suite_fn *suites, int verbose);
+
+void cct_suite_begin(const char *name);
+void cct_suite_end();
+void cct_test_begin(const char *name);
+void cct_test_end();
+void cct_check_passed(void);
+void cct_check_failed(const char *file, int line, const char *assertion,
+ const char *expected, const char *actual);
+
+#endif
--- /dev/null
+/* Mode: -*-c-*- */
+/*
+ * Copyright (C) 2010 Joel Rosdahl
+ *
+ * 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, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "framework.h"
+#include "../getopt_long.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define SUITE(name) unsigned suite_##name(unsigned);
+#include "suites.h"
+#undef SUITE
+
+const char USAGE_TEXT[] =
+ "Usage:\n"
+ " test [options]\n"
+ "\n"
+ "Options:\n"
+ " -h, --help print this help text\n"
+ " -v, --verbose enable verbose logging of tests\n";
+
+extern char *cache_logfile;
+
+int main(int argc, char **argv)
+{
+ static const struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"verbose", no_argument, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+ };
+ int verbose = 0;
+ int c;
+
+ while ((c = getopt_long(argc, argv, "hv", options, NULL)) != -1) {
+ switch (c) {
+ case 'h':
+ fprintf(stdout, USAGE_TEXT);
+ return 0;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ fprintf(stderr, USAGE_TEXT);
+ return 1;
+ }
+ }
+
+ suite_fn suites[] = {
+#define SUITE(name) &suite_##name,
+#include "suites.h"
+#undef SUITE
+ NULL
+ };
+
+ if (getenv("RUN_FROM_BUILD_FARM")) {
+ verbose = 1;
+ }
+ cache_logfile = getenv("CCACHE_LOGFILE");
+
+ return cct_run(suites, verbose);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Joel Rosdahl
+ *
+ * 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, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * This file contains tests for functions in util.c.
+ */
+
+#include "../ccache.h"
+#include "framework.h"
+
+TEST_SUITE(util)
+
+TEST(basename)
+{
+ CHECK_STR_EQ_FREE2("foo.c", basename("foo.c"));
+ CHECK_STR_EQ_FREE2("foo.c", basename("dir1/dir2/foo.c"));
+ CHECK_STR_EQ_FREE2("foo.c", basename("/dir/foo.c"));
+ CHECK_STR_EQ_FREE2("", basename("dir1/dir2/"));
+}
+
+TEST_SUITE_END