[Cogl] [PATCH v2] tests: Adds our first white-box unit test

Robert Bragg robert at sixbynine.org
Thu Jun 6 08:41:42 PDT 2013


From: Robert Bragg <robert at linux.intel.com>

This adds the missing files and uses $(NM) as suggested.

regards,
- Robert

--- >8 ---

This adds a white-box unit test that verifies that GL_BLEND is disabled
when drawing an opaque rectangle, enabled when drawing a transparent
rectangle and then disabled again when drawing a transparent rectangle
but with a blend string that effectively disables blending.

This shares the test utilities and launcher infrastructure we are using
for conformance tests so we get consistent reporting and so unit tests
will be run against a range of different drivers.

This adds a --enable-unit-tests configure option which is enabled by
default but if disabled will make all UNIT_TESTS() into static inline
functions that we should expect the compiler to discard since they won't
be referenced by anything.
---
 Makefile.am                           |   2 +-
 cogl/Makefile.am                      |   8 +-
 cogl/driver/gl/cogl-pipeline-opengl.c |  36 +++-
 configure.ac                          |  18 +-
 test-fixtures/Makefile.am             |  24 +++
 test-fixtures/test-unit.h             |  31 +++
 test-fixtures/test-utils.c            | 342 ++++++++++++++++++++++++++++++++++
 test-fixtures/test-utils.h            | 156 ++++++++++++++++
 tests/Makefile.am                     |  18 +-
 tests/config.env.in                   |   3 +
 tests/conform/Makefile.am             |  10 +-
 tests/conform/config.env.in           |   3 -
 tests/conform/run-tests.sh            | 149 ---------------
 tests/conform/test-launcher.sh.in     |  33 ----
 tests/conform/test-utils.c            | 339 ---------------------------------
 tests/conform/test-utils.h            | 155 ---------------
 tests/run-tests.sh                    | 149 +++++++++++++++
 tests/test-launcher.sh                |  39 ++++
 tests/unit/Makefile.am                |  97 ++++++++++
 tests/unit/test-unit-main.c           |  41 ++++
 20 files changed, 960 insertions(+), 693 deletions(-)
 create mode 100644 test-fixtures/Makefile.am
 create mode 100644 test-fixtures/test-unit.h
 create mode 100644 test-fixtures/test-utils.c
 create mode 100644 test-fixtures/test-utils.h
 create mode 100644 tests/config.env.in
 delete mode 100644 tests/conform/config.env.in
 delete mode 100755 tests/conform/run-tests.sh
 delete mode 100755 tests/conform/test-launcher.sh.in
 delete mode 100644 tests/conform/test-utils.c
 delete mode 100644 tests/conform/test-utils.h
 create mode 100755 tests/run-tests.sh
 create mode 100755 tests/test-launcher.sh
 create mode 100644 tests/unit/Makefile.am
 create mode 100644 tests/unit/test-unit-main.c

diff --git a/Makefile.am b/Makefile.am
index 15fb589..308e66c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = deps cogl tests
+SUBDIRS = deps test-fixtures cogl tests
 
 if BUILD_COGL_PANGO
 SUBDIRS += cogl-pango
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 369bca4..323239d 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -539,13 +539,16 @@ if !USE_GLIB
 libcogl2_la_LIBADD += $(top_builddir)/deps/glib/libglib.la
 libcogl2_la_LIBADD += $(top_builddir)/deps/gmodule/libgmodule.la
 endif
+if UNIT_TESTS
+libcogl2_la_LIBADD += $(top_builddir)/test-fixtures/libtest-fixtures.la
+endif
 # XXX: The aim is to eventually get rid of all private API exports
 # for cogl-pango.
 libcogl2_la_LDFLAGS = \
 	-no-undefined \
 	-version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \
 	-export-dynamic \
-	-export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_foreach_sub_texture_in_region|_cogl_atlas_texture_new_with_size|_cogl_profile_trace_message|_cogl_context_get_default).*"
+	-export-symbols-regex "^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_foreach_sub_texture_in_region|_cogl_atlas_texture_new_with_size|_cogl_profile_trace_message|_cogl_context_get_default|test_|unit_test_).*"
 
 libcogl2_la_SOURCES = $(cogl_sources_c)
 nodist_libcogl2_la_SOURCES = $(BUILT_SOURCES)
@@ -658,6 +661,9 @@ Cogl-2.0.gir: libcogl2.la Makefile
 Cogl_2_0_gir_NAMESPACE = Cogl
 Cogl_2_0_gir_VERSION = 2.0
 Cogl_2_0_gir_LIBS = libcogl2.la
+if UNIT_TESTS
+Cogl_2_0_gir_LIBS += $(top_builddir)/test-fixtures/libtest-fixtures.la
+endif
 Cogl_2_0_gir_FILES = $(cogl_public_h) cogl-enum-types.h
 Cogl_2_0_gir_CFLAGS = $(AM_CPPFLAGS) $(AM_CPPFLAGS) $(COGL_DEP_CFLAGS) -UCOGL_ENABLE_EXPERIMENTAL_API -UCOGL_ENABLE_EXPERIMENTAL_2_0_API
 Cogl_2_0_gir_INCLUDES = GL-1.0 GObject-2.0
diff --git a/cogl/driver/gl/cogl-pipeline-opengl.c b/cogl/driver/gl/cogl-pipeline-opengl.c
index cffff3a..30e3e8e 100644
--- a/cogl/driver/gl/cogl-pipeline-opengl.c
+++ b/cogl/driver/gl/cogl-pipeline-opengl.c
@@ -25,9 +25,7 @@
  *   Robert Bragg <robert at linux.intel.com>
  */
 
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include "cogl-debug.h"
 #include "cogl-util-gl-private.h"
@@ -41,6 +39,8 @@
 
 #include "cogl-pipeline-progend-glsl-private.h"
 
+#include <test-fixtures/test-unit.h>
+
 #include <glib.h>
 #include <string.h>
 
@@ -444,6 +444,38 @@ flush_depth_state (CoglContext *ctx,
     }
 }
 
