All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH] Basic unit testing support for GRUB
@ 2009-11-13 17:45 BVK
  2009-11-16 21:21 ` Robert Millan
  2009-11-25 16:34 ` Felix Zielcke
  0 siblings, 2 replies; 7+ messages in thread
From: BVK @ 2009-11-13 17:45 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 577 bytes --]

Hi,


Attached is the (experimental branch) patch for unit testing that I
have in mind for GRUB.  Please let me know your comments.

It provides "make check" target which would build and execute test
programs from "tests" directory.  We can write small programs to test
specific functions in GRUB.  For example, attached patch has sample
program comparing grub_sprintf behavior against standard sprintf
output (from glibc).

Note that it currently has few gotchas, like using TARGET_CC instead
of BUILD_CC, no support for XPASS, XFAIL tests, etc.



regards,
-- 
bvk.chaitanya

[-- Attachment #2: unit-testing-support.patch --]
[-- Type: application/octet-stream, Size: 6127 bytes --]

=== modified file 'Makefile.in'
--- old/Makefile.in	2009-11-09 22:48:53 +0000
+++ new/Makefile.in	2009-11-13 17:03:17 +0000
@@ -125,6 +125,7 @@
 SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) $(grub-mkconfig_SCRIPTS) \
 	$(lib_SCRIPTS)
 INFOS = $(info_INFOS)
+TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
 
 CLEANFILES =
 MOSTLYCLEANFILES =
@@ -151,6 +152,9 @@
 include $(srcdir)/conf/$(target_cpu)-$(platform).mk
 endif
 
+# for tests
+include $(srcdir)/conf/tests.mk
+
 # For external modules.
 -include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk)
 
@@ -428,7 +432,15 @@
 	@echo "$(distdir).tar.gz is ready for distribution" | \
 	  sed 'h;s/./=/g;p;x;p;x'
 
-check:
+check: $(TESTS)
+	@list='$(TESTS)'; \
+	for file in $$list; do \
+	  if $(builddir)/$$file; then \
+	    echo "$$file: PASS"; \
+	  else \
+	    echo "$$file: FAIL"; \
+	  fi; \
+	done
 
 .SUFFIX:
 .SUFFIX: .c .o .S .d

=== added file 'conf/tests.rmk'
--- old/conf/tests.rmk	1970-01-01 00:00:00 +0000
+++ new/conf/tests.rmk	2009-11-13 17:31:12 +0000
@@ -0,0 +1,6 @@
+# -*- makefile -*-
+
+check_PROGRAMS += grub_sprintf
+grub_sprintf_SOURCES = tests/grub_sprintf.c tests/misc.c kern/misc.c
+grub_sprintf_CFLAGS = -Wno-error -Wno-format
+

=== added directory 'tests'
=== added file 'tests/grub_sprintf.c'
--- old/tests/grub_sprintf.c	1970-01-01 00:00:00 +0000
+++ new/tests/grub_sprintf.c	2009-11-13 17:34:00 +0000
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grub/misc.h>
+#include "test.h"
+
+#define TEST(type,fmt,val)				\
+  do {							\
+    int r1, r2;						\
+    char b1[1024];					\
+    char b2[1024];					\
+							\
+    b1[0] = b2[0] = '\0';				\
+    r1 = sprintf(b1, fmt, val);				\
+    r2 = grub_sprintf(b2, fmt, val);			\
+							\
+    if ((strcmp(b1, b2) != 0) || (r1 != r2)) {		\
+      printf("for (\"%s\","type") "			\
+	     "result should be (\"%s\",%d) "		\
+	     "but got (\"%s\",%d)\n",			\
+	     fmt, val, b1, r1, b2, r2);			\
+      exit(1);						\
+    }							\
+							\
+  } while (0)
+
+#define D(fmt,v) TEST("%20d",fmt,v)
+#define U(fmt,v) TEST("%20u",fmt,v)
+#define x(fmt,v) TEST("%20x",fmt,v)
+#define X(fmt,v) TEST("%20X",fmt,v)
+#define P(fmt,v) TEST("%20p",fmt,v)
+#define S(fmt,s) TEST("%20s",fmt,s)
+
+static void
+sprintf_checks(void)
+{
+  D("%d",   -1); D("%d",   0); D("%d", 1); 
+  D("%5d",  -1); D("%5d",  0); D("%5d", 1);
+  D("%-5d", -1); D("%-5d", 0); D("%-5d", 1);
+  D("%.5d", -1); D("%.5d", 0); D("%.5d", 1);
+  D("%5.0d", -1); D("%5.0d", 0); D("%5.0d", 1);
+  D("%-5.0d", -1); D("%-5.0d", 0); D("%-5.0d", 1);
+
+  U("%d",   -1); U("%d",   0); U("%d", 1); 
+  U("%5d",  -1); U("%5d",  0); U("%5d", 1);
+  U("%-5d", -1); U("%-5d", 0); U("%-5d", 1);
+  U("%.5d", -1); U("%.5d", 0); U("%.5d", 1);
+  U("%5.0d", -1); U("%5.0d", 0); U("%5.0d", 1);
+  U("%-5.0d", -1); U("%-5.0d", 0); U("%-5.0d", 1);
+
+  x("%d",   -1); x("%d",   0); x("%d", 1); 
+  x("%5d",  -1); x("%5d",  0); x("%5d", 1);
+  x("%-5d", -1); x("%-5d", 0); x("%-5d", 1);
+  x("%.5d", -1); x("%.5d", 0); x("%.5d", 1);
+  x("%5.0d", -1); x("%5.0d", 0); x("%5.0d", 1);
+  x("%-5.0d", -1); x("%-5.0d", 0); x("%-5.0d", 1);
+
+  X("%d",   -1); X("%d",   0); X("%d", 1); 
+  X("%5d",  -1); X("%5d",  0); X("%5d", 1);
+  X("%-5d", -1); X("%-5d", 0); X("%-5d", 1);
+  X("%.5d", -1); X("%.5d", 0); X("%.5d", 1);
+  X("%5.0d", -1); X("%5.0d", 0); X("%5.0d", 1);
+  X("%-5.0d", -1); X("%-5.0d", 0); X("%-5.0d", 1);
+
+  P("%p", NULL);
+  P("%p", sprintf_checks);
+
+  S("%s", (char*)NULL);
+  S("%s", "abcd");
+  S("%10s", "abcd");
+  S("%10.5s", "abcdefgh");
+  S("%10.5s", "ab");
+  S("%2.5s", "a");
+  S("%2.5s", "abcdefgh");
+
+  D("%4.2d",1);
+  D("%4.2d",12);
+  D("%4.2d",123);
+  D("%4.2d",1234);
+  D("%4.2d",12345);
+  D("%3.3d",12);
+  D("%3.3d",123);
+  D("%3.3d",1234);
+  D("%2.4d",12345);
+  D("%2.4d",1234);
+  D("%2.4d",123);
+  D("%2.4d",12);
+  D("%2.4d",1);
+  D("%.0d",0);
+  D("%.0d",1);
+
+  S("%4.2s","1");
+  S("%4.2s","12");
+  S("%4.2s","123");
+  S("%4.2s","1234");
+  S("%4.2s","12345");
+  S("%3.3s","12");
+  S("%3.3s","123");
+  S("%3.3s","1234");
+  S("%2.4s","12345");
+  S("%2.4s","1234");
+  S("%2.4s","123");
+  S("%2.4s","12");
+  S("%2.4s","1");
+
+  // add few more here, if necessary
+}
+
+int
+main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
+{
+  sprintf_checks();
+  return 0;
+}

