aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Golaszewski <bartekgola@gmail.com>2014-06-22 16:30:41 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2014-06-22 16:30:41 +0200
commit3ed81cf0529145d04299c4cd48b1aaab2fe36193 (patch)
treef8d40bf4c55c9dadba0773543048a5d69b695002
parent5d2e409ef8224dc32fde59702e8ec90b231441ed (diff)
downloadbusybox-w32-3ed81cf0529145d04299c4cd48b1aaab2fe36193.tar.gz
busybox-w32-3ed81cf0529145d04299c4cd48b1aaab2fe36193.tar.bz2
busybox-w32-3ed81cf0529145d04299c4cd48b1aaab2fe36193.zip
unit-tests: implement the unit-testing framework
This set of patches adds a simple unit-testing framework to Busybox unit-tests: add some helper macros for unit-test framework implementation unit-tests: implement the unit-testing framework unit-tests: add basic documentation on writing the unit test cases unit-tests: modify the Makefile 'test' target to run unit-tests too unit-tests: add two example test cases unit-tests: modify the existing strrstr test code to use the unit-test framework Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--Config.in8
-rw-r--r--Makefile.custom4
-rw-r--r--docs/unit-tests.txt50
-rw-r--r--include/libbb.h135
-rw-r--r--include/platform.h3
-rw-r--r--libbb/bbunit.c90
-rw-r--r--libbb/obscure.c38
-rw-r--r--libbb/strrstr.c19
8 files changed, 335 insertions, 12 deletions
diff --git a/Config.in b/Config.in
index b6eaea541..b83beb52d 100644
--- a/Config.in
+++ b/Config.in
@@ -675,6 +675,14 @@ config DEBUG_PESSIMIZE
675 in a much bigger executable that more closely matches the source 675 in a much bigger executable that more closely matches the source
676 code. 676 code.
677 677
678config UNIT_TEST
679 bool "Build unit tests"
680 default n
681 help
682 Say Y here if you want to build unit tests (both the framework and
683 test cases) as a Busybox applet. This results in bigger code, so you
684 probably don't want this option in production builds.
685
678config WERROR 686config WERROR
679 bool "Abort compilation on any warning" 687 bool "Abort compilation on any warning"
680 default n 688 default n
diff --git a/Makefile.custom b/Makefile.custom
index 8c95ef2d4..f8a12831d 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -55,7 +55,11 @@ endif
55# (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be)) 55# (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be))
56.PHONY: check 56.PHONY: check
57.PHONY: test 57.PHONY: test
58ifeq ($(CONFIG_UNIT_TEST),y)
59UNIT_CMD = ./busybox unit
60endif
58check test: busybox busybox.links 61check test: busybox busybox.links
62 $(UNIT_CMD)
59 test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree) 63 test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree)
60 bindir=$(objtree) srcdir=$(srctree)/testsuite \ 64 bindir=$(objtree) srcdir=$(srctree)/testsuite \
61 $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)" 65 $(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)"
diff --git a/docs/unit-tests.txt b/docs/unit-tests.txt
new file mode 100644
index 000000000..0fb522086
--- /dev/null
+++ b/docs/unit-tests.txt
@@ -0,0 +1,50 @@
1Busybox unit test framework
2===========================
3
4This document describes what you need to do to write test cases using the
5Busybox unit test framework.
6
7
8Building unit tests
9-------------------
10
11The framework and all tests are built as a regular Busybox applet if option
12CONFIG_UNIT_TEST (found in General Configuration -> Debugging Options) is set.
13
14
15Writing test cases
16------------------
17
18Unit testing interface can be found in include/bbunit.h.
19
20Tests can be placed in any .c file in Busybox tree - preferably right next to
21the functions they test. Test cases should be enclosed within an #if, and
22should start with BBUNIT_DEFINE_TEST macro and end with BBUNIT_ENDTEST within
23the test curly brackets. If an assertion fails the test ends immediately, ie.
24the following assertions will not be reached. Any code placed after
25BBUNIT_ENDTEST is executed regardless of the test result. Here's an example:
26
27#if ENABLE_UNIT_TEST
28
29BBUNIT_DEFINE_TEST(test_name)
30{
31 int *i;
32
33 i = malloc(sizeof(int));
34 BBUNIT_ASSERT_NOTNULL(i);
35 *i = 2;
36 BBUNIT_ASSERT_EQ((*i)*(*i), 4);
37
38 BBUNIT_ENDTEST;
39
40 free(i);
41}
42
43#endif /* ENABLE_UNIT_TEST */
44
45
46Running the unit test suite
47---------------------------
48
49To run the tests you can either directly run 'busybox unit' or use 'make test'
50to run both the unit tests (if compiled) and regular test suite.
diff --git a/include/libbb.h b/include/libbb.h
index 7a3610bb9..cede50cc2 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1941,6 +1941,141 @@ static ALWAYS_INLINE unsigned char bb_ascii_tolower(unsigned char a)
1941#define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20) 1941#define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20)
1942 1942
1943 1943
1944/* Simple unit-testing framework */
1945
1946typedef void (*bbunit_testfunc)(void);
1947
1948struct bbunit_listelem {
1949 struct bbunit_listelem* next;
1950 const char* name;
1951 bbunit_testfunc testfunc;
1952};
1953
1954void bbunit_registertest(struct bbunit_listelem* test);
1955void bbunit_settestfailed(void);
1956
1957#define BBUNIT_DEFINE_TEST(NAME) \
1958 static void bbunit_##NAME##_test(void); \
1959 static struct bbunit_listelem bbunit_##NAME##_elem = { \
1960 .name = #NAME, \
1961 .testfunc = bbunit_##NAME##_test, \
1962 }; \
1963 static void INIT_LAST bbunit_##NAME##_register(void) \
1964 { \
1965 bbunit_registertest(&bbunit_##NAME##_elem); \
1966 } \
1967 static void bbunit_##NAME##_test(void)
1968
1969/*
1970 * Both 'goto bbunit_end' and 'break' are here only to get rid
1971 * of compiler warnings.
1972 */
1973#define BBUNIT_ENDTEST \
1974 do { \
1975 goto bbunit_end; \
1976 bbunit_end: \
1977 break; \
1978 } while (0)
1979
1980#define BBUNIT_PRINTASSERTFAIL \
1981 do { \
1982 bb_error_msg( \
1983 "[ERROR] Assertion failed in file %s, line %d", \
1984 __FILE__, __LINE__); \
1985 } while (0)
1986
1987#define BBUNIT_ASSERTION_FAILED \
1988 do { \
1989 bbunit_settestfailed(); \
1990 goto bbunit_end; \
1991 } while (0)
1992
1993/*
1994 * Assertions.
1995 * For now we only offer assertions which cause tests to fail
1996 * immediately. In the future 'expects' might be added too -
1997 * similar to those offered by the gtest framework.
1998 */
1999#define BBUNIT_ASSERT_EQ(EXPECTED, ACTUAL) \
2000 do { \
2001 if ((EXPECTED) != (ACTUAL)) { \
2002 BBUNIT_PRINTASSERTFAIL; \
2003 bb_error_msg("[ERROR] '%s' isn't equal to '%s'", \
2004 #EXPECTED, #ACTUAL); \
2005 BBUNIT_ASSERTION_FAILED; \
2006 } \
2007 } while (0)
2008
2009#define BBUNIT_ASSERT_NOTEQ(EXPECTED, ACTUAL) \
2010 do { \
2011 if ((EXPECTED) == (ACTUAL)) { \
2012 BBUNIT_PRINTASSERTFAIL; \
2013 bb_error_msg("[ERROR] '%s' is equal to '%s'", \
2014 #EXPECTED, #ACTUAL); \
2015 BBUNIT_ASSERTION_FAILED; \
2016 } \
2017 } while (0)
2018
2019#define BBUNIT_ASSERT_NOTNULL(PTR) \
2020 do { \
2021 if ((PTR) == NULL) { \
2022 BBUNIT_PRINTASSERTFAIL; \
2023 bb_error_msg("[ERROR] '%s' is NULL!", #PTR); \
2024 BBUNIT_ASSERTION_FAILED; \
2025 } \
2026 } while (0)
2027
2028#define BBUNIT_ASSERT_NULL(PTR) \
2029 do { \
2030 if ((PTR) != NULL) { \
2031 BBUNIT_PRINTASSERTFAIL; \
2032 bb_error_msg("[ERROR] '%s' is not NULL!", #PTR); \
2033 BBUNIT_ASSERTION_FAILED; \
2034 } \
2035 } while (0)
2036
2037#define BBUNIT_ASSERT_FALSE(STATEMENT) \
2038 do { \
2039 if ((STATEMENT)) { \
2040 BBUNIT_PRINTASSERTFAIL; \
2041 bb_error_msg("[ERROR] Statement '%s' evaluated to true!", \
2042 #STATEMENT); \
2043 BBUNIT_ASSERTION_FAILED; \
2044 } \
2045 } while (0)
2046
2047#define BBUNIT_ASSERT_TRUE(STATEMENT) \
2048 do { \
2049 if (!(STATEMENT)) { \
2050 BBUNIT_PRINTASSERTFAIL; \
2051 bb_error_msg("[ERROR] Statement '%s' evaluated to false!", \
2052 #STATEMENT); \
2053 BBUNIT_ASSERTION_FAILED; \
2054 } \
2055 } while (0)
2056
2057#define BBUNIT_ASSERT_STREQ(STR1, STR2) \
2058 do { \
2059 if (strcmp(STR1, STR2) != 0) { \
2060 BBUNIT_PRINTASSERTFAIL; \
2061 bb_error_msg("[ERROR] Strings '%s' and '%s' " \
2062 "are not the same", STR1, STR2); \
2063 BBUNIT_ASSERTION_FAILED; \
2064 } \
2065 } while (0)
2066
2067#define BBUNIT_ASSERT_STRNOTEQ(STR1, STR2) \
2068 do { \
2069 if (strcmp(STR1, STR2) == 0) { \
2070 BBUNIT_PRINTASSERTFAIL; \
2071 bb_error_msg("[ERROR] Strings '%s' and '%s' " \
2072 "are the same, but were " \
2073 "expected to differ", STR1, STR2); \
2074 BBUNIT_ASSERTION_FAILED; \
2075 } \
2076 } while (0)
2077
2078
1944POP_SAVED_FUNCTION_VISIBILITY 2079POP_SAVED_FUNCTION_VISIBILITY
1945 2080
1946#endif 2081#endif
diff --git a/include/platform.h b/include/platform.h
index 92f775551..413c2224c 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -76,6 +76,9 @@
76# define UNUSED_PARAM_RESULT 76# define UNUSED_PARAM_RESULT
77#endif 77#endif
78 78
79/* used by unit test machinery to run registration functions */
80#define INIT_LAST __attribute__ ((constructor(2000)))
81
79/* -fwhole-program makes all symbols local. The attribute externally_visible 82/* -fwhole-program makes all symbols local. The attribute externally_visible
80 * forces a symbol global. */ 83 * forces a symbol global. */
81#if __GNUC_PREREQ(4,1) 84#if __GNUC_PREREQ(4,1)
diff --git a/libbb/bbunit.c b/libbb/bbunit.c
new file mode 100644
index 000000000..256014441
--- /dev/null
+++ b/libbb/bbunit.c
@@ -0,0 +1,90 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bbunit: Simple unit-testing framework for Busybox.
4 *
5 * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9
10//kbuild:lib-$(CONFIG_UNIT_TEST) += bbunit.o
11//applet:IF_UNIT_TEST(APPLET(unit, BB_DIR_USR_BIN, BB_SUID_DROP))
12
13//usage:#define unit_trivial_usage
14//usage: ""
15//usage:#define unit_full_usage "\n\n"
16//usage: "Run the unit-test suite"
17
18#include "libbb.h"
19
20#define WANT_TIMING 0
21
22static llist_t *tests = NULL;
23static unsigned tests_registered = 0;
24static int test_retval;
25
26void bbunit_registertest(struct bbunit_listelem *test)
27{
28 llist_add_to_end(&tests, test);
29 tests_registered++;
30}
31
32void bbunit_settestfailed(void)
33{
34 test_retval = -1;
35}
36
37#if WANT_TIMING
38static void timeval_diff(struct timeval* res,
39 const struct timeval* x,
40 const struct timeval* y)
41{
42 long udiff = x->tv_usec - y->tv_usec;
43
44 res->tv_sec = x->tv_sec - y->tv_sec - (udiff < 0);
45 res->tv_usec = (udiff >= 0 ? udiff : udiff + 1000000);
46}
47#endif
48
49int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) MAIN_EXTERNALLY_VISIBLE;
50int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
51{
52 unsigned tests_run = 0;
53 unsigned tests_failed = 0;
54#if WANT_TIMING
55 struct timeval begin;
56 struct timeval end;
57 struct timeval time_spent;
58 gettimeofday(&begin, NULL);
59#endif
60
61 bb_error_msg("Running %d test(s)...", tests_registered);
62 for (;;) {
63 struct bbunit_listelem* el = llist_pop(&tests);
64 if (!el)
65 break;
66 bb_error_msg("Case: [%s]", el->name);
67 test_retval = 0;
68 el->testfunc();
69 if (test_retval < 0) {
70 bb_error_msg("[ERROR] [%s]: TEST FAILED", el->name);
71 tests_failed++;
72 }
73 tests_run++;
74 el = el->next;
75 }
76
77#if WANT_TIMING
78 gettimeofday(&end, NULL);
79 timeval_diff(&time_spent, &end, &begin);
80 bb_error_msg("Elapsed time %u.%06u seconds"
81 (int)time_spent.tv_sec,
82 (int)time_spent.tv_usec);
83#endif
84 if (tests_failed > 0) {
85 bb_error_msg("[ERROR] %u test(s) FAILED", tests_failed);
86 return EXIT_FAILURE;
87 }
88 bb_error_msg("All tests passed");
89 return EXIT_SUCCESS;
90}
diff --git a/libbb/obscure.c b/libbb/obscure.c
index 24c4ac917..ad17d1ff1 100644
--- a/libbb/obscure.c
+++ b/libbb/obscure.c
@@ -182,3 +182,41 @@ int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *
182 } 182 }
183 return 0; 183 return 0;
184} 184}
185
186#if ENABLE_UNIT_TEST
187
188/* Test obscure_msg() instead of obscure() in order not to print anything. */
189
190static const struct passwd pw = {
191 .pw_name = (char *)"johndoe",
192 .pw_gecos = (char *)"John Doe",
193};
194
195BBUNIT_DEFINE_TEST(obscure_weak_pass)
196{
197 /* Empty password */
198 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "", &pw));
199 /* Pure numbers */
200 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "23577315", &pw));
201 /* Similar to pw_name */
202 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "johndoe123%", &pw));
203 /* Similar to pw_gecos, reversed */
204 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "eoD nhoJ^44@", &pw));
205 /* Similar to the old password */
206 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "d4#21?'S", &pw));
207 /* adjacent letters */
208 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "qwerty123", &pw));
209 /* Many similar chars */
210 BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "^33Daaaaaa1", &pw));
211
212 BBUNIT_ENDTEST;
213}
214
215BBUNIT_DEFINE_TEST(obscure_strong_pass)
216{
217 BBUNIT_ASSERT_NULL(obscure_msg("Rt4##2&:'|", "}(^#rrSX3S*22", &pw));
218
219 BBUNIT_ENDTEST;
220}
221
222#endif /* ENABLE_UNIT_TEST */
diff --git a/libbb/strrstr.c b/libbb/strrstr.c
index d8823fc51..93d970a1b 100644
--- a/libbb/strrstr.c
+++ b/libbb/strrstr.c
@@ -7,13 +7,7 @@
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9 9
10#ifdef __DO_STRRSTR_TEST
11#include <stdlib.h>
12#include <string.h>
13#include <stdio.h>
14#else
15#include "libbb.h" 10#include "libbb.h"
16#endif
17 11
18/* 12/*
19 * The strrstr() function finds the last occurrence of the substring needle 13 * The strrstr() function finds the last occurrence of the substring needle
@@ -34,8 +28,9 @@ char* FAST_FUNC strrstr(const char *haystack, const char *needle)
34 } 28 }
35} 29}
36 30
37#ifdef __DO_STRRSTR_TEST 31#if ENABLE_UNIT_TEST
38int main(int argc, char **argv) 32
33BBUNIT_DEFINE_TEST(strrstr)
39{ 34{
40 static const struct { 35 static const struct {
41 const char *h, *n; 36 const char *h, *n;
@@ -59,13 +54,13 @@ int main(int argc, char **argv)
59 i = 0; 54 i = 0;
60 while (i < sizeof(test_array) / sizeof(test_array[0])) { 55 while (i < sizeof(test_array) / sizeof(test_array[0])) {
61 const char *r = strrstr(test_array[i].h, test_array[i].n); 56 const char *r = strrstr(test_array[i].h, test_array[i].n);
62 printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r);
63 if (r == NULL) 57 if (r == NULL)
64 r = test_array[i].h - 1; 58 r = test_array[i].h - 1;
65 printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED"); 59 BBUNIT_ASSERT_EQ(r, test_array[i].h + test_array[i].pos);
66 i++; 60 i++;
67 } 61 }
68 62
69 return 0; 63 BBUNIT_ENDTEST;
70} 64}
71#endif 65
66#endif /* ENABLE_UNIT_TEST */