+UNIT_TEST (check_gl_blend_enable,
+           0 /* no requirements */,
+           0 /* no failure cases */)
+{
+  CoglPipeline *pipeline = cogl_pipeline_new (test_ctx);
+
+  /* By default blending should be disabled */
+  g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
+
+  cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
+  _cogl_framebuffer_flush_journal (test_fb);
+
+  /* After drawing an opaque rectangle blending should still be
+   * disabled */
+  g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
+
+  cogl_pipeline_set_color4f (pipeline, 0, 0, 0, 0);
+  cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
+  _cogl_framebuffer_flush_journal (test_fb);
+
+  /* After drawing a transparent rectangle blending should be enabled */
+  g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 1);
+
+  cogl_pipeline_set_blend (pipeline, "RGBA=ADD(SRC_COLOR, 0)", NULL);
+  cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1);
+  _cogl_framebuffer_flush_journal (test_fb);
+
+  /* After setting a blend string that effectively disables blending
+   * then blending should be disabled */
+  g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0);
+}
+
 static void
 _cogl_pipeline_flush_color_blend_alpha_depth_state (
                                             CoglPipeline *pipeline,
diff --git a/configure.ac b/configure.ac
index a4184b7..498583d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -267,6 +267,18 @@ AS_CASE(
 
 AC_SUBST(COGL_DEBUG_CFLAGS)
 
+AC_ARG_ENABLE(
+  [unit-tests],
+  [AC_HELP_STRING([--enable-unit-tests=@<:@no/yes@:>@], [Build Cogl unit tests @<:@default=yes@:>@])],
+  [],
+  enable_unit_tests=yes
+)
+AS_IF([test "x$enable_unit_tests" = "xyes"],
+      [
+        AC_DEFINE([ENABLE_UNIT_TESTS], [1], [Whether to enable building unit tests])
+      ]
+)
+AM_CONDITIONAL(UNIT_TESTS, test "x$enable_unit_tests" = "xyes")
 
 dnl     ============================================================
 dnl     Enable cairo usage for debugging
@@ -1399,6 +1411,7 @@ deps/Makefile
 deps/glib/Makefile
 deps/gmodule/Makefile
 deps/gmodule/gmoduleconf.h
+test-fixtures/Makefile
 cogl/Makefile
 cogl/cogl2.pc
 cogl/cogl-defines.h
@@ -1422,9 +1435,9 @@ doc/reference/cogl-gst/Makefile
 doc/reference/cogl-gst/cogl-gst-docs.xml
 examples/Makefile
 tests/Makefile
+tests/config.env
 tests/conform/Makefile
-tests/conform/config.env
-tests/conform/test-launcher.sh
+tests/unit/Makefile
 tests/micro-perf/Makefile
 tests/data/Makefile
 po/Makefile.in
@@ -1482,6 +1495,7 @@ echo ""
 echo " • Extra:"
 echo "        Build API reference: ${enable_gtk_doc}"
 echo "        Build introspection data: ${enable_introspection}"
+echo "        Build unit tests: ${enable_unit_tests}"
 echo "        Enable internationalization: ${USE_NLS}"
 
 echo ""
diff --git a/test-fixtures/Makefile.am b/test-fixtures/Makefile.am
new file mode 100644
index 0000000..62fd9d1
--- /dev/null
+++ b/test-fixtures/Makefile.am
@@ -0,0 +1,24 @@
+
+noinst_LTLIBRARIES = libtest-fixtures.la
+
+libtest_fixtures_la_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_builddir)/cogl \
+	-Wall \
+	$(NULL)
+
+if !USE_GLIB
+libtest_fixtures_la_CPPFLAGS += -I$(top_builddir)/deps/glib
+endif
+
+libtest_fixtures_la_CPPFLAGS += \
+	-DCOGL_DISABLE_DEPRECATED \
+	-DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" \
+	-DCOGL_COMPILATION
+
+libtest_fixtures_la_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+
+libtest_fixtures_la_SOURCES = \
+	test-utils.h \
+	test-utils.c
+
diff --git a/test-fixtures/test-unit.h b/test-fixtures/test-unit.h
new file mode 100644
index 0000000..270a941
--- /dev/null
+++ b/test-fixtures/test-unit.h
@@ -0,0 +1,31 @@
+#ifndef _TEST_UNIT_H_
+#define _TEST_UNIT_H_
+
+#include <test-fixtures/test-utils.h>
+
+#ifdef ENABLE_UNIT_TESTS
+
+typedef struct _CoglUnitTest
+{
+  const char *name;
+  TestFlags requirement_flags;
+  TestFlags known_failure_flags;
+  void (*run) (void);
+} CoglUnitTest;
+
+#define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \
+  static void NAME (void); \
+  \
+  const CoglUnitTest unit_test_##NAME = \
+  { #NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS, NAME }; \
+  \
+  static void NAME (void)
+
+#else /* ENABLE_UNIT_TESTS */
+
+#define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \
+  static inline void NAME (void)
+
+#endif /* ENABLE_UNIT_TESTS */
+
+#endif /* _TEST_UNIT_H_ */
diff --git a/test-fixtures/test-utils.c b/test-fixtures/test-utils.c
new file mode 100644
index 0000000..0855893
--- /dev/null
+++ b/test-fixtures/test-utils.c
@@ -0,0 +1,342 @@
+#include <config.h>
+
+#include <cogl/cogl.h>
+#include <stdlib.h>
+
+#include "test-unit.h"
+#include "test-utils.h"
+
+#define FB_WIDTH 512
+#define FB_HEIGHT 512
+
+static CoglBool cogl_test_is_verbose;
+
+CoglContext *test_ctx;
+CoglFramebuffer *test_fb;
+
+static CoglBool
+check_flags (TestFlags flags,
+             CoglRenderer *renderer)
+{
+  if (flags & TEST_REQUIREMENT_GL &&
+      cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL &&
+      cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL3)
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_NPOT &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_TEXTURE_3D &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_3D))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_TEXTURE_RECTANGLE &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_POINT_SPRITE &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_POINT_SPRITE))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_GLES2_CONTEXT &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLES2_CONTEXT))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_MAP_WRITE &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_GLSL &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLSL))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_OFFSCREEN &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_OFFSCREEN))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_REQUIREMENT_FENCE &&
+      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE))
+    {
+      return FALSE;
+    }
+
+  if (flags & TEST_KNOWN_FAILURE)
+    {
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+CoglBool
+is_boolean_env_set (const char *variable)
+{
+  char *val = getenv (variable);
+  CoglBool ret;
+
+  if (!val)
+    return FALSE;
+
+  if (g_ascii_strcasecmp (val, "1") == 0 ||
+      g_ascii_strcasecmp (val, "on") == 0 ||
+      g_ascii_strcasecmp (val, "true") == 0)
+    ret = TRUE;
+  else if (g_ascii_strcasecmp (val, "0") == 0 ||
+           g_ascii_strcasecmp (val, "off") == 0 ||
+           g_ascii_strcasecmp (val, "false") == 0)
+    ret = FALSE;
+  else
+    {
+      g_critical ("Spurious boolean environment variable value (%s=%s)",
+                  variable, val);
+      ret = TRUE;
+    }
+
+  return ret;
+}
+
+void
+test_utils_init (TestFlags requirement_flags,
+                 TestFlags known_failure_flags)
+{
+  static int counter = 0;
+  CoglError *error = NULL;
+  CoglOnscreen *onscreen = NULL;
+  CoglDisplay *display;
+  CoglRenderer *renderer;
+  CoglBool missing_requirement;
+  CoglBool known_failure;
+
+  if (counter != 0)
+    g_critical ("We don't support running more than one test at a time\n"
+                "in a single test run due to the state leakage that can\n"
+                "cause subsequent tests to fail.\n"
+                "\n"
+                "If you want to run all the tests you should run\n"
+                "$ make test-report");
+  counter++;
+
+  if (is_boolean_env_set ("COGL_TEST_VERBOSE") ||
+      is_boolean_env_set ("V"))
+    cogl_test_is_verbose = TRUE;
+
+  if (g_getenv ("G_DEBUG"))
+    {
+      char *debug = g_strconcat (g_getenv ("G_DEBUG"), ",fatal-warnings", NULL);
+      g_setenv ("G_DEBUG", debug, TRUE);
+      g_free (debug);
+    }
+  else
+    g_setenv ("G_DEBUG", "fatal-warnings", TRUE);
+
+  g_setenv ("COGL_X11_SYNC", "1", 0);
+
+  test_ctx = cogl_context_new (NULL, &error);
+  if (!test_ctx)
+    g_critical ("Failed to create a CoglContext: %s", error->message);
+
+  display = cogl_context_get_display (test_ctx);
+  renderer = cogl_display_get_renderer (display);
+
+  missing_requirement = !check_flags (requirement_flags, renderer);
+  known_failure = !check_flags (known_failure_flags, renderer);
+
+  if (is_boolean_env_set ("COGL_TEST_ONSCREEN"))
+    {
+      onscreen = cogl_onscreen_new (test_ctx, 640, 480);
+      test_fb = COGL_FRAMEBUFFER (onscreen);
+    }
+  else
+    {
+      CoglOffscreen *offscreen;
+      CoglTexture2D *tex = cogl_texture_2d_new_with_size (test_ctx,
+                                                          FB_WIDTH, FB_HEIGHT,
+                                                          COGL_PIXEL_FORMAT_ANY);
+      offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (tex));
+      test_fb = COGL_FRAMEBUFFER (offscreen);
+    }
+
+  if (!cogl_framebuffer_allocate (test_fb, &error))
+    g_critical ("Failed to allocate framebuffer: %s", error->message);
+
+  if (onscreen)
+    cogl_onscreen_show (onscreen);
+
+  cogl_framebuffer_clear4f (test_fb,
+                            COGL_BUFFER_BIT_COLOR |
+                            COGL_BUFFER_BIT_DEPTH |
+                            COGL_BUFFER_BIT_STENCIL,
+                            0, 0, 0, 1);
+
+  if (missing_requirement)
+    g_print ("WARNING: Missing required feature[s] for this test\n");
+  else if (known_failure)
+    g_print ("WARNING: Test is known to fail\n");
+}
+
+void
+test_utils_fini (void)
+{
+  if (test_fb)
+    cogl_object_unref (test_fb);
+
+  if (test_ctx)
+    cogl_object_unref (test_ctx);
+}
+
+static CoglBool
+compare_component (int a, int b)
+{
+  return ABS (a - b) <= 1;
+}
+
+void
+test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel,
+                                    uint32_t expected_pixel)
+{
+  /* Compare each component with a small fuzz factor */
+  if (!compare_component (screen_pixel[0], expected_pixel >> 24) ||
+      !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) ||
+      !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff) ||
+      !compare_component (screen_pixel[3], (expected_pixel >> 0) & 0xff))
+    {
+      uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel);
+      char *screen_pixel_string =
+        g_strdup_printf ("#%08x", screen_pixel_num);
+      char *expected_pixel_string =
+        g_strdup_printf ("#%08x", expected_pixel);
+
+      g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string);
+
+      g_free (screen_pixel_string);
+      g_free (expected_pixel_string);
+    }
+}
+
+void
+test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel)
+{
+  /* Compare each component with a small fuzz factor */
+  if (!compare_component (screen_pixel[0], expected_pixel >> 24) ||
+      !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) ||
+      !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff))
+    {
+      uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel);
+      char *screen_pixel_string =
+        g_strdup_printf ("#%06x", screen_pixel_num >> 8);
+      char *expected_pixel_string =
+        g_strdup_printf ("#%06x", expected_pixel >> 8);
+
+      g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string);
+
+      g_free (screen_pixel_string);
+      g_free (expected_pixel_string);
+    }
+}
+
+void
+test_utils_check_pixel (CoglFramebuffer *fb,
+                        int x, int y, uint32_t expected_pixel)
+{
+  uint8_t pixel[4];
+
+  cogl_framebuffer_read_pixels (fb,
+                                x, y, 1, 1,
+                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                pixel);
+
+  test_utils_compare_pixel (pixel, expected_pixel);
+}
+
+void
+test_utils_check_pixel_and_alpha (CoglFramebuffer *fb,
+                                  int x, int y, uint32_t expected_pixel)
+{
+  uint8_t pixel[4];
+
+  cogl_framebuffer_read_pixels (fb,
+                                x, y, 1, 1,
+                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                pixel);
+
+  test_utils_compare_pixel_and_alpha (pixel, expected_pixel);
+}
+
+void
+test_utils_check_pixel_rgb (CoglFramebuffer *fb,
+                            int x, int y, int r, int g, int b)
+{
+  test_utils_check_pixel (fb, x, y, (r << 24) | (g << 16) | (b << 8));
+}
+
+void
+test_utils_check_region (CoglFramebuffer *fb,
+                         int x, int y,
+                         int width, int height,
+                         uint32_t expected_rgba)
+{
+  uint8_t *pixels, *p;
+
+  pixels = p = g_malloc (width * height * 4);
+  cogl_framebuffer_read_pixels (fb,
+                                x,
+                                y,
+                                width,
+                                height,
+                                COGL_PIXEL_FORMAT_RGBA_8888,
+                                p);
+
+  /* Check whether the center of each division is the right color */
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        test_utils_compare_pixel (p, expected_rgba);
+        p += 4;
+      }
+
+  g_free (pixels);
+}
+
+CoglTexture *
+test_utils_create_color_texture (CoglContext *context,
+                                 uint32_t color)
+{
+  CoglTexture2D *tex_2d;
+
+  color = GUINT32_TO_BE (color);
+
+  tex_2d = cogl_texture_2d_new_from_data (context,
+                                          1, 1, /* width/height */
+                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                          4, /* rowstride */
+                                          (uint8_t *) &color,
+                                          NULL);
+
+  return COGL_TEXTURE (tex_2d);
+}
+
+CoglBool
+cogl_test_verbose (void)
+{
+  return cogl_test_is_verbose;
+}
diff --git a/test-fixtures/test-utils.h b/test-fixtures/test-utils.h
new file mode 100644
index 0000000..b5ecdd1
--- /dev/null
+++ b/test-fixtures/test-utils.h
@@ -0,0 +1,156 @@
+#ifndef _TEST_UTILS_H_
+#define _TEST_UTILS_H_
+
+#include <cogl/cogl.h>
+#include <glib.h>
+
+/* We don't really care about functions that are defined without a
+   header for the unit tests so we can just disable it here */
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-declarations"
+#endif
+
+typedef enum _TestFlags
+{
+  TEST_KNOWN_FAILURE = 1<<0,
+  TEST_REQUIREMENT_GL = 1<<1,
+  TEST_REQUIREMENT_NPOT = 1<<2,
+  TEST_REQUIREMENT_TEXTURE_3D = 1<<3,
+  TEST_REQUIREMENT_TEXTURE_RECTANGLE = 1<<4,
+  TEST_REQUIREMENT_POINT_SPRITE = 1<<5,
+  TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6,
+  TEST_REQUIREMENT_MAP_WRITE = 1<<7,
+  TEST_REQUIREMENT_GLSL = 1<<8,
+  TEST_REQUIREMENT_OFFSCREEN = 1<<9,
+  TEST_REQUIREMENT_FENCE = 1<<10
+} TestFlags;
+
+extern CoglContext *test_ctx;
+extern CoglFramebuffer *test_fb;
+
+void
+test_utils_init (TestFlags requirement_flags,
+                 TestFlags known_failure_flags);
+
+void
+test_utils_fini (void);
+
+/*
+ * test_utils_check_pixel:
+ * @framebuffer: The #CoglFramebuffer to read from
+ * @x: x co-ordinate of the pixel to test
+ * @y: y co-ordinate of the pixel to test
+ * @pixel: An integer of the form 0xRRGGBBAA representing the expected
+ *         pixel value
+ *
+ * This performs reads a pixel on the given cogl @framebuffer and
+ * asserts that it matches the given color. The alpha channel of the
+ * color is ignored. The pixels are converted to a string and compared
+ * with g_assert_cmpstr so that if the comparison fails then the
+ * assert will display a meaningful message
+ */
+void
+test_utils_check_pixel (CoglFramebuffer *framebuffer,
+                        int x, int y, uint32_t expected_pixel);
+
+/**
+ * @framebuffer: The #CoglFramebuffer to read from
+ * @x: x co-ordinate of the pixel to test
+ * @y: y co-ordinate of the pixel to test
+ * @pixel: An integer of the form 0xRRGGBBAA representing the expected
+ *         pixel value
+ *
+ * This performs reads a pixel on the given cogl @framebuffer and
+ * asserts that it matches the given color. The alpha channel is also
+ * checked unlike with test_utils_check_pixel(). The pixels are
+ * converted to a string and compared with g_assert_cmpstr so that if
+ * the comparison fails then the assert will display a meaningful
+ * message.
+ */
+void
+test_utils_check_pixel_and_alpha (CoglFramebuffer *fb,
+                                  int x, int y, uint32_t expected_pixel);
+
+/*
+ * test_utils_check_pixel:
+ * @framebuffer: The #CoglFramebuffer to read from
+ * @x: x co-ordinate of the pixel to test
+ * @y: y co-ordinate of the pixel to test
+ * @pixel: An integer of the form 0xrrggbb representing the expected pixel value
+ *
+ * This performs reads a pixel on the given cogl @framebuffer and
+ * asserts that it matches the given color. The alpha channel of the
+ * color is ignored. The pixels are converted to a string and compared
+ * with g_assert_cmpstr so that if the comparison fails then the
+ * assert will display a meaningful message
+ */
+void
+test_utils_check_pixel_rgb (CoglFramebuffer *framebuffer,
+                            int x, int y, int r, int g, int b);
+
+/*
+ * test_utils_check_region:
+ * @framebuffer: The #CoglFramebuffer to read from
+ * @x: x co-ordinate of the region to test
+ * @y: y co-ordinate of the region to test
+ * @width: width of the region to test
+ * @height: height of the region to test
+ * @pixel: An integer of the form 0xrrggbb representing the expected region color
+ *
+ * Performs a read pixel on the specified region of the given cogl
+ * @framebuffer and asserts that it matches the given color. The alpha
+ * channel of the color is ignored. The pixels are converted to a
+ * string and compared with g_assert_cmpstr so that if the comparison
+ * fails then the assert will display a meaningful message
+ */
+void
+test_utils_check_region (CoglFramebuffer *framebuffer,
+                         int x, int y,
+                         int width, int height,
+                         uint32_t expected_rgba);
+
+/*
+ * test_utils_compare_pixel:
+ * @screen_pixel: A pixel stored in memory
+ * @expected_pixel: The expected RGBA value
+ *
+ * Compares a pixel from a buffer to an expected value. The pixels are
+ * converted to a string and compared with g_assert_cmpstr so that if
+ * the comparison fails then the assert will display a meaningful
+ * message.
+ */
+void
+test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel);
+
+/*
+ * test_utils_compare_pixel_and_alpha:
+ * @screen_pixel: A pixel stored in memory
+ * @expected_pixel: The expected RGBA value
+ *
+ * Compares a pixel from a buffer to an expected value. This is
+ * similar to test_utils_compare_pixel() except that it doesn't ignore
+ * the alpha component.
+ */
+void
+test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel,
+                                    uint32_t expected_pixel);
+
+/*
+ * test_utils_create_color_texture:
+ * @context: A #CoglContext
+ * @color: A color to put in the texture
+ *
+ * Creates a 1x1-pixel RGBA texture filled with the given color.
+ */
+CoglTexture *
+test_utils_create_color_texture (CoglContext *context,
+                                 uint32_t color);
+
+/* cogl_test_verbose:
+ *
+ * Queries if the user asked for verbose output or not.
+ */
+CoglBool
+cogl_test_verbose (void);
+
+#endif /* _TEST_UTILS_H_ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f13c8a1..e23762a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,11 +1,23 @@
-SUBDIRS = conform micro-perf data
+SUBDIRS = conform
 