=== added file 'tests/misc.c'
--- old/tests/misc.c	1970-01-01 00:00:00 +0000
+++ new/tests/misc.c	2009-11-13 17:00:55 +0000
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "test.h"
+
+char *
+grub_env_get (const char *name __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_err_t
+grub_error (grub_err_t n, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+
+  return n;
+}
+
+void *
+grub_malloc (grub_size_t size)
+{
+  return malloc (size);
+}
+
+void
+grub_refresh (void)
+{
+  fflush (stdout);
+}
+
+void
+grub_putchar (int c)
+{
+  putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+  return -1;
+}
+
+void
+grub_exit (void)
+{
+  exit (1);
+}
+
+struct grub_handler_class grub_term_input_class;
+struct grub_handler_class grub_term_output_class;

=== added file 'tests/test.h'
--- old/tests/test.h	1970-01-01 00:00:00 +0000
+++ new/tests/test.h	2009-11-13 17:35:28 +0000
@@ -0,0 +1,22 @@
+#ifndef GRUB_TEST_HEADER
+#define GRUB_TEST_HEADER
+
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/handler.h>
+
+// Below functions are necessary for successfully linking tests.  We
+// define dummy versions to keep the linker from complaining.
+
+char *     grub_env_get (const char *name __attribute__ ((unused)));
+grub_err_t grub_error   (grub_err_t n, const char *fmt, ...);
+void *     grub_malloc  (grub_size_t size);
+void       grub_refresh (void);
+void       grub_putchar (int c);
+int        grub_getkey  (void);
+void       grub_exit    (void);
+
+extern struct grub_handler_class grub_term_input_class;
+extern struct grub_handler_class grub_term_output_class;
+
+#endif /* ! GRUB_TEST_HEADER */


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-11-13 17:45 [RFC][PATCH] Basic unit testing support for GRUB BVK
@ 2009-11-16 21:21 ` Robert Millan
  2009-11-23 15:16   ` BVK
  2009-11-25 16:34 ` Felix Zielcke
  1 sibling, 1 reply; 7+ messages in thread
From: Robert Millan @ 2009-11-16 21:21 UTC (permalink / raw)
  To: The development of GNU GRUB

On Fri, Nov 13, 2009 at 11:15:41PM +0530, BVK wrote:
> Hi,
> 
> 
> Attached is the (experimental branch) patch for unit testing that I
> have in mind for GRUB.  Please let me know your comments.
> 
> It provides "make check" target which would build and execute test
> programs from "tests" directory.  We can write small programs to test
> specific functions in GRUB.  For example, attached patch has sample
> program comparing grub_sprintf behavior against standard sprintf
> output (from glibc).
> 
> Note that it currently has few gotchas, like using TARGET_CC instead
> of BUILD_CC, no support for XPASS, XFAIL tests, etc.

Some parts don't follow our coding style;  please check with indent(1)
utility, it should produce the right indentation.  Also, we use C-style
/**/ syntax for comments.

Ah, and could you include a ChangeLog entry?

Thank you!

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-11-16 21:21 ` Robert Millan
@ 2009-11-23 15:16   ` BVK
  2009-11-23 15:54     ` BVK
  0 siblings, 1 reply; 7+ messages in thread
From: BVK @ 2009-11-23 15:16 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 2090 bytes --]

Hi,


I have been busy last week, so I couldn't reply :-(

But I am able to complete most of the framework.  It has support for
logging multiple testcase failures and reporting them at the end of
test execution.  Please go through the attached patch and let me know
your suggestions.

Attached framework has support for two kinds of tests.

1. Unit Tests
2. Functional Tests

Unit tests are test programs that doesn't need an active GRUB instance
for their operation. These programs target specific code of GRUB, like
grub_printf logic, etc.  These are run during the build time by "make
check" command.  Since these are normal programs, they can link with
standard C library and others.

Functional tests are for testing GRUB functionality that needs an
active instance of GRUB (for example, testing serial console code,
partition tables logic, biosdisk access, etc.).  Every functional test
is a GRUB module that registers itself when its loaded.  These are run
using special GRUB command "functional_test" in GRUB shell.

Attached patch has example tests for both. An additional grub_sprintf
unit test is also available which tests grub_sprintf function logic
and gets executed as part of "make check".

Since functional tests are grub modules, we either need to boot into
GRUB to run them or can make use of i386-qemu port of GRUB.  Below is
how I currently tested functional tests using i386-port


$ ./autogen.sh
$ ./configure --prefix=/usr --with-platform=qemu
$ make && make check
$ ./grub-mkimage -d . -o grub.bios at_keyboard normal help halt
functional_test example_functional_test
$ qemu -bios ./grub.bios -hda /dev/zero

rescue:grub> functional_test example_functional_test
...
rescue:grub> halt


As for the future framework related enhancements, I am thinking of
developing a grub-test script which would run functional tests
directly inside a qemu using serial console -- as part of the build
process.  Then we can have test scripts using grub-test as its
interpreter, which would remove the difference between functional
tests and unit tests.




thanks,
-- 
bvk.chaitanya

[-- Attachment #2: testing-framework.patch --]
[-- Type: application/octet-stream, Size: 17560 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2009-11-09 21:58:22 +0000
+++ ChangeLog	2009-11-23 14:24:29 +0000
@@ -1,3 +1,16 @@
+2009-11-23  BVK Chaitanya  <bvk.groups@gmail.com>
+
+	Regression testing framework for GRUB.
+
+	* Makefile.in: Added rules for make check target.
+	* include/grub/test.h: New file.  Header for testing framework.
+	* tests/test.c: Common definitions for the framework.
+	* tests/functional_test.c: Definitions for functional tests.
+	* tests/unit_test.c: Definitions for unit tests.
+	* tests/example_unit_test.c: Example test.
+	* tests/example_functional_test.c: Example test.
+	* tests/grub_sprintf.c: Unit test for grub_sprintf.
+
 2009-11-09  Robert Millan  <rmh.grub@aybabtu.com>
 
 	Import from Gnulib.

=== modified file 'Makefile.in'
--- Makefile.in	2009-11-09 22:48:53 +0000
+++ Makefile.in	2009-11-23 14:33:29 +0000
@@ -125,6 +125,7 @@
 SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) $(grub-mkconfig_SCRIPTS) \
 	$(lib_SCRIPTS)
 INFOS = $(info_INFOS)
+TESTS = $(check_PROGRAMS) $(check_SCRIPTS) $(check_MODULES)
 
 CLEANFILES =
 MOSTLYCLEANFILES =
@@ -151,6 +152,9 @@
 include $(srcdir)/conf/$(target_cpu)-$(platform).mk
 endif
 
+# for tests
+include $(srcdir)/conf/tests.mk
+
 # For external modules.
 -include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk)
 
@@ -428,7 +432,15 @@
 	@echo "$(distdir).tar.gz is ready for distribution" | \
 	  sed 'h;s/./=/g;p;x;p;x'
 
-check:
+check: $(TESTS)
+	@list="$(check_PROGRAMS) $(check_SCRIPTS)"; \
+	for file in $$list; do \
+	  if $(builddir)/$$file; then \
+	    echo "$$file: PASS"; \
+	  else \
+	    echo "$$file: FAIL"; \
+	  fi; \
+	done
 
 .SUFFIX:
 .SUFFIX: .c .o .S .d

