diff --git a/.gitignore b/.gitignore index f5e3dd788..a6a1a6fa1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /include/git/config.h *.o *.a +*.exe diff --git a/Makefile b/Makefile index 51fcd3cac..8c7af5e16 100644 --- a/Makefile +++ b/Makefile @@ -16,11 +16,19 @@ OBJS += src/os/$(OS).o HDRS += include/git/config.h HDRS += include/git/os/$(OS).h +GIT_LIB = libgit2.a -all:: libgit2.a +TEST_OBJ = $(patsubst %.c,%.o,\ + $(wildcard tests/t[0-9][0-9][0-9][0-9]-*.c)) +TEST_EXE = $(patsubst %.o,%.exe,$(TEST_OBJ)) +TEST_RUN = $(patsubst %.exe,%.run,$(TEST_EXE)) + +all:: $(GIT_LIB) clean: - rm -f libgit2.a src/*.o + rm -f $(GIT_LIB) + rm -f src/*.o + rm -f tests/*.o tests/*.exe rm -f include/git/config.h rm -rf apidocs @@ -28,6 +36,8 @@ apidocs: $(DOXYGEN) api.doxygen cp CONVENTIONS apidocs/ +test: $(TEST_RUN) + .c.o: $(CC) $(BASIC_CFLAGS) $(CFLAGS) -c $< -o $@ @@ -36,10 +46,28 @@ include/git/config.h: include/git/config.h.in mv $@+ $@ $(OBJS): $(HDRS) -libgit2.a: $(OBJS) - rm -f libgit2.a - $(AR) cr libgit2.a $(OBJS) +$(GIT_LIB): $(OBJS) + rm -f $(LIB) + $(AR) cr $(GIT_LIB) $(OBJS) + +T_HDR = tests/test_lib.h +T_LIB = tests/test_lib.o +T_MAIN_C = tests/test_main.c +T_MAIN_O = tests/test_main.o + +$(T_LIB): tests/test_lib.h $(HDRS) +$(TEST_EXE): $(T_LIB) $(T_HDR) $(T_MAIN_C) $(HDRS) $(GIT_LIB) + +tests/%.exe: tests/%.o + grep BEGIN_TEST $(patsubst %.o,%.c,$<) >tests/test_contents + $(CC) $(CFLAGS) -Iinclude -c $(T_MAIN_C) -o $(T_MAIN_O) + $(CC) -o $@ $(T_MAIN_O) $< $(T_LIB) -L. -lgit2 + rm -f $(T_MAIN_O) tests/test_contents + +$(TEST_RUN): $(TEST_EXE) + $< .PHONY: all .PHONY: clean +.PHONY: test $(TEST_RUN) .PHONY: apidocs diff --git a/include/git/common.h b/include/git/common.h index 45bb34841..98822557c 100644 --- a/include/git/common.h +++ b/include/git/common.h @@ -43,6 +43,20 @@ # define GIT_EXTERN(type) type #endif +/** Declare a function never returns to the caller. */ +#ifdef __GNUC__ +# define GIT_NORETURN __attribute__((__noreturn__)) +#else +# define GIT_NORETURN /* empty */ +#endif + +/** Declare a function's takes printf style arguments. */ +#ifdef __GNUC__ +# define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b))) +#else +# define GIT_FORMAT_PRINTF(a,b) /* empty */ +#endif + /** * @file git/common.h * @brief Git common platform definitions diff --git a/tests/t0000-oid.c b/tests/t0000-oid.c new file mode 100644 index 000000000..b0a3077f9 --- /dev/null +++ b/tests/t0000-oid.c @@ -0,0 +1,46 @@ +#include "test_lib.h" +#include + +BEGIN_TEST(empty_string) + git_oid out; + must_fail(git_oid_mkstr(&out, "")); +END_TEST + +BEGIN_TEST(invalid_string_moo) + git_oid out; + must_fail(git_oid_mkstr(&out, "moo")); +END_TEST + +BEGIN_TEST(invalid_string_16a67770b7d8d72317c4b775213c23a8bd74f5ez) + git_oid out; + must_fail(git_oid_mkstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez")); +END_TEST + +BEGIN_TEST(valid_string_16a67770b7d8d72317c4b775213c23a8bd74f5e0) + git_oid out; + unsigned char exp[] = { + 0x16, 0xa6, 0x77, 0x70, 0xb7, + 0xd8, 0xd7, 0x23, 0x17, 0xc4, + 0xb7, 0x75, 0x21, 0x3c, 0x23, + 0xa8, 0xbd, 0x74, 0xf5, 0xe0, + }; + + must_pass(git_oid_mkstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0")); + must_pass(memcmp(out.id, exp, sizeof(out.id))); + + must_pass(git_oid_mkstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0")); + must_pass(memcmp(out.id, exp, sizeof(out.id))); +END_TEST + +BEGIN_TEST(valid_raw) + git_oid out; + unsigned char exp[] = { + 0x16, 0xa6, 0x77, 0x70, 0xb7, + 0xd8, 0xd7, 0x23, 0x17, 0xc4, + 0xb7, 0x75, 0x21, 0x3c, 0x23, + 0xa8, 0xbd, 0x74, 0xf5, 0xe0, + }; + + git_oid_mkraw(&out, exp); + must_pass(memcmp(out.id, exp, sizeof(out.id))); +END_TEST diff --git a/tests/test_lib.c b/tests/test_lib.c new file mode 100644 index 000000000..06a1c29eb --- /dev/null +++ b/tests/test_lib.c @@ -0,0 +1,99 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include "test_lib.h" + +struct test_info +{ + struct test_info *next; + const char *test_name; + const char *file_name; + int line_no; +}; + +static int first_test = 1; +static struct test_info *current_test; + +static void show_test_result(const char *status) +{ + fprintf(stderr, "* %-6s %5d: %s\n", + status, + current_test->line_no, + current_test->test_name); +} + +void test_die(const char *fmt, ...) +{ + va_list p; + + if (current_test) + show_test_result("FAILED"); + + va_start(p, fmt); + vfprintf(stderr, fmt, p); + va_end(p); + fputc('\n', stderr); + fflush(stderr); + exit(128); +} + +void test_begin( + const char *test_name, + const char *file_name, + int line_no) +{ + struct test_info *i = malloc(sizeof(*i)); + if (!i) + test_die("cannot malloc memory"); + + i->test_name = test_name; + i->file_name = file_name; + i->line_no = line_no; + current_test = i; + + if (first_test) { + const char *name = strrchr(i->file_name, '/'); + if (name) + name = name + 1; + else + name = i->file_name; + fprintf(stderr, "*** %s ***\n", name); + first_test = 0; + } +} + +void test_end(void) +{ + if (!current_test) + test_die("BEGIN_TEST() not used before END_TEST"); + + show_test_result("ok"); + free(current_test); + current_test = NULL; +} diff --git a/tests/test_lib.h b/tests/test_lib.h new file mode 100644 index 000000000..7e7c39379 --- /dev/null +++ b/tests/test_lib.h @@ -0,0 +1,85 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +/** + * Declares a new test block starting, with the specified name. + * @param name C symbol to assign to this test's function. + */ +#define BEGIN_TEST(name) \ + void testfunc__##name (void) \ + { \ + test_begin(#name, __FILE__, __LINE__); \ + { + +/** Denote the end of a test. */ +#define END_TEST \ + } \ + test_end(); \ + } + +/* These are internal functions for BEGIN_TEST, END_TEST. */ +extern void test_begin(const char *, const char *, int); +extern void test_end(void); + +/** + * Abort the current test suite. + * + * This function terminates the current test suite + * and does not return to the caller. + * + * @param fmt printf style format string. + */ +extern void test_die(const char *fmt, ...) + GIT_NORETURN + GIT_FORMAT_PRINTF(1, 2); + +/** + * Evaluate a library function which must return success. + * + * The definition of "success" is the classical 0 return value. + * This macro makes the test suite fail if the expression evaluates + * to a non-zero result. It is suitable for testing most API + * functions in the library. + * + * @param expr the expression to evaluate, and test the result of. + */ +#define must_pass(expr) \ + if (expr) test_die("line %d: %s", __LINE__, #expr) + +/** + * Evaluate a library function which must return an error. + * + * The definition of "failure" is the classical non-0 return value. + * This macro makes the test suite fail if the expression evaluates + * to 0 (aka success). It is suitable for testing most API + * functions in the library. + * + * @param expr the expression to evaluate, and test the result of. + */ +#define must_fail(expr) \ + if (!(expr)) test_die("line %d: %s", __LINE__, #expr) diff --git a/tests/test_main.c b/tests/test_main.c new file mode 100644 index 000000000..a870abdd5 --- /dev/null +++ b/tests/test_main.c @@ -0,0 +1,39 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "test_lib.h" + +#undef BEGIN_TEST +#define BEGIN_TEST(name) extern void testfunc__##name(void); +#include "test_contents" + +int main(int argc, char **argv) +{ +#undef BEGIN_TEST +#define BEGIN_TEST(name) testfunc__##name(); +#include "test_contents" + + return 0; +}