-DIST_SUBDIRS = conform micro-perf data
+if UNIT_TESTS
+SUBDIRS += unit
+endif
 
-EXTRA_DIST = README
+SUBDIRS += micro-perf data
 
+DIST_SUBDIRS = conform unit micro-perf data
+
+EXTRA_DIST = README test-launcher.sh run-tests.sh
+
+if UNIT_TESTS
+test conform:
+	( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+	( cd ./unit && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+else
 test conform:
 	( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+endif
 
 .PHONY: test conform
 
diff --git a/tests/config.env.in b/tests/config.env.in
new file mode 100644
index 0000000..d377756
--- /dev/null
+++ b/tests/config.env.in
@@ -0,0 +1,3 @@
+HAVE_GL=@HAVE_GL@
+HAVE_GLES1=@HAVE_GLES1@
+HAVE_GLES2=@HAVE_GLES2@
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
index e445741..182cc36 100644
--- a/tests/conform/Makefile.am
+++ b/tests/conform/Makefile.am
@@ -5,8 +5,6 @@ NULL =
 noinst_PROGRAMS = test-conformance
 
 common_sources = \
-	test-utils.h \
-	test-utils.c \
 	test-conform-main.c \
 	$(NULL)
 
@@ -94,7 +92,7 @@ wrappers: stamp-test-conformance
 stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c
 	@mkdir -p wrappers
 	@sed -n -e 's/^ \{1,\}ADD_TEST *( *\([a-zA-Z0-9_]\{1,\}\).*/\1/p' $(srcdir)/test-conform-main.c > unit-tests
-	@chmod +x test-launcher.sh
+	@chmod +x $(top_srcdir)/tests/test-launcher.sh
 	@( echo "/stamp-test-conformance" ; \
 	   echo "/test-conformance$(EXEEXT)" ; \
 	   echo "*.o" ; \
@@ -104,7 +102,7 @@ stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c
 	do \
 		unit=`basename $$i | sed -e s/_/-/g`; \
 		echo "  GEN    $$unit"; \
-		( echo "#!/bin/sh" ; echo "$(abs_builddir)/test-launcher.sh '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
+		( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-conformance$(EXEEXT) '' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
 		chmod +x $$unit$(SHEXT); \
 		echo "/$$unit$(SHEXT)" >> .gitignore; \
 	done \
@@ -129,6 +127,7 @@ BUILT_SOURCES = wrappers
 # testing (such as test-bitmask) will still compile
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
+	-I$(top_srcdir)/test-fixtures \
 	-I$(top_builddir)/cogl
 
 if !USE_GLIB
@@ -144,6 +143,7 @@ test_conformance_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
 test_conformance_LDADD = \
 	$(COGL_DEP_LIBS) \
 	$(top_builddir)/cogl/libcogl2.la \
+	$(top_builddir)/test-fixtures/libtest-fixtures.la \
 	$(LIBM)
 if !USE_GLIB
 test_conformance_LDADD += $(top_builddir)/deps/glib/libglib.la
@@ -151,7 +151,7 @@ endif
 test_conformance_LDFLAGS = -export-dynamic
 
 test: wrappers
-	@$(top_srcdir)/tests/conform/run-tests.sh $(abs_builddir)/config.env
+	@$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-conformance$(EXEEXT)
 
 # XXX: we could prevent the conformance test suite from running
 #      by simply defining this variable conditionally
diff --git a/tests/conform/config.env.in b/tests/conform/config.env.in
deleted file mode 100644
index d377756..0000000
--- a/tests/conform/config.env.in
+++ /dev/null
@@ -1,3 +0,0 @@
-HAVE_GL=@HAVE_GL@
-HAVE_GLES1=@HAVE_GLES1@
-HAVE_GLES2=@HAVE_GLES2@
diff --git a/tests/conform/run-tests.sh b/tests/conform/run-tests.sh
deleted file mode 100755
index 11e740a..0000000
--- a/tests/conform/run-tests.sh
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/bin/bash
-
-. $1
-
-set +m
-
-trap "" ERR
-trap "" SIGABRT
-trap "" SIGFPE
-trap "" SIGSEGV
-
-EXIT=0
-MISSING_FEATURE="WARNING: Missing required feature";
-KNOWN_FAILURE="WARNING: Test is known to fail";
-
-if test -f ./test-conformance; then
-  TEST_CONFORMANCE=./test-conformance
-elif test -f ./test-conformance.exe; then
-  TEST_CONFORMANCE=./test-conformance.exe
-fi
-
-echo "Key:"
-echo "ok = Test passed"
-echo "n/a = Driver is missing a feature required for the test"
-echo "FAIL = Unexpected failure"
-echo "fail = Test failed, but it was an expected failure"
-echo "PASS! = Unexpected pass"
-echo ""
-
-get_status()
-{
-  case $1 in
-      # Special value we use to indicate that the test failed
-      # but it was an expected failure so don't fail the
-      # overall test run as a result...
-      300)
-      echo -n "fail";;
-      # Special value we use to indicate that the test passed
-      # but we weren't expecting it to pass‽
-      400)
-      echo -n 'PASS!';;
-
-      # Special value to indicate the test is missing a required feature
-      500)
-      echo -n "n/a";;
-
-      0)
-      echo -n "ok";;
-
-      *)
-      echo -n "FAIL";;
-  esac
-}
-
-run_test()
-{
-  $($TEST_CONFORMANCE $1 &>.log)
-  TMP=$?
-  var_name=$2_result
-  eval $var_name=$TMP
-  if grep -q "$MISSING_FEATURE" .log; then
-    if test $TMP -ne 0; then
-      eval $var_name=500
-    else
-      eval $var_name=400
-    fi
-  elif grep -q "$KNOWN_FAILURE" .log; then
-    if test $TMP -ne 0; then
-      eval $var_name=300
-    else
-      eval $var_name=400
-    fi
-  else
-    if test $TMP -ne 0; then EXIT=$TMP; fi
-  fi
-}
-
-TITLE_FORMAT="%35s"
-printf $TITLE_FORMAT "Test"
-
-if test $HAVE_GL -eq 1; then
-  GL_FORMAT=" %6s %8s %7s %6s %6s"
-  printf "$GL_FORMAT" "GL+FF" "GL+ARBFP" "GL+GLSL" "GL-NPT" "GL3"
-fi
-if test $HAVE_GLES2 -eq 1; then
-  GLES2_FORMAT=" %6s %7s"
-  printf "$GLES2_FORMAT" "ES2" "ES2-NPT"
-fi
-
-echo ""
-echo ""
-
-for test in `cat unit-tests`
-do
-  export COGL_DEBUG=
-
-  if test $HAVE_GL -eq 1; then
-    export COGL_DRIVER=gl
-    export COGL_DEBUG=disable-glsl,disable-arbfp
-    run_test $test gl_ff
-
-    export COGL_DRIVER=gl
-    # NB: we can't explicitly disable fixed + glsl in this case since
-    # the arbfp code only supports fragment processing so we need either
-    # the fixed or glsl vertends
-    export COGL_DEBUG=
-    run_test $test gl_arbfp
-
-    export COGL_DRIVER=gl
-    export COGL_DEBUG=disable-fixed,disable-arbfp
-    run_test $test gl_glsl
-
-    export COGL_DRIVER=gl
-    export COGL_DEBUG=disable-npot-textures
-    run_test $test gl_npot
-
-    export COGL_DRIVER=gl3
-    export COGL_DEBUG=
-    run_test $test gl3
-  fi
-
-  if test $HAVE_GLES2 -eq 1; then
-    export COGL_DRIVER=gles2
-    export COGL_DEBUG=
-    run_test $test gles2
-
-    export COGL_DRIVER=gles2
-    export COGL_DEBUG=disable-npot-textures
-    run_test $test gles2_npot
-  fi
-
-  printf $TITLE_FORMAT "$test:"
-  if test $HAVE_GL -eq 1; then
-    printf "$GL_FORMAT" \
-      "`get_status $gl_ff_result`" \
-      "`get_status $gl_arbfp_result`" \
-      "`get_status $gl_glsl_result`" \
-      "`get_status $gl_npot_result`" \
-      "`get_status $gl3_result`"
-  fi
-  if test $HAVE_GLES2 -eq 1; then
-    printf "$GLES2_FORMAT" \
-      "`get_status $gles2_result`" \
-      "`get_status $gles2_npot_result`"
-  fi
-  echo ""
-done
-
-exit $EXIT
diff --git a/tests/conform/test-launcher.sh.in b/tests/conform/test-launcher.sh.in
deleted file mode 100755
index 1c29003..0000000
--- a/tests/conform/test-launcher.sh.in
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-UNIT_TEST=$1
-shift
-
-test -z ${UNIT_TEST} && {
-        echo "Usage: $0 UNIT_TEST"
-        exit 1
-}
-
-UNIT_TEST=`echo $UNIT_TEST|sed 's/-/_/g'`
-
-echo "Running: ./test-conformance ${UNIT_TEST} $@"
-echo ""
-if test -f @abs_builddir@/test-conformance; then
-  TEST_CONFORMANCE=@abs_builddir@/test-conformance
-elif test -f @abs_builddir@/test-conformance.exe; then
-  TEST_CONFORMANCE=@abs_builddir@/test-conformance.exe
-fi
-COGL_TEST_VERBOSE=1 $TEST_CONFORMANCE ${UNIT_TEST} "$@"
-exit_val=$?
-
-echo ""
-echo "NOTE: For debugging purposes, you can run this single test as follows:"
-echo "$ libtool --mode=execute \\"
-echo "          gdb --eval-command=\"b ${UNIT_TEST}\" \\"
-echo "          --args ./test-conformance ${UNIT_TEST}"
-echo "or:"
-echo "$ env G_SLICE=always-malloc \\"
-echo "  libtool --mode=execute \\"
-echo "          valgrind ./test-conformance ${UNIT_TEST}"
-
-exit $exit_val
diff --git a/tests/conform/test-utils.c b/tests/conform/test-utils.c
deleted file mode 100644
index b8f2dcd..0000000
--- a/tests/conform/test-utils.c
+++ /dev/null
@@ -1,339 +0,0 @@
-#include <cogl/cogl.h>
-#include <stdlib.h>
-
-#include "test-utils.h"
-
-#define FB_WIDTH 512
-#define FB_HEIGHT 512
-
-static CoglBool cogl_test_is_verbose;
-
-CoglContext *test_ctx;
-CoglFramebuffer *test_fb;
-
-static CoglBool
-check_flags (TestFlags flags,
-             CoglRenderer *renderer)
-{
-  if (flags & TEST_REQUIREMENT_GL &&
-      cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL &&
-      cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL3)
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_NPOT &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_NPOT))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_TEXTURE_3D &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_3D))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_TEXTURE_RECTANGLE &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RECTANGLE))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_POINT_SPRITE &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_POINT_SPRITE))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_GLES2_CONTEXT &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLES2_CONTEXT))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_MAP_WRITE &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_GLSL &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_GLSL))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_OFFSCREEN &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_OFFSCREEN))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_REQUIREMENT_FENCE &&
-      !cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE))
-    {
-      return FALSE;
-    }
-
-  if (flags & TEST_KNOWN_FAILURE)
-    {
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-CoglBool
-is_boolean_env_set (const char *variable)
-{
-  char *val = getenv (variable);
-  CoglBool ret;
-
-  if (!val)
-    return FALSE;
-
-  if (g_ascii_strcasecmp (val, "1") == 0 ||
-      g_ascii_strcasecmp (val, "on") == 0 ||
-      g_ascii_strcasecmp (val, "true") == 0)
-    ret = TRUE;
-  else if (g_ascii_strcasecmp (val, "0") == 0 ||
-           g_ascii_strcasecmp (val, "off") == 0 ||
-           g_ascii_strcasecmp (val, "false") == 0)
-    ret = FALSE;
-  else
-    {
-      g_critical ("Spurious boolean environment variable value (%s=%s)",
-                  variable, val);
-      ret = TRUE;
-    }
-
-  return ret;
-}
-
-void
-test_utils_init (TestFlags requirement_flags,
-                 TestFlags known_failure_flags)
-{
-  static int counter = 0;
-  CoglError *error = NULL;
-  CoglOnscreen *onscreen = NULL;
-  CoglDisplay *display;
-  CoglRenderer *renderer;
-  CoglBool missing_requirement;
-  CoglBool known_failure;
-
-  if (counter != 0)
-    g_critical ("We don't support running more than one test at a time\n"
-                "in a single test run due to the state leakage that can\n"
-                "cause subsequent tests to fail.\n"
-                "\n"
-                "If you want to run all the tests you should run\n"
-                "$ make test-report");
-  counter++;
-
-  if (is_boolean_env_set ("COGL_TEST_VERBOSE") ||
-      is_boolean_env_set ("V"))
-    cogl_test_is_verbose = TRUE;
-
-  if (g_getenv ("G_DEBUG"))
-    {
-      char *debug = g_strconcat (g_getenv ("G_DEBUG"), ",fatal-warnings", NULL);
-      g_setenv ("G_DEBUG", debug, TRUE);
-      g_free (debug);
-    }
-  else
-    g_setenv ("G_DEBUG", "fatal-warnings", TRUE);
-
-  g_setenv ("COGL_X11_SYNC", "1", 0);
-
-  test_ctx = cogl_context_new (NULL, &error);
-  if (!test_ctx)
-    g_critical ("Failed to create a CoglContext: %s", error->message);
-
-  display = cogl_context_get_display (test_ctx);
-  renderer = cogl_display_get_renderer (display);
-
-  missing_requirement = !check_flags (requirement_flags, renderer);
-  known_failure = !check_flags (known_failure_flags, renderer);
-
-  if (is_boolean_env_set ("COGL_TEST_ONSCREEN"))
-    {
-      onscreen = cogl_onscreen_new (test_ctx, 640, 480);
-      test_fb = COGL_FRAMEBUFFER (onscreen);
-    }
-  else
-    {
-      CoglOffscreen *offscreen;
-      CoglTexture2D *tex = cogl_texture_2d_new_with_size (test_ctx,
-                                                          FB_WIDTH, FB_HEIGHT,
-                                                          COGL_PIXEL_FORMAT_ANY);
-      offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (tex));
-      test_fb = COGL_FRAMEBUFFER (offscreen);
-    }
-
-  if (!cogl_framebuffer_allocate (test_fb, &error))
-    g_critical ("Failed to allocate framebuffer: %s", error->message);
-
-  if (onscreen)
-    cogl_onscreen_show (onscreen);
-
-  cogl_framebuffer_clear4f (test_fb,
-                            COGL_BUFFER_BIT_COLOR |
-                            COGL_BUFFER_BIT_DEPTH |
-                            COGL_BUFFER_BIT_STENCIL,
-                            0, 0, 0, 1);
-
-  if (missing_requirement)
-    g_print ("WARNING: Missing required feature[s] for this test\n");
-  else if (known_failure)
-    g_print ("WARNING: Test is known to fail\n");
-}
-
-void
-test_utils_fini (void)
-{
-  if (test_fb)
-    cogl_object_unref (test_fb);
-
-  if (test_ctx)
-    cogl_object_unref (test_ctx);
-}
-
-static CoglBool
-compare_component (int a, int b)
-{
-  return ABS (a - b) <= 1;
-}
-
-void
-test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel,
-                                    uint32_t expected_pixel)
-{
-  /* Compare each component with a small fuzz factor */
-  if (!compare_component (screen_pixel[0], expected_pixel >> 24) ||
-      !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) ||
-      !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff) ||
-      !compare_component (screen_pixel[3], (expected_pixel >> 0) & 0xff))
-    {
-      uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel);
-      char *screen_pixel_string =
-        g_strdup_printf ("#%08x", screen_pixel_num);
-      char *expected_pixel_string =
-        g_strdup_printf ("#%08x", expected_pixel);
-
-      g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string);
-
-      g_free (screen_pixel_string);
-      g_free (expected_pixel_string);
-    }
-}
-
-void
-test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel)
-{
-  /* Compare each component with a small fuzz factor */
-  if (!compare_component (screen_pixel[0], expected_pixel >> 24) ||
-      !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) ||
-      !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff))
-    {
-      uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel);
-      char *screen_pixel_string =
-        g_strdup_printf ("#%06x", screen_pixel_num >> 8);
-      char *expected_pixel_string =
-        g_strdup_printf ("#%06x", expected_pixel >> 8);
-
-      g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string);
-
-      g_free (screen_pixel_string);
-      g_free (expected_pixel_string);
-    }
-}
-
-void
-test_utils_check_pixel (CoglFramebuffer *fb,
-                        int x, int y, uint32_t expected_pixel)
-{
-  uint8_t pixel[4];
-
-  cogl_framebuffer_read_pixels (fb,
-                                x, y, 1, 1,
-                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                pixel);
-
-  test_utils_compare_pixel (pixel, expected_pixel);
-}
-
-void
-test_utils_check_pixel_and_alpha (CoglFramebuffer *fb,
-                                  int x, int y, uint32_t expected_pixel)
-{
-  uint8_t pixel[4];
-
-  cogl_framebuffer_read_pixels (fb,
-                                x, y, 1, 1,
-                                COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                pixel);
-
-  test_utils_compare_pixel_and_alpha (pixel, expected_pixel);
-}
-
-void
-test_utils_check_pixel_rgb (CoglFramebuffer *fb,
-                            int x, int y, int r, int g, int b)
-{
-  test_utils_check_pixel (fb, x, y, (r << 24) | (g << 16) | (b << 8));
-}
-
-void
-test_utils_check_region (CoglFramebuffer *fb,
-                         int x, int y,
-                         int width, int height,
-                         uint32_t expected_rgba)
-{
-  uint8_t *pixels, *p;
-
-  pixels = p = g_malloc (width * height * 4);
-  cogl_framebuffer_read_pixels (fb,
-                                x,
-                                y,
-                                width,
-                                height,
-                                COGL_PIXEL_FORMAT_RGBA_8888,
-                                p);
-
-  /* Check whether the center of each division is the right color */
-  for (y = 0; y < height; y++)
-    for (x = 0; x < width; x++)
-      {
-        test_utils_compare_pixel (p, expected_rgba);
-        p += 4;
-      }
-
-  g_free (pixels);
-}
-
-CoglTexture *
-test_utils_create_color_texture (CoglContext *context,
-                                 uint32_t color)
-{
-  CoglTexture2D *tex_2d;
-
-  color = GUINT32_TO_BE (color);
-
-  tex_2d = cogl_texture_2d_new_from_data (context,
-                                          1, 1, /* width/height */
-                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                          COGL_PIXEL_FORMAT_RGBA_8888_PRE,
-                                          4, /* rowstride */
-                                          (uint8_t *) &color,
-                                          NULL);
-
-  return COGL_TEXTURE (tex_2d);
-}
-
-CoglBool
-cogl_test_verbose (void)
-{
-  return cogl_test_is_verbose;
-}
diff --git a/tests/conform/test-utils.h b/tests/conform/test-utils.h
deleted file mode 100644
index 49dbe1b..0000000
--- a/tests/conform/test-utils.h
+++ /dev/null
@@ -1,155 +0,0 @@
-#ifndef _TEST_UTILS_H_
-#define _TEST_UTILS_H_
-
-#include <glib.h>
-
-/* We don't really care about functions that are defined without a
-   header for the unit tests so we can just disable it here */
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wmissing-declarations"
-#endif
-
-typedef enum _TestFlags
-{
-  TEST_KNOWN_FAILURE = 1<<0,
-  TEST_REQUIREMENT_GL = 1<<1,
-  TEST_REQUIREMENT_NPOT = 1<<2,
-  TEST_REQUIREMENT_TEXTURE_3D = 1<<3,
-  TEST_REQUIREMENT_TEXTURE_RECTANGLE = 1<<4,
-  TEST_REQUIREMENT_POINT_SPRITE = 1<<5,
-  TEST_REQUIREMENT_GLES2_CONTEXT = 1<<6,
-  TEST_REQUIREMENT_MAP_WRITE = 1<<7,
-  TEST_REQUIREMENT_GLSL = 1<<8,
-  TEST_REQUIREMENT_OFFSCREEN = 1<<9,
-  TEST_REQUIREMENT_FENCE = 1<<10
-} TestFlags;
-
-extern CoglContext *test_ctx;
-extern CoglFramebuffer *test_fb;
-
-void
-test_utils_init (TestFlags requirement_flags,
-                 TestFlags known_failure_flags);
-
-void
-test_utils_fini (void);
-
-/*
- * test_utils_check_pixel:
- * @framebuffer: The #CoglFramebuffer to read from
- * @x: x co-ordinate of the pixel to test
- * @y: y co-ordinate of the pixel to test
- * @pixel: An integer of the form 0xRRGGBBAA representing the expected
- *         pixel value
- *
- * This performs reads a pixel on the given cogl @framebuffer and
- * asserts that it matches the given color. The alpha channel of the
- * color is ignored. The pixels are converted to a string and compared
- * with g_assert_cmpstr so that if the comparison fails then the
- * assert will display a meaningful message
- */
-void
-test_utils_check_pixel (CoglFramebuffer *framebuffer,
-                        int x, int y, uint32_t expected_pixel);
-
-/**
- * @framebuffer: The #CoglFramebuffer to read from
- * @x: x co-ordinate of the pixel to test
- * @y: y co-ordinate of the pixel to test
- * @pixel: An integer of the form 0xRRGGBBAA representing the expected
- *         pixel value
- *
- * This performs reads a pixel on the given cogl @framebuffer and
- * asserts that it matches the given color. The alpha channel is also
- * checked unlike with test_utils_check_pixel(). The pixels are
- * converted to a string and compared with g_assert_cmpstr so that if
- * the comparison fails then the assert will display a meaningful
- * message.
- */
-void
-test_utils_check_pixel_and_alpha (CoglFramebuffer *fb,
-                                  int x, int y, uint32_t expected_pixel);
-
-/*
- * test_utils_check_pixel:
- * @framebuffer: The #CoglFramebuffer to read from
- * @x: x co-ordinate of the pixel to test
- * @y: y co-ordinate of the pixel to test
- * @pixel: An integer of the form 0xrrggbb representing the expected pixel value
- *
- * This performs reads a pixel on the given cogl @framebuffer and
- * asserts that it matches the given color. The alpha channel of the
- * color is ignored. The pixels are converted to a string and compared
- * with g_assert_cmpstr so that if the comparison fails then the
- * assert will display a meaningful message
- */
-void
-test_utils_check_pixel_rgb (CoglFramebuffer *framebuffer,
-                            int x, int y, int r, int g, int b);
-
-/*
- * test_utils_check_region:
- * @framebuffer: The #CoglFramebuffer to read from
- * @x: x co-ordinate of the region to test
- * @y: y co-ordinate of the region to test
- * @width: width of the region to test
- * @height: height of the region to test
- * @pixel: An integer of the form 0xrrggbb representing the expected region color
- *
- * Performs a read pixel on the specified region of the given cogl
- * @framebuffer and asserts that it matches the given color. The alpha
- * channel of the color is ignored. The pixels are converted to a
- * string and compared with g_assert_cmpstr so that if the comparison
- * fails then the assert will display a meaningful message
- */
-void
-test_utils_check_region (CoglFramebuffer *framebuffer,
-                         int x, int y,
-                         int width, int height,
-                         uint32_t expected_rgba);
-
-/*
- * test_utils_compare_pixel:
- * @screen_pixel: A pixel stored in memory
- * @expected_pixel: The expected RGBA value
- *
- * Compares a pixel from a buffer to an expected value. The pixels are
- * converted to a string and compared with g_assert_cmpstr so that if
- * the comparison fails then the assert will display a meaningful
- * message.
- */
-void
-test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel);
-
-/*
- * test_utils_compare_pixel_and_alpha:
- * @screen_pixel: A pixel stored in memory
- * @expected_pixel: The expected RGBA value
- *
- * Compares a pixel from a buffer to an expected value. This is
- * similar to test_utils_compare_pixel() except that it doesn't ignore
- * the alpha component.
- */
-void
-test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel,
-                                    uint32_t expected_pixel);
-
-/*
- * test_utils_create_color_texture:
- * @context: A #CoglContext
- * @color: A color to put in the texture
- *
- * Creates a 1x1-pixel RGBA texture filled with the given color.
- */
-CoglTexture *
-test_utils_create_color_texture (CoglContext *context,
-                                 uint32_t color);
-
-/* cogl_test_verbose:
- *
- * Queries if the user asked for verbose output or not.
- */
-CoglBool
-cogl_test_verbose (void);
-
-#endif /* _TEST_UTILS_H_ */
diff --git a/tests/run-tests.sh b/tests/run-tests.sh
new file mode 100755
index 0000000..a004943
--- /dev/null
+++ b/tests/run-tests.sh
@@ -0,0 +1,149 @@
+#!/bin/bash
+
+ENVIRONMENT_CONFIG=$1
+shift
+
+TEST_BINARY=$1
+shift
+
+. $ENVIRONMENT_CONFIG
+
+set +m
+
+trap "" ERR
+trap "" SIGABRT
+trap "" SIGFPE
+trap "" SIGSEGV
+
+EXIT=0
+MISSING_FEATURE="WARNING: Missing required feature";
+KNOWN_FAILURE="WARNING: Test is known to fail";
+
+echo "Key:"
+echo "ok = Test passed"
+echo "n/a = Driver is missing a feature required for the test"
+echo "FAIL = Unexpected failure"
+echo "fail = Test failed, but it was an expected failure"
+echo "PASS! = Unexpected pass"
+echo ""
+
+get_status()
+{
+  case $1 in
+      # Special value we use to indicate that the test failed
+      # but it was an expected failure so don't fail the
+      # overall test run as a result...
+      300)
+      echo -n "fail";;
+      # Special value we use to indicate that the test passed
+      # but we weren't expecting it to pass‽
+      400)
+      echo -n 'PASS!';;
+
+      # Special value to indicate the test is missing a required feature
+      500)
+      echo -n "n/a";;
+
+      0)
+      echo -n "ok";;
+
+      *)
+      echo -n "FAIL";;
+  esac
+}
+
+run_test()
+{
+  $($TEST_BINARY $1 &>.log)
+  TMP=$?
+  var_name=$2_result
+  eval $var_name=$TMP
+  if grep -q "$MISSING_FEATURE" .log; then
+    if test $TMP -ne 0; then
+      eval $var_name=500
+    else
+      eval $var_name=400
+    fi
+  elif grep -q "$KNOWN_FAILURE" .log; then
+    if test $TMP -ne 0; then
+      eval $var_name=300
+    else
+      eval $var_name=400
+    fi
+  else
+    if test $TMP -ne 0; then EXIT=$TMP; fi
+  fi
+}
+
+TITLE_FORMAT="%35s"
+printf $TITLE_FORMAT "Test"
+
+if test $HAVE_GL -eq 1; then
+  GL_FORMAT=" %6s %8s %7s %6s %6s"
+  printf "$GL_FORMAT" "GL+FF" "GL+ARBFP" "GL+GLSL" "GL-NPT" "GL3"
+fi
+if test $HAVE_GLES2 -eq 1; then
+  GLES2_FORMAT=" %6s %7s"
+  printf "$GLES2_FORMAT" "ES2" "ES2-NPT"
+fi
+
+echo ""
+echo ""
+
+for test in `cat unit-tests`
+do
+  export COGL_DEBUG=
+
+  if test $HAVE_GL -eq 1; then
+    export COGL_DRIVER=gl
+    export COGL_DEBUG=disable-glsl,disable-arbfp
+    run_test $test gl_ff
+
+    export COGL_DRIVER=gl
+    # NB: we can't explicitly disable fixed + glsl in this case since
+    # the arbfp code only supports fragment processing so we need either
+    # the fixed or glsl vertends
+    export COGL_DEBUG=
+    run_test $test gl_arbfp
+
+    export COGL_DRIVER=gl
+    export COGL_DEBUG=disable-fixed,disable-arbfp
+    run_test $test gl_glsl
+
+    export COGL_DRIVER=gl
+    export COGL_DEBUG=disable-npot-textures
+    run_test $test gl_npot
+
+    export COGL_DRIVER=gl3
+    export COGL_DEBUG=
+    run_test $test gl3
+  fi
+
+  if test $HAVE_GLES2 -eq 1; then
+    export COGL_DRIVER=gles2
+    export COGL_DEBUG=
+    run_test $test gles2
+
+    export COGL_DRIVER=gles2
+    export COGL_DEBUG=disable-npot-textures
+    run_test $test gles2_npot
+  fi
+
+  printf $TITLE_FORMAT "$test:"
+  if test $HAVE_GL -eq 1; then
+    printf "$GL_FORMAT" \
+      "`get_status $gl_ff_result`" \
+      "`get_status $gl_arbfp_result`" \
+      "`get_status $gl_glsl_result`" \
+      "`get_status $gl_npot_result`" \
+      "`get_status $gl3_result`"
+  fi
+  if test $HAVE_GLES2 -eq 1; then
+    printf "$GLES2_FORMAT" \
+      "`get_status $gles2_result`" \
+      "`get_status $gles2_npot_result`"
+  fi
+  echo ""
+done
+
+exit $EXIT
diff --git a/tests/test-launcher.sh b/tests/test-launcher.sh
new file mode 100755
index 0000000..e159e2e
--- /dev/null
+++ b/tests/test-launcher.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+TEST_BINARY=$1
+shift
+
+SYMBOL_PREFIX=$1
+shift
+
+UNIT_TEST=$1
+shift
+
+test -z ${UNIT_TEST} && {
+        echo "Usage: $0 UNIT_TEST"
+        exit 1
+}
+
+BINARY_NAME=`basename $TEST_BINARY`
+UNIT_TEST=`echo $UNIT_TEST|sed 's/-/_/g'`
+
+echo "Running: ./$BINARY_NAME ${UNIT_TEST} $@"
+echo ""
+COGL_TEST_VERBOSE=1 $TEST_BINARY ${UNIT_TEST} "$@"
+exit_val=$?
+
+if test $exit_val -eq 0; then
+  echo "OK"
+fi
+
+echo ""
+echo "NOTE: For debugging purposes, you can run this single test as follows:"
+echo "$ libtool --mode=execute \\"
+echo "          gdb --eval-command=\"start\" --eval-command=\"b ${UNIT_TEST#${SYMBOL_PREFIX}}\" \\"
+echo "          --args ./$BINARY_NAME ${UNIT_TEST}"
+echo "or:"
+echo "$ env G_SLICE=always-malloc \\"
+echo "  libtool --mode=execute \\"
+echo "          valgrind ./$BINARY_NAME ${UNIT_TEST}"
+
+exit $exit_val
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
new file mode 100644
index 0000000..09bf9a3
--- /dev/null
+++ b/tests/unit/Makefile.am
@@ -0,0 +1,97 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+NULL =
+
+noinst_PROGRAMS = test-unit
+
+test_unit_SOURCES = test-unit-main.c
+
+if OS_WIN32
+SHEXT =
+else
+SHEXT = $(EXEEXT)
+endif
+
+# For convenience, this provides a way to easily run individual unit tests:
+.PHONY: wrappers clean-wrappers
+
+wrappers: stamp-test-unit
+	@true
+stamp-test-unit: Makefile test-unit
+	@mkdir -p wrappers
+	source $(top_builddir)/cogl/libcogl2.la ; \
+	  $(NM) $(top_builddir)/cogl/.libs/"$$dlname"| \
+	  grep 'D unit_test_'|sed 's/.\+ D //' > unit-tests
+	@chmod +x $(top_srcdir)/tests/test-launcher.sh
+	@( echo "/stamp-test-unit" ; \
+	   echo "/test-unit$(EXEEXT)" ; \
+	   echo "*.o" ; \
+	   echo ".gitignore" ; \
+	   echo "unit-tests" ; ) > .gitignore
+	@for i in `cat unit-tests`; \
+	do \
+		unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \
+		echo "  GEN    $$unit"; \
+		( echo "#!/bin/sh" ; echo "$(top_srcdir)/tests/test-launcher.sh $(abs_builddir)/test-unit$(EXEEXT) 'unit_test_' '$$i' \"\$$@\"" ) > $$unit$(SHEXT) ; \
+		chmod +x $$unit$(SHEXT); \
+		echo "/$$unit$(SHEXT)" >> .gitignore; \
+	done \
+	&& echo timestamp > $(@F)
+
+clean-wrappers:
+	@for i in `cat unit-tests`; \
+	do \
+		unit=`echo $$i | sed -e s/_/-/g | sed s/unit-test-//`; \
+		echo "  RM     $$unit"; \
+		rm -f $$unit$(SHEXT) ; \
+	done \
+	&& rm -f unit-tests \
+	&& rm -f stamp-test-unit
+
+# NB: BUILT_SOURCES here a misnomer. We aren't building source, just inserting
+# a phony rule that will generate symlink scripts for running individual tests
+BUILT_SOURCES = wrappers
+
+# The include of the $(buildir)/cogl directory here is to make it so
+# that tests that directly include Cogl source code for whitebox
+# testing (such as test-bitmask) will still compile
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/test-fixtures \
+	-I$(top_builddir)/cogl
+
+if !USE_GLIB
+AM_CPPFLAGS += -I$(top_builddir)/deps/glib
+endif
+
+AM_CPPFLAGS += \
+	-DCOGL_DISABLE_DEPRECATED \
+	-DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\" \
+	-DCOGL_COMPILATION
+
+test_unit_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+test_unit_LDADD = \
+	$(COGL_DEP_LIBS) \
+	$(top_builddir)/cogl/libcogl2.la \
+	$(top_builddir)/test-fixtures/libtest-fixtures.la \
+	$(LIBM)
+if !USE_GLIB
+test_unit_LDADD += $(top_builddir)/deps/glib/libglib.la
+endif
+test_unit_LDFLAGS = -export-dynamic
+
+test: wrappers
+	@$(top_srcdir)/tests/run-tests.sh $(abs_builddir)/../config.env $(abs_builddir)/test-unit$(EXEEXT)
+
+# XXX: we could prevent the unit test suite from running
+#      by simply defining this variable conditionally
+TEST_PROGS = test-unit
+
+.PHONY: test
+
+DISTCLEANFILES = .gitignore
+
+# we override the clean-generic target to clean up the wrappers so
+# we cannot use CLEANFILES
+clean-generic: clean-wrappers
+	$(QUIET_RM)rm -f .log
diff --git a/tests/unit/test-unit-main.c b/tests/unit/test-unit-main.c
new file mode 100644
index 0000000..f78399a
--- /dev/null
+++ b/tests/unit/test-unit-main.c
@@ -0,0 +1,41 @@
+#include <config.h>
+
+#include <dlfcn.h>
+
+#include <test-fixtures/test-unit.h>
+
+int
+main (int argc, char **argv)
+{
+  const CoglUnitTest *unit_test;
+  int i;
+
+  if (argc != 2)
+    {
+      g_printerr ("usage %s UNIT_TEST\n", argv[0]);
+      exit (1);
+    }
+
+  /* Just for convenience in case people try passing the wrapper
+   * filenames for the UNIT_TEST argument we normalize '-' characters
+   * to '_' characters... */
+  for (i = 0; argv[1][i]; i++)
+    {
+      if (argv[1][i] == '-')
+        argv[1][i] = '_';
+    }
+
+  unit_test = dlsym (RTLD_DEFAULT, argv[1]);
+  if (!unit_test)
+    {
+      g_printerr ("Unknown test name \"%s\"\n", argv[1]);
+      return 1;
+    }
+
+  test_utils_init (unit_test->requirement_flags,
+                   unit_test->known_failure_flags);
+  unit_test->run ();
+  test_utils_fini ();
+
+  return 0;
+}
-- 
1.8.2.1



More information about the Cogl mailing list