=== added file 'conf/tests.rmk'
--- conf/tests.rmk	1970-01-01 00:00:00 +0000
+++ conf/tests.rmk	2009-11-23 14:11:45 +0000
@@ -0,0 +1,26 @@
+# -*- makefile -*-
+
+check_MODULES += functional_test.mod
+functional_test_mod_SOURCES = tests/functional_test.c tests/test.c
+functional_test_mod_CFLAGS  = $(COMMON_CFLAGS)
+functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# Unit tests
+
+check_PROGRAMS += example_unit_test
+example_unit_test_SOURCES = tests/example_unit_test.c kern/list.c kern/misc.c tests/test.c tests/unit_test.c
+example_unit_test_CFLAGS  = -Wno-format
+
+check_PROGRAMS += grub_sprintf
+grub_sprintf_SOURCES = tests/grub_sprintf.c kern/misc.c kern/list.c tests/test.c tests/unit_test.c
+grub_sprintf_CFLAGS  = -Wno-error -Wno-format
+
+# Functional tests
+
+check_MODULES += example_functional_test.mod
+example_functional_test_mod_SOURCES = tests/example_functional_test.c
+example_functional_test_mod_CFLAGS  = -Wno-format $(COMMON_CFLAGS)
+example_functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+
+

=== added file 'include/grub/test.h'
--- include/grub/test.h	1970-01-01 00:00:00 +0000
+++ include/grub/test.h	2009-11-23 14:14:27 +0000
@@ -0,0 +1,84 @@
+#ifndef GRUB_TEST_HEADER
+#define GRUB_TEST_HEADER
+
+#include <grub/dl.h>
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/symbol.h>
+
+struct grub_test
+{
+  /* The next test.  */
+  struct grub_test *next;
+
+  /* The test name.  */
+  char *name;
+
+  /* The test main function.  */
+  void (*main) (void);
+};
+typedef struct grub_test *grub_test_t;
+
+extern grub_test_t EXPORT_VAR (grub_test_list);
+
+void EXPORT_FUNC (grub_test_register) (const char *name, void (*test) (void));
+
+void EXPORT_FUNC (grub_test_unregister) (const char *name);
+
+/* Execute a test and print results.  */
+int grub_test_run (const char *name);
+
+/* Test `cond' for nonzero; log failure otherwise.  */
+void grub_test_nonzero (int cond, const char *file,
+			const char *func, grub_uint32_t line,
+			const char *fmt, ...)
+  __attribute__ ((format (printf, 5, 6)));
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+#  define __func__ __FUNCTION__
+# else
+#  define __func__ "<unknown>"
+# endif
+#endif
+
+/* Macro to fill in location details and an optional error message.  */
+#define grub_test_assert(cond, ...)			\
+  grub_test_nonzero(cond, __FILE__, __func__, __LINE__, \
+		    ## __VA_ARGS__,			\
+		    "assert failed: %s", #cond)
+
+/* Macro to define a unit test.  */
+#define GRUB_UNIT_TEST(name, funp)		\
+  void grub_unit_test_init (void)		\
+  {						\
+    grub_test_register (name, funp);		\
+  }						\
+						\
+  void grub_unit_test_fini (void)		\
+  {						\
+    grub_test_unregister (name);		\
+  }
+
+/* Macro to define a functional test.  */
+#define GRUB_FUNCTIONAL_TEST(name, funp)	\
+  GRUB_MOD_INIT(functional_test_##funp)		\
+  {						\
+    grub_test_register (name, funp);		\
+  }						\
+						\
+  GRUB_MOD_FINI(functional_test_##funp)		\
+  {						\
+    grub_test_unregister (name);		\
+  }
+
+/* Functions that are defined differently for unit and functional tests.  */
+void *grub_test_malloc (grub_size_t size);
+void grub_test_free (void *ptr);
+char *grub_test_strdup (const char *str);
+int grub_test_vsprintf (char *str, const char *fmt, va_list args);
+int grub_test_printf (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+
+#endif /* ! GRUB_TEST_HEADER */

=== added directory 'tests'
=== added file 'tests/example_functional_test.c'
--- tests/example_functional_test.c	1970-01-01 00:00:00 +0000
+++ tests/example_functional_test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,17 @@
+/* All tests need to include test.h for GRUB testing framework.  */
+#include <grub/test.h>
+
+/* Functional test main method.  */
+static void
+example_test (void)
+{
+  /* Check if 1st argument is true and report with default error message.  */
+  grub_test_assert (1 == 1);
+
+  /* Check if 1st argument is true and report with custom error message.  */
+  grub_test_assert (2 == 2, "2 equal 2 expected");
+  grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a functional test.  */
+GRUB_FUNCTIONAL_TEST ("example_functional_test", example_test);

=== added file 'tests/example_unit_test.c'
--- tests/example_unit_test.c	1970-01-01 00:00:00 +0000
+++ tests/example_unit_test.c	2009-11-23 14:30:40 +0000
@@ -0,0 +1,20 @@
+/* Unit tests are normal programs, so they can include C library.  */
+#include <string.h>
+
+/* All tests need to include test.h for GRUB testing framework.  */
+#include <grub/test.h>
+
+/* Unit test main method.  */
+static void
+example_test (void)
+{
+  /* Check if 1st argument is true and report with default error message.  */
+  grub_test_assert (1 == 1);
+
+  /* Check if 1st argument is true and report with custom error message.  */
+  grub_test_assert (2 == 2, "2 equal 2 expected");
+  grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a unit test.  */
+GRUB_UNIT_TEST ("example_unit_test", example_test);

=== added file 'tests/functional_test.c'
--- tests/functional_test.c	1970-01-01 00:00:00 +0000
+++ tests/functional_test.c	2009-11-23 14:33:14 +0000
@@ -0,0 +1,89 @@
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/test.h>
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+  return grub_malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+  grub_free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+  return grub_vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+  return grub_strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+  int r;
+  va_list ap;
+
+  va_start (ap, fmt);
+  r = grub_vprintf (fmt, ap);
+  va_end (ap);
+
+  return r;
+}
+
+static grub_err_t
+grub_functional_test (struct grub_extcmd *cmd __attribute__ ((unused)),
+		      int argc, char **args __attribute__ ((unused)))
+{
+  int i;
+  int status;
+  grub_test_t test;
+
+  auto int print_name (grub_test_t test);
+  int print_name (grub_test_t test)
+  {
+    grub_printf ("%s\n", test->name);
+    return 0;
+  }
+
+  if (!argc)
+    {
+      grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+			 (grub_list_hook_t) print_name);
+      return GRUB_ERR_NONE;
+    }
+
+  status = 0;
+  for (i = 0; i < argc; i++)
+    {
+      test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list),
+				   args[i]);
+      status = grub_test_run (test->name) ? : status;
+    }
+
+  return status;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT (functional_test)
+{
+  cmd = grub_register_extcmd ("functional_test", grub_functional_test,
+			      GRUB_COMMAND_FLAG_CMDLINE,
+			      "functional_test [name ...]",
+			      "Run or list functional tests.", 0);
+}
+
+GRUB_MOD_FINI (functional_test)
+{
+  grub_unregister_extcmd (cmd);
+}

=== added file 'tests/grub_sprintf.c'
--- tests/grub_sprintf.c	1970-01-01 00:00:00 +0000
+++ tests/grub_sprintf.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grub/test.h>
+#include <grub/misc.h>
+
+#define TEST(type,fmt,val)				\
+  do {							\
+    int r1, r2;						\
+    char b1[1024];					\
+    char b2[1024];					\
+							\
+    b1[0] = b2[0] = '\0';				\
+    r1 = sprintf (b1, fmt, val);			\
+    r2 = grub_sprintf (b2, fmt, val);			\
+							\
+    grub_test_assert (strcmp (b1, b2) == 0,		\
+		      "for (\"%s\","type") "		\
+		      "result should be (\"%s\",%d) "	\
+		      "but got (\"%s\",%d)",		\
+		      fmt, val, b1, r1, b2, r2);	\
+							\
+    grub_test_assert (r1 == r2,				\
+		      "for (\"%s\","type") "		\
+		      "result should be (\"%s\",%d) "	\
+		      "but got (\"%s\",%d)",		\
+		      fmt, val, b1, r1, b2, r2);	\
+  } while (0)
+
+#define D(fmt,v) TEST("%d",fmt,v)
+#define U(fmt,v) TEST("%u",fmt,v)
+#define x(fmt,v) TEST("%x",fmt,v)
+#define X(fmt,v) TEST("%X",fmt,v)
+#define P(fmt,v) TEST("%p",fmt,v)
+#define S(fmt,s) TEST("%s",fmt,s)
+
+static void
+sprintf_checks (void)
+{
+  D ("%d", -1);
+  D ("%d", 0);
+  D ("%d", 1);
+  D ("%5d", -1);
+  D ("%5d", 0);
+  D ("%5d", 1);
+  D ("%-5d", -1);
+  D ("%-5d", 0);
+  D ("%-5d", 1);
+  D ("%.5d", -1);
+  D ("%.5d", 0);
+  D ("%.5d", 1);
+  D ("%5.0d", -1);
+  D ("%5.0d", 0);
+  D ("%5.0d", 1);
+  D ("%-5.0d", -1);
+  D ("%-5.0d", 0);
+  D ("%-5.0d", 1);
+
+  U ("%d", -1);
+  U ("%d", 0);
+  U ("%d", 1);
+  U ("%5d", -1);
+  U ("%5d", 0);
+  U ("%5d", 1);
+  U ("%-5d", -1);
+  U ("%-5d", 0);
+  U ("%-5d", 1);
+  U ("%.5d", -1);
+  U ("%.5d", 0);
+  U ("%.5d", 1);
+  U ("%5.0d", -1);
+  U ("%5.0d", 0);
+  U ("%5.0d", 1);
+  U ("%-5.0d", -1);
+  U ("%-5.0d", 0);
+  U ("%-5.0d", 1);
+
+  x ("%d", -1);
+  x ("%d", 0);
+  x ("%d", 1);
+  x ("%5d", -1);
+  x ("%5d", 0);
+  x ("%5d", 1);
+  x ("%-5d", -1);
+  x ("%-5d", 0);
+  x ("%-5d", 1);
+  x ("%.5d", -1);
+  x ("%.5d", 0);
+  x ("%.5d", 1);
+  x ("%5.0d", -1);
+  x ("%5.0d", 0);
+  x ("%5.0d", 1);
+  x ("%-5.0d", -1);
+  x ("%-5.0d", 0);
+  x ("%-5.0d", 1);
+
+  X ("%d", -1);
+  X ("%d", 0);
+  X ("%d", 1);
+  X ("%5d", -1);
+  X ("%5d", 0);
+  X ("%5d", 1);
+  X ("%-5d", -1);
+  X ("%-5d", 0);
+  X ("%-5d", 1);
+  X ("%.5d", -1);
+  X ("%.5d", 0);
+  X ("%.5d", 1);
+  X ("%5.0d", -1);
+  X ("%5.0d", 0);
+  X ("%5.0d", 1);
+  X ("%-5.0d", -1);
+  X ("%-5.0d", 0);
+  X ("%-5.0d", 1);
+
+  P ("%p", NULL);
+  P ("%p", sprintf_checks);
+
+  S ("%s", (char *) NULL);
+  S ("%s", "abcd");
+  S ("%10s", "abcd");
+  S ("%10.5s", "abcdefgh");
+  S ("%10.5s", "ab");
+  S ("%2.5s", "a");
+  S ("%2.5s", "abcdefgh");
+
+  D ("%4.2d", 1);
+  D ("%4.2d", 12);
+  D ("%4.2d", 123);
+  D ("%4.2d", 1234);
+  D ("%4.2d", 12345);
+  D ("%3.3d", 12);
+  D ("%3.3d", 123);
+  D ("%3.3d", 1234);
+  D ("%2.4d", 12345);
+  D ("%2.4d", 1234);
+  D ("%2.4d", 123);
+  D ("%2.4d", 12);
+  D ("%2.4d", 1);
+  D ("%.0d", 0);
+  D ("%.0d", 1);
+
+  S ("%4.2s", "1");
+  S ("%4.2s", "12");
+  S ("%4.2s", "123");
+  S ("%4.2s", "1234");
+  S ("%4.2s", "12345");
+  S ("%3.3s", "12");
+  S ("%3.3s", "123");
+  S ("%3.3s", "1234");
+  S ("%2.4s", "12345");
+  S ("%2.4s", "1234");
+  S ("%2.4s", "123");
+  S ("%2.4s", "12");
+  S ("%2.4s", "1");
+
+  /* add few more here, if necessary  */
+}
+
+GRUB_UNIT_TEST ("grub_sprintf", sprintf_checks);

=== added file 'tests/test.c'
--- tests/test.c	1970-01-01 00:00:00 +0000
+++ tests/test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,150 @@
+#include <grub/misc.h>
+#include <grub/test.h>
+
+struct grub_test_failure
+{
+  /* The next failure.  */
+  struct grub_test_failure *next;
+
+  /* The test source file name.  */
+  char *file;
+
+  /* The test function name.  */
+  char *funp;
+
+  /* The test call line number.  */
+  grub_uint32_t line;
+
+  /* The test failure message.  */
+  char *message;
+};
+typedef struct grub_test_failure *grub_test_failure_t;
+
+grub_test_t grub_test_list;
+static grub_test_failure_t failure_list;
+
+static void
+add_failure (const char *file,
+	     const char *funp,
+	     grub_uint32_t line, const char *fmt, va_list args)
+{
+  char buf[1024];
+  grub_test_failure_t failure;
+
+  failure = (grub_test_failure_t) grub_test_malloc (sizeof (*failure));
+  if (!failure)
+    return;
+
+  grub_test_vsprintf (buf, fmt, args);
+
+  failure->file = grub_test_strdup (file ? : "<unknown_file>");
+  failure->funp = grub_test_strdup (funp ? : "<unknown_function>");
+  failure->line = line;
+  failure->message = grub_test_strdup (buf);
+
+  grub_list_push (GRUB_AS_LIST_P (&failure_list), GRUB_AS_LIST (failure));
+}
+
+void
+free_failures (void)
+{
+  grub_test_failure_t item;
+
+  while ((item = grub_list_pop (GRUB_AS_LIST_P (&failure_list))) != 0)
+    {
+      if (item->message)
+	grub_test_free (item->message);
+
+      if (item->funp)
+	grub_test_free (item->funp);
+
+      if (item->file)
+	grub_test_free (item->file);
+
+      grub_test_free (item);
+    }
+  failure_list = 0;
+}
+
+void
+grub_test_nonzero (int cond,
+		   const char *file,
+		   const char *funp, grub_uint32_t line, const char *fmt, ...)
+{
+  va_list ap;
+
+  if (cond)
+    return;
+
+  va_start (ap, fmt);
+  add_failure (file, funp, line, fmt, ap);
+  va_end (ap);
+}
+
+void
+grub_test_register (const char *name, void (*test_main) (void))
+{
+  grub_test_t test;
+
+  test = (grub_test_t) grub_test_malloc (sizeof (*test));
+  if (!test)
+    return;
+
+  test->name = grub_test_strdup (name);
+  test->main = test_main;
+
+  grub_list_push (GRUB_AS_LIST_P (&grub_test_list), GRUB_AS_LIST (test));
+}
+
+void
+grub_test_unregister (const char *name)
+{
+  grub_test_t test;
+
+  test = (grub_test_t) grub_named_list_find
+    (GRUB_AS_NAMED_LIST (grub_test_list), name);
+
+  if (test)
+    {
+      grub_list_remove (GRUB_AS_LIST_P (&grub_test_list),
+			GRUB_AS_LIST (test));
+
+      if (test->name)
+	grub_test_free (test->name);
+
+      grub_test_free (test);
+    }
+}
+
+int
+grub_test_run (const char *name)
+{
+  grub_test_t test;
+
+  auto int print_failure (grub_test_failure_t item);
+  int print_failure (grub_test_failure_t item)
+  {
+    grub_test_failure_t failure = (grub_test_failure_t) item;
+
+    grub_test_printf (" %s:%s:%u: %s\n",
+		      (failure->file ? : "<unknown_file>"),
+		      (failure->funp ? : "<unknown_function>"),
+		      failure->line, (failure->message ? : "<no message>"));
+    return 0;
+  }
+
+  test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list), name);
+  if (!test)
+    return GRUB_ERR_FILE_NOT_FOUND;
+
+  test->main ();
+
+  if (!failure_list)
+    return GRUB_ERR_NONE;
+
+  grub_test_printf ("%s:\n", test->name);
+  grub_list_iterate (GRUB_AS_LIST (failure_list),
+		     (grub_list_hook_t) print_failure);
+  free_failures ();
+  return GRUB_ERR_TEST_FAILURE;
+}

=== added file 'tests/unit_test.c'
--- tests/unit_test.c	1970-01-01 00:00:00 +0000
+++ tests/unit_test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <grub/list.h>
+#include <grub/test.h>
+#include <grub/handler.h>
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+  return malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+  free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+  return vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+  return strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+  int r;
+  va_list ap;
+
+  va_start (ap, fmt);
+  r = vprintf (fmt, ap);
+  va_end (ap);
+
+  return r;
+}
+
+int
+main (int argc __attribute__ ((unused)),
+      char *argv[] __attribute__ ((unused)))
+{
+  int status = 0;
+
+  extern void grub_unit_test_init (void);
+  extern void grub_unit_test_fini (void);
+
+  auto int run_test (grub_test_t test);
+  int run_test (grub_test_t test)
+  {
+    status = grub_test_run (test->name) ? : status;
+    return 0;
+  }
+
+  grub_unit_test_init ();
+  grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+		     (grub_list_hook_t) run_test);
+  grub_unit_test_fini ();
+
+  exit (status);
+}
+
+/* Other misc. functions necessary for successful linking.  */
+
+char *
+grub_env_get (const char *name __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_err_t
+grub_error (grub_err_t n, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+
+  return n;
+}
+
+void *
+grub_malloc (grub_size_t size)
+{
+  return malloc (size);
+}
+
+void
+grub_refresh (void)
+{
+  fflush (stdout);
+}
+
+void
+grub_putchar (int c)
+{
+  putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+  return -1;
+}
+
+void
+grub_exit (void)
+{
+  exit (1);
+}
+
+struct grub_handler_class grub_term_input_class;
+struct grub_handler_class grub_term_output_class;


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-11-23 15:16   ` BVK
@ 2009-11-23 15:54     ` BVK
  0 siblings, 0 replies; 7+ messages in thread
From: BVK @ 2009-11-23 15:54 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 119 bytes --]

Earlier patch had ChangeLog entry missing for conf/tests.rmk.
Attached version has it fixed now.




-- 
bvk.chaitanya

[-- Attachment #2: testing-framework.patch --]
[-- Type: application/octet-stream, Size: 17608 bytes --]

=== modified file 'ChangeLog'
--- ChangeLog	2009-11-09 21:58:22 +0000
+++ ChangeLog	2009-11-23 15:40:59 +0000
@@ -1,3 +1,17 @@
+2009-11-23  BVK Chaitanya  <bvk.groups@gmail.com>
+
+	Regression testing framework for GRUB.
+
+	* Makefile.in: Added rules for make check target.
+	* conf/tests.rmk: New file.  Rules for tests.
+	* include/grub/test.h: New file.  Header for testing framework.
+	* tests/test.c: Common definitions for the framework.
+	* tests/functional_test.c: Definitions for functional tests.
+	* tests/unit_test.c: Definitions for unit tests.
+	* tests/example_unit_test.c: Example test.
+	* tests/example_functional_test.c: Example test.
+	* tests/grub_sprintf.c: Unit test for grub_sprintf.
+
 2009-11-09  Robert Millan  <rmh.grub@aybabtu.com>
 
 	Import from Gnulib.

=== modified file 'Makefile.in'
--- Makefile.in	2009-11-09 22:48:53 +0000
+++ Makefile.in	2009-11-23 14:33:29 +0000
@@ -125,6 +125,7 @@
 SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) $(grub-mkconfig_SCRIPTS) \
 	$(lib_SCRIPTS)
 INFOS = $(info_INFOS)
+TESTS = $(check_PROGRAMS) $(check_SCRIPTS) $(check_MODULES)
 
 CLEANFILES =
 MOSTLYCLEANFILES =
@@ -151,6 +152,9 @@
 include $(srcdir)/conf/$(target_cpu)-$(platform).mk
 endif
 
+# for tests
+include $(srcdir)/conf/tests.mk
+
 # For external modules.
 -include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk)
 
@@ -428,7 +432,15 @@
 	@echo "$(distdir).tar.gz is ready for distribution" | \
 	  sed 'h;s/./=/g;p;x;p;x'
 
-check:
+check: $(TESTS)
+	@list="$(check_PROGRAMS) $(check_SCRIPTS)"; \
+	for file in $$list; do \
+	  if $(builddir)/$$file; then \
+	    echo "$$file: PASS"; \
+	  else \
+	    echo "$$file: FAIL"; \
+	  fi; \
+	done
 
 .SUFFIX:
 .SUFFIX: .c .o .S .d

=== added file 'conf/tests.rmk'
--- conf/tests.rmk	1970-01-01 00:00:00 +0000
+++ conf/tests.rmk	2009-11-23 14:11:45 +0000
@@ -0,0 +1,26 @@
+# -*- makefile -*-
+
+check_MODULES += functional_test.mod
+functional_test_mod_SOURCES = tests/functional_test.c tests/test.c
+functional_test_mod_CFLAGS  = $(COMMON_CFLAGS)
+functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# Unit tests
+
+check_PROGRAMS += example_unit_test
+example_unit_test_SOURCES = tests/example_unit_test.c kern/list.c kern/misc.c tests/test.c tests/unit_test.c
+example_unit_test_CFLAGS  = -Wno-format
+
+check_PROGRAMS += grub_sprintf
+grub_sprintf_SOURCES = tests/grub_sprintf.c kern/misc.c kern/list.c tests/test.c tests/unit_test.c
+grub_sprintf_CFLAGS  = -Wno-error -Wno-format
+
+# Functional tests
+
+check_MODULES += example_functional_test.mod
+example_functional_test_mod_SOURCES = tests/example_functional_test.c
+example_functional_test_mod_CFLAGS  = -Wno-format $(COMMON_CFLAGS)
+example_functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+
+

=== added file 'include/grub/test.h'
--- include/grub/test.h	1970-01-01 00:00:00 +0000
+++ include/grub/test.h	2009-11-23 14:14:27 +0000
@@ -0,0 +1,84 @@
+#ifndef GRUB_TEST_HEADER
+#define GRUB_TEST_HEADER
+
+#include <grub/dl.h>
+#include <grub/list.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/symbol.h>
+
+struct grub_test
+{
+  /* The next test.  */
+  struct grub_test *next;
+
+  /* The test name.  */
+  char *name;
+
+  /* The test main function.  */
+  void (*main) (void);
+};
+typedef struct grub_test *grub_test_t;
+
+extern grub_test_t EXPORT_VAR (grub_test_list);
+
+void EXPORT_FUNC (grub_test_register) (const char *name, void (*test) (void));
+
+void EXPORT_FUNC (grub_test_unregister) (const char *name);
+
+/* Execute a test and print results.  */
+int grub_test_run (const char *name);
+
+/* Test `cond' for nonzero; log failure otherwise.  */
+void grub_test_nonzero (int cond, const char *file,
+			const char *func, grub_uint32_t line,
+			const char *fmt, ...)
+  __attribute__ ((format (printf, 5, 6)));
+
+#if __STDC_VERSION__ < 199901L
+# if __GNUC__ >= 2
+#  define __func__ __FUNCTION__
+# else
+#  define __func__ "<unknown>"
+# endif
+#endif
+
+/* Macro to fill in location details and an optional error message.  */
+#define grub_test_assert(cond, ...)			\
+  grub_test_nonzero(cond, __FILE__, __func__, __LINE__, \
+		    ## __VA_ARGS__,			\
+		    "assert failed: %s", #cond)
+
+/* Macro to define a unit test.  */
+#define GRUB_UNIT_TEST(name, funp)		\
+  void grub_unit_test_init (void)		\
+  {						\
+    grub_test_register (name, funp);		\
+  }						\
+						\
+  void grub_unit_test_fini (void)		\
+  {						\
+    grub_test_unregister (name);		\
+  }
+
+/* Macro to define a functional test.  */
+#define GRUB_FUNCTIONAL_TEST(name, funp)	\
+  GRUB_MOD_INIT(functional_test_##funp)		\
+  {						\
+    grub_test_register (name, funp);		\
+  }						\
+						\
+  GRUB_MOD_FINI(functional_test_##funp)		\
+  {						\
+    grub_test_unregister (name);		\
+  }
+
+/* Functions that are defined differently for unit and functional tests.  */
+void *grub_test_malloc (grub_size_t size);
+void grub_test_free (void *ptr);
+char *grub_test_strdup (const char *str);
+int grub_test_vsprintf (char *str, const char *fmt, va_list args);
+int grub_test_printf (const char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+
+#endif /* ! GRUB_TEST_HEADER */

=== added directory 'tests'
=== added file 'tests/example_functional_test.c'
--- tests/example_functional_test.c	1970-01-01 00:00:00 +0000
+++ tests/example_functional_test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,17 @@
+/* All tests need to include test.h for GRUB testing framework.  */
+#include <grub/test.h>
+
+/* Functional test main method.  */
+static void
+example_test (void)
+{
+  /* Check if 1st argument is true and report with default error message.  */
+  grub_test_assert (1 == 1);
+
+  /* Check if 1st argument is true and report with custom error message.  */
+  grub_test_assert (2 == 2, "2 equal 2 expected");
+  grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a functional test.  */
+GRUB_FUNCTIONAL_TEST ("example_functional_test", example_test);

=== added file 'tests/example_unit_test.c'
--- tests/example_unit_test.c	1970-01-01 00:00:00 +0000
+++ tests/example_unit_test.c	2009-11-23 14:30:40 +0000
@@ -0,0 +1,20 @@
+/* Unit tests are normal programs, so they can include C library.  */
+#include <string.h>
+
+/* All tests need to include test.h for GRUB testing framework.  */
+#include <grub/test.h>
+
+/* Unit test main method.  */
+static void
+example_test (void)
+{
+  /* Check if 1st argument is true and report with default error message.  */
+  grub_test_assert (1 == 1);
+
+  /* Check if 1st argument is true and report with custom error message.  */
+  grub_test_assert (2 == 2, "2 equal 2 expected");
+  grub_test_assert (2 == 3, "2 is not equal to %d", 3);
+}
+
+/* Register example_test method as a unit test.  */
+GRUB_UNIT_TEST ("example_unit_test", example_test);

=== added file 'tests/functional_test.c'
--- tests/functional_test.c	1970-01-01 00:00:00 +0000
+++ tests/functional_test.c	2009-11-23 14:33:14 +0000
@@ -0,0 +1,89 @@
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/extcmd.h>
+#include <grub/test.h>
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+  return grub_malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+  grub_free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+  return grub_vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+  return grub_strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+  int r;
+  va_list ap;
+
+  va_start (ap, fmt);
+  r = grub_vprintf (fmt, ap);
+  va_end (ap);
+
+  return r;
+}
+
+static grub_err_t
+grub_functional_test (struct grub_extcmd *cmd __attribute__ ((unused)),
+		      int argc, char **args __attribute__ ((unused)))
+{
+  int i;
+  int status;
+  grub_test_t test;
+
+  auto int print_name (grub_test_t test);
+  int print_name (grub_test_t test)
+  {
+    grub_printf ("%s\n", test->name);
+    return 0;
+  }
+
+  if (!argc)
+    {
+      grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+			 (grub_list_hook_t) print_name);
+      return GRUB_ERR_NONE;
+    }
+
+  status = 0;
+  for (i = 0; i < argc; i++)
+    {
+      test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list),
+				   args[i]);
+      status = grub_test_run (test->name) ? : status;
+    }
+
+  return status;
+}
+
+static grub_extcmd_t cmd;
+
+GRUB_MOD_INIT (functional_test)
+{
+  cmd = grub_register_extcmd ("functional_test", grub_functional_test,
+			      GRUB_COMMAND_FLAG_CMDLINE,
+			      "functional_test [name ...]",
+			      "Run or list functional tests.", 0);
+}
+
+GRUB_MOD_FINI (functional_test)
+{
+  grub_unregister_extcmd (cmd);
+}

=== added file 'tests/grub_sprintf.c'
--- tests/grub_sprintf.c	1970-01-01 00:00:00 +0000
+++ tests/grub_sprintf.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grub/test.h>
+#include <grub/misc.h>
+
+#define TEST(type,fmt,val)				\
+  do {							\
+    int r1, r2;						\
+    char b1[1024];					\
+    char b2[1024];					\
+							\
+    b1[0] = b2[0] = '\0';				\
+    r1 = sprintf (b1, fmt, val);			\
+    r2 = grub_sprintf (b2, fmt, val);			\
+							\
+    grub_test_assert (strcmp (b1, b2) == 0,		\
+		      "for (\"%s\","type") "		\
+		      "result should be (\"%s\",%d) "	\
+		      "but got (\"%s\",%d)",		\
+		      fmt, val, b1, r1, b2, r2);	\
+							\
+    grub_test_assert (r1 == r2,				\
+		      "for (\"%s\","type") "		\
+		      "result should be (\"%s\",%d) "	\
+		      "but got (\"%s\",%d)",		\
+		      fmt, val, b1, r1, b2, r2);	\
+  } while (0)
+
+#define D(fmt,v) TEST("%d",fmt,v)
+#define U(fmt,v) TEST("%u",fmt,v)
+#define x(fmt,v) TEST("%x",fmt,v)
+#define X(fmt,v) TEST("%X",fmt,v)
+#define P(fmt,v) TEST("%p",fmt,v)
+#define S(fmt,s) TEST("%s",fmt,s)
+
+static void
+sprintf_checks (void)
+{
+  D ("%d", -1);
+  D ("%d", 0);
+  D ("%d", 1);
+  D ("%5d", -1);
+  D ("%5d", 0);
+  D ("%5d", 1);
+  D ("%-5d", -1);
+  D ("%-5d", 0);
+  D ("%-5d", 1);
+  D ("%.5d", -1);
+  D ("%.5d", 0);
+  D ("%.5d", 1);
+  D ("%5.0d", -1);
+  D ("%5.0d", 0);
+  D ("%5.0d", 1);
+  D ("%-5.0d", -1);
+  D ("%-5.0d", 0);
+  D ("%-5.0d", 1);
+
+  U ("%d", -1);
+  U ("%d", 0);
+  U ("%d", 1);
+  U ("%5d", -1);
+  U ("%5d", 0);
+  U ("%5d", 1);
+  U ("%-5d", -1);
+  U ("%-5d", 0);
+  U ("%-5d", 1);
+  U ("%.5d", -1);
+  U ("%.5d", 0);
+  U ("%.5d", 1);
+  U ("%5.0d", -1);
+  U ("%5.0d", 0);
+  U ("%5.0d", 1);
+  U ("%-5.0d", -1);
+  U ("%-5.0d", 0);
+  U ("%-5.0d", 1);
+
+  x ("%d", -1);
+  x ("%d", 0);
+  x ("%d", 1);
+  x ("%5d", -1);
+  x ("%5d", 0);
+  x ("%5d", 1);
+  x ("%-5d", -1);
+  x ("%-5d", 0);
+  x ("%-5d", 1);
+  x ("%.5d", -1);
+  x ("%.5d", 0);
+  x ("%.5d", 1);
+  x ("%5.0d", -1);
+  x ("%5.0d", 0);
+  x ("%5.0d", 1);
+  x ("%-5.0d", -1);
+  x ("%-5.0d", 0);
+  x ("%-5.0d", 1);
+
+  X ("%d", -1);
+  X ("%d", 0);
+  X ("%d", 1);
+  X ("%5d", -1);
+  X ("%5d", 0);
+  X ("%5d", 1);
+  X ("%-5d", -1);
+  X ("%-5d", 0);
+  X ("%-5d", 1);
+  X ("%.5d", -1);
+  X ("%.5d", 0);
+  X ("%.5d", 1);
+  X ("%5.0d", -1);
+  X ("%5.0d", 0);
+  X ("%5.0d", 1);
+  X ("%-5.0d", -1);
+  X ("%-5.0d", 0);
+  X ("%-5.0d", 1);
+
+  P ("%p", NULL);
+  P ("%p", sprintf_checks);
+
+  S ("%s", (char *) NULL);
+  S ("%s", "abcd");
+  S ("%10s", "abcd");
+  S ("%10.5s", "abcdefgh");
+  S ("%10.5s", "ab");
+  S ("%2.5s", "a");
+  S ("%2.5s", "abcdefgh");
+
+  D ("%4.2d", 1);
+  D ("%4.2d", 12);
+  D ("%4.2d", 123);
+  D ("%4.2d", 1234);
+  D ("%4.2d", 12345);
+  D ("%3.3d", 12);
+  D ("%3.3d", 123);
+  D ("%3.3d", 1234);
+  D ("%2.4d", 12345);
+  D ("%2.4d", 1234);
+  D ("%2.4d", 123);
+  D ("%2.4d", 12);
+  D ("%2.4d", 1);
+  D ("%.0d", 0);
+  D ("%.0d", 1);
+
+  S ("%4.2s", "1");
+  S ("%4.2s", "12");
+  S ("%4.2s", "123");
+  S ("%4.2s", "1234");
+  S ("%4.2s", "12345");
+  S ("%3.3s", "12");
+  S ("%3.3s", "123");
+  S ("%3.3s", "1234");
+  S ("%2.4s", "12345");
+  S ("%2.4s", "1234");
+  S ("%2.4s", "123");
+  S ("%2.4s", "12");
+  S ("%2.4s", "1");
+
+  /* add few more here, if necessary  */
+}
+
+GRUB_UNIT_TEST ("grub_sprintf", sprintf_checks);

=== added file 'tests/test.c'
--- tests/test.c	1970-01-01 00:00:00 +0000
+++ tests/test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,150 @@
+#include <grub/misc.h>
+#include <grub/test.h>
+
+struct grub_test_failure
+{
+  /* The next failure.  */
+  struct grub_test_failure *next;
+
+  /* The test source file name.  */
+  char *file;
+
+  /* The test function name.  */
+  char *funp;
+
+  /* The test call line number.  */
+  grub_uint32_t line;
+
+  /* The test failure message.  */
+  char *message;
+};
+typedef struct grub_test_failure *grub_test_failure_t;
+
+grub_test_t grub_test_list;
+static grub_test_failure_t failure_list;
+
+static void
+add_failure (const char *file,
+	     const char *funp,
+	     grub_uint32_t line, const char *fmt, va_list args)
+{
+  char buf[1024];
+  grub_test_failure_t failure;
+
+  failure = (grub_test_failure_t) grub_test_malloc (sizeof (*failure));
+  if (!failure)
+    return;
+
+  grub_test_vsprintf (buf, fmt, args);
+
+  failure->file = grub_test_strdup (file ? : "<unknown_file>");
+  failure->funp = grub_test_strdup (funp ? : "<unknown_function>");
+  failure->line = line;
+  failure->message = grub_test_strdup (buf);
+
+  grub_list_push (GRUB_AS_LIST_P (&failure_list), GRUB_AS_LIST (failure));
+}
+
+void
+free_failures (void)
+{
+  grub_test_failure_t item;
+
+  while ((item = grub_list_pop (GRUB_AS_LIST_P (&failure_list))) != 0)
+    {
+      if (item->message)
+	grub_test_free (item->message);
+
+      if (item->funp)
+	grub_test_free (item->funp);
+
+      if (item->file)
+	grub_test_free (item->file);
+
+      grub_test_free (item);
+    }
+  failure_list = 0;
+}
+
+void
+grub_test_nonzero (int cond,
+		   const char *file,
+		   const char *funp, grub_uint32_t line, const char *fmt, ...)
+{
+  va_list ap;
+
+  if (cond)
+    return;
+
+  va_start (ap, fmt);
+  add_failure (file, funp, line, fmt, ap);
+  va_end (ap);
+}
+
+void
+grub_test_register (const char *name, void (*test_main) (void))
+{
+  grub_test_t test;
+
+  test = (grub_test_t) grub_test_malloc (sizeof (*test));
+  if (!test)
+    return;
+
+  test->name = grub_test_strdup (name);
+  test->main = test_main;
+
+  grub_list_push (GRUB_AS_LIST_P (&grub_test_list), GRUB_AS_LIST (test));
+}
+
+void
+grub_test_unregister (const char *name)
+{
+  grub_test_t test;
+
+  test = (grub_test_t) grub_named_list_find
+    (GRUB_AS_NAMED_LIST (grub_test_list), name);
+
+  if (test)
+    {
+      grub_list_remove (GRUB_AS_LIST_P (&grub_test_list),
+			GRUB_AS_LIST (test));
+
+      if (test->name)
+	grub_test_free (test->name);
+
+      grub_test_free (test);
+    }
+}
+
+int
+grub_test_run (const char *name)
+{
+  grub_test_t test;
+
+  auto int print_failure (grub_test_failure_t item);
+  int print_failure (grub_test_failure_t item)
+  {
+    grub_test_failure_t failure = (grub_test_failure_t) item;
+
+    grub_test_printf (" %s:%s:%u: %s\n",
+		      (failure->file ? : "<unknown_file>"),
+		      (failure->funp ? : "<unknown_function>"),
+		      failure->line, (failure->message ? : "<no message>"));
+    return 0;
+  }
+
+  test = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_test_list), name);
+  if (!test)
+    return GRUB_ERR_FILE_NOT_FOUND;
+
+  test->main ();
+
+  if (!failure_list)
+    return GRUB_ERR_NONE;
+
+  grub_test_printf ("%s:\n", test->name);
+  grub_list_iterate (GRUB_AS_LIST (failure_list),
+		     (grub_list_hook_t) print_failure);
+  free_failures ();
+  return GRUB_ERR_TEST_FAILURE;
+}

=== added file 'tests/unit_test.c'
--- tests/unit_test.c	1970-01-01 00:00:00 +0000
+++ tests/unit_test.c	2009-11-23 14:14:27 +0000
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <grub/list.h>
+#include <grub/test.h>
+#include <grub/handler.h>
+
+void *
+grub_test_malloc (grub_size_t size)
+{
+  return malloc (size);
+}
+
+void
+grub_test_free (void *ptr)
+{
+  free (ptr);
+}
+
+int
+grub_test_vsprintf (char *str, const char *fmt, va_list args)
+{
+  return vsprintf (str, fmt, args);
+}
+
+char *
+grub_test_strdup (const char *str)
+{
+  return strdup (str);
+}
+
+int
+grub_test_printf (const char *fmt, ...)
+{
+  int r;
+  va_list ap;
+
+  va_start (ap, fmt);
+  r = vprintf (fmt, ap);
+  va_end (ap);
+
+  return r;
+}
+
+int
+main (int argc __attribute__ ((unused)),
+      char *argv[] __attribute__ ((unused)))
+{
+  int status = 0;
+
+  extern void grub_unit_test_init (void);
+  extern void grub_unit_test_fini (void);
+
+  auto int run_test (grub_test_t test);
+  int run_test (grub_test_t test)
+  {
+    status = grub_test_run (test->name) ? : status;
+    return 0;
+  }
+
+  grub_unit_test_init ();
+  grub_list_iterate (GRUB_AS_LIST (grub_test_list),
+		     (grub_list_hook_t) run_test);
+  grub_unit_test_fini ();
+
+  exit (status);
+}
+
+/* Other misc. functions necessary for successful linking.  */
+
+char *
+grub_env_get (const char *name __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_err_t
+grub_error (grub_err_t n, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+
+  return n;
+}
+
+void *
+grub_malloc (grub_size_t size)
+{
+  return malloc (size);
+}
+
+void
+grub_refresh (void)
+{
+  fflush (stdout);
+}
+
+void
+grub_putchar (int c)
+{
+  putchar (c);
+}
+
+int
+grub_getkey (void)
+{
+  return -1;
+}
+
+void
+grub_exit (void)
+{
+  exit (1);
+}
+
+struct grub_handler_class grub_term_input_class;
+struct grub_handler_class grub_term_output_class;


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-11-13 17:45 [RFC][PATCH] Basic unit testing support for GRUB BVK
  2009-11-16 21:21 ` Robert Millan
@ 2009-11-25 16:34 ` Felix Zielcke
  2009-12-03  3:38   ` BVK
  1 sibling, 1 reply; 7+ messages in thread
From: Felix Zielcke @ 2009-11-25 16:34 UTC (permalink / raw)
  To: The development of GNU GRUB

Am Freitag, den 13.11.2009, 23:15 +0530 schrieb BVK:
> Hi,
> 
> 
> Attached is the (experimental branch) patch for unit testing that I
> have in mind for GRUB.  Please let me know your comments.
> 
> It provides "make check" target which would build and execute test
> programs from "tests" directory.  We can write small programs to test
> specific functions in GRUB.  For example, attached patch has sample
> program comparing grub_sprintf behavior against standard sprintf
> output (from glibc).
> 
> Note that it currently has few gotchas, like using TARGET_CC instead
> of BUILD_CC, no support for XPASS, XFAIL tests, etc.

Thanks for your work.
A really useful test and hopefully not complicated to implement would be
if the config generated by grub-mkconfig (with e.g. 1 fake linux kernel)
can be parsed by sh.mod and only uses commands listed in commands.lst

Reasoning:
With current trunk grub-mkconfig this line ends up in grub.cfg:
  menuentry "Debian, with Linux GNU/Linux" {menuentry "2.6.31-1-amd64, with Linux " {     insmod ext2
and of course this totally breaks the parser.

-- 
Felix Zielcke
Proud Debian Maintainer and GNU GRUB developer




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-11-25 16:34 ` Felix Zielcke
@ 2009-12-03  3:38   ` BVK
  2009-12-04  7:15     ` BVK
  0 siblings, 1 reply; 7+ messages in thread
From: BVK @ 2009-12-03  3:38 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi,


I have been traveling a lot, so I couldn't reply :-(


On Wed, Nov 25, 2009 at 4:34 PM, Felix Zielcke <fzielcke@z-51.de> wrote:
>
> Thanks for your work.
> A really useful test and hopefully not complicated to implement would be
> if the config generated by grub-mkconfig (with e.g. 1 fake linux kernel)
> can be parsed by sh.mod and only uses commands listed in commands.lst

IMO this should also be done as part of update-grub process, because
parts of grub.cfg file generated includes changes done by users too.
Having a test case would only ensure that config snippets from GRUB
source tree are parseable.  So what we need is a grub util that checks
for correctness of grub.cfg at user site.

I am not sure how we can achieve this with current grub tools :-(
Maybe a grub-emu variant can do this parsing?




-- 
bvk.chaitanya



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH] Basic unit testing support for GRUB
  2009-12-03  3:38   ` BVK
@ 2009-12-04  7:15     ` BVK
  0 siblings, 0 replies; 7+ messages in thread
From: BVK @ 2009-12-04  7:15 UTC (permalink / raw)
  To: The development of GNU GRUB

Please see the other thread "Syntax checker for GRUB script
configuration file" for this tool.



-- 
bvk.chaitanya



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2009-12-04  7:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-11-13 17:45 [RFC][PATCH] Basic unit testing support for GRUB BVK
2009-11-16 21:21 ` Robert Millan
2009-11-23 15:16   ` BVK
2009-11-23 15:54     ` BVK
2009-11-25 16:34 ` Felix Zielcke
2009-12-03  3:38   ` BVK
2009-12-04  7:15     ` BVK

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.