[cogl/wip/port-clutter-tests] Starts porting Cogl conformance tests from Clutter



commit 4dd2269701438a4917286dab565f8e0acb6e9b03
Author: Robert Bragg <robert linux intel com>
Date:   Thu May 5 23:34:38 2011 +0100

    Starts porting Cogl conformance tests from Clutter
    
    This makes a start on porting the Cogl conformance tests that use to
    live in the Clutter repository to be standalone Cogl tests that no
    longer require a ClutterStage.
    
    The main thing is that this commit brings in the basic testing
    infrastructure we need, so no we can port more and more tests
    incrementally.

 .gitignore                                    |   27 ++
 Makefile.am                                   |    2 +-
 cogl/cogl-xlib-renderer.c                     |    5 +
 configure.ac                                  |    4 +
 tests/Makefile.am                             |   16 +
 tests/README                                  |   31 ++
 tests/conform/Makefile.am                     |  236 ++++++++++++++
 tests/conform/run-tests.sh                    |   12 +
 tests/conform/test-atlas-migration.c          |  133 ++++++++
 tests/conform/test-backface-culling.c         |  339 +++++++++++++++++++
 tests/conform/test-blend-strings.c            |  434 +++++++++++++++++++++++++
 tests/conform/test-conform-main.c             |  170 ++++++++++
 tests/conform/test-depth-test.c               |  291 +++++++++++++++++
 tests/conform/test-fixed.c                    |   18 +
 tests/conform/test-fixtures.c                 |   12 +
 tests/conform/test-just-vertex-shader.c       |  137 ++++++++
 tests/conform/test-launcher.sh.in             |   25 ++
 tests/conform/test-materials.c                |  285 ++++++++++++++++
 tests/conform/test-multitexture.c             |  206 ++++++++++++
 tests/conform/test-npot-texture.c             |  236 ++++++++++++++
 tests/conform/test-object.c                   |   86 +++++
 tests/conform/test-offscreen.c                |  167 ++++++++++
 tests/conform/test-path.c                     |  234 +++++++++++++
 tests/conform/test-pipeline-user-matrix.c     |  144 ++++++++
 tests/conform/test-pixel-buffer.c             |  330 +++++++++++++++++++
 tests/conform/test-premult.c                  |  367 +++++++++++++++++++++
 tests/conform/test-primitive.c                |  230 +++++++++++++
 tests/conform/test-readpixels.c               |  178 ++++++++++
 tests/conform/test-sub-texture.c              |  371 +++++++++++++++++++++
 tests/conform/test-texture-3d.c               |  230 +++++++++++++
 tests/conform/test-texture-get-set-data.c     |  166 ++++++++++
 tests/conform/test-texture-mipmaps.c          |  136 ++++++++
 tests/conform/test-texture-pixmap-x11.c       |  245 ++++++++++++++
 tests/conform/test-texture-rectangle.c        |  276 ++++++++++++++++
 tests/conform/test-utils.c                    |   80 +++++
 tests/conform/test-utils.h                    |   41 +++
 tests/conform/test-vertex-buffer-contiguous.c |  257 +++++++++++++++
 tests/conform/test-vertex-buffer-interleved.c |  162 +++++++++
 tests/conform/test-vertex-buffer-mutability.c |  198 +++++++++++
 tests/conform/test-viewport.c                 |  416 ++++++++++++++++++++++++
 tests/conform/test-wrap-modes.c               |  317 ++++++++++++++++++
 tests/data/Makefile.am                        |    3 +
 tests/data/valgrind.suppressions              |  173 ++++++++++
 43 files changed, 7425 insertions(+), 1 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 5a17a47..18ddd29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,3 +90,30 @@ TAGS
 *.rej
 .DS_Store
 .testlogs-*
+tests/conform/test-cogl-atlas-migration
+tests/conform/test-cogl-backface-culling
+tests/conform/test-cogl-blend-strings
+tests/conform/test-cogl-fixed
+tests/conform/test-cogl-just-vertex-shader
+tests/conform/test-cogl-materials
+tests/conform/test-cogl-multitexture
+tests/conform/test-cogl-npot-texture
+tests/conform/test-cogl-object
+tests/conform/test-cogl-offscreen
+tests/conform/test-cogl-path
+tests/conform/test-cogl-pipeline-user-matrix
+tests/conform/test-cogl-pixel-array
+tests/conform/test-cogl-premult
+tests/conform/test-cogl-primitive
+tests/conform/test-cogl-readpixels
+tests/conform/test-cogl-sub-texture
+tests/conform/test-cogl-texture-3d
+tests/conform/test-cogl-texture-get-set-data
+tests/conform/test-cogl-texture-mipmaps
+tests/conform/test-cogl-texture-pixmap-x11
+tests/conform/test-cogl-texture-rectangle
+tests/conform/test-cogl-vertex-buffer-contiguous
+tests/conform/test-cogl-vertex-buffer-interleved
+tests/conform/test-cogl-vertex-buffer-mutability
+tests/conform/test-cogl-viewport
+tests/conform/test-cogl-wrap-modes
diff --git a/Makefile.am b/Makefile.am
index 2806a54..dd34ca2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = cogl
+SUBDIRS = cogl tests
 
 if BUILD_COGL_PANGO
 SUBDIRS += cogl-pango
diff --git a/cogl/cogl-xlib-renderer.c b/cogl/cogl-xlib-renderer.c
index 8b4d3cc..92fe5ff 100644
--- a/cogl/cogl-xlib-renderer.c
+++ b/cogl/cogl-xlib-renderer.c
@@ -40,6 +40,8 @@
 #include <X11/Xlib.h>
 #include <X11/extensions/Xdamage.h>
 
+#include <stdlib.h>
+
 static char *_cogl_x11_display_name = NULL;
 static GList *_cogl_xlib_renderers = NULL;
 
@@ -163,6 +165,9 @@ _cogl_xlib_renderer_connect (CoglRenderer *renderer, GError **error)
   if (!assert_xlib_display (renderer, error))
     return FALSE;
 
+  if (getenv ("COGL_X11_SYNC"))
+    XSynchronize (xlib_renderer->xdpy, TRUE);
+
   /* Check whether damage events are supported on this display */
   if (!XDamageQueryExtension (xlib_renderer->xdpy,
                               &x11_renderer->damage_base,
diff --git a/configure.ac b/configure.ac
index bc7110d..c230638 100644
--- a/configure.ac
+++ b/configure.ac
@@ -968,6 +968,10 @@ doc/reference/cogl/cogl-docs.xml
 doc/reference/cogl-2.0-experimental/Makefile
 doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml
 examples/Makefile
+tests/Makefile
+tests/conform/Makefile
+tests/conform/test-launcher.sh
+tests/data/Makefile
 po/Makefile.in
 )
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..8bf6ba2
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS = conform data
+
+DIST_SUBDIRS = conform data
+
+EXTRA_DIST = README
+
+test conform:
+	( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+
+test-report full-report:
+	( cd ./conform && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $$?
+
+.PHONY: test conform test-report full-report
+
+# run make test as part of make check
+check-local: test
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..a8c01e7
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,31 @@
+Outline of test categories:
+
+The conform/ tests should be non-interactive unit-tests that verify a single
+feature is behaving as documented. See conform/ADDING_NEW_TESTS for more
+details.
+
+The micro-bench/ tests should be focused performance test, ideally testing a
+single metric. Please never forget that these tests are synthetic and if you
+are using them then you understand what metric is being tested. They probably
+don't reflect any real world application loads and the intention is that you
+use these tests once you have already determined the crux of your problem and
+need focused feedback that your changes are indeed improving matters. There is
+no exit status requirements for these tests, but they should give clear
+feedback as to their performance. If the framerate is the feedback metric, then
+the test should forcibly enable FPS debugging.
+
+The data/ directory contains optional data (like images) that can be
+referenced by a test.
+
+Other notes:
+
+â All tests should ideally include a detailed description in the source
+explaining exactly what the test is for, how the test was designed to work,
+and possibly a rationale for the approach taken for testing.
+
+â When running tests under Valgrind, you should follow the instructions
+available here:
+
+        http://live.gnome.org/Valgrind
+
+and also use the suppression file available inside the data/ directory.
diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am
new file mode 100644
index 0000000..54a4892
--- /dev/null
+++ b/tests/conform/Makefile.am
@@ -0,0 +1,236 @@
+include $(top_srcdir)/build/autotools/Makefile.am.silent
+
+NULL =
+
+noinst_PROGRAMS = test-conformance
+
+common_sources = \
+	test-utils.h \
+	test-utils.c \
+	test-conform-main.c \
+	$(NULL)
+
+unported_test_sources = \
+	test-backface-culling.c \
+	test-blend-strings.c \
+	test-fixed.c \
+	test-materials.c \
+	test-pipeline-user-matrix.c \
+	test-viewport.c \
+	test-multitexture.c \
+	test-npot-texture.c \
+	test-object.c \
+	test-offscreen.c \
+	test-path.c \
+	test-pixel-buffer.c \
+	test-premult.c \
+	test-readpixels.c \
+	test-sub-texture.c \
+	test-texture-3d.c \
+	test-texture-get-set-data.c \
+	test-texture-mipmaps.c \
+	test-texture-pixmap-x11.c \
+	test-texture-rectangle.c \
+	test-atlas-migration.c \
+	test-vertex-buffer-contiguous.c \
+	test-vertex-buffer-interleved.c \
+	test-vertex-buffer-mutability.c \
+	test-wrap-modes.c \
+	test-primitive.c \
+	test-just-vertex-shader.c \
+	$(NULL)
+
+test_sources = \
+	test-depth-test.c \
+	$(NULL)
+
+test_conformance_SOURCES = $(common_sources) $(test_sources)
+
+if OS_WIN32
+SHEXT =
+else
+SHEXT = $(EXEEXT)
+endif
+
+# For convenience, this provides a way to easily run individual unit tests:
+.PHONY: wrappers clean-wrappers
+
+#UNIT_TESTS = `./test-conformance -l -m thorough | $(GREP) '^/'`
+
+wrappers: stamp-test-conformance
+	@true
+stamp-test-conformance: Makefile $(srcdir)/test-conform-main.c
+	@mkdir -p wrappers
+	@sed -n \
+		-e 's/^ \{1,\}ADD_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+		-e 's/^ \{1,\}ADD_CONDITIONAL_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+		-e 's/^ \{1,\}ADD_TODO_TEST *(.*"\([^",]\{1,\}\)", *\([a-zA-Z0-9_]\{1,\}\).*/\/conform\1\/\2/p' \
+	$(srcdir)/test-conform-main.c > unit-tests
+	@chmod +x test-launcher.sh
+	@( echo "/stamp-test-conformance" ; \
+	   echo "/test-conformance" ; \
+	   echo "*.o" ; \
+	   echo "*.xml" ; \
+	   echo "*.html" ; \
+	   echo ".gitignore" ; \
+	   echo "unit-tests" ; \
+	   echo "/wrappers/" ) > .gitignore
+	@for i in `cat unit-tests`; \
+	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 "exec $(abs_builddir)/test-conformance$(EXEEXT) -p $$i \"\$$ \"" ) > wrappers/$$unit$(SHEXT) ; \
+		chmod +x $$unit$(SHEXT); \
+		chmod +x wrappers/$$unit$(SHEXT); \
+		echo "/$$unit$(SHEXT)" >> .gitignore; \
+	done \
+	&& echo timestamp > $(@F)
+
+clean-wrappers:
+	@for i in `cat unit-tests`; \
+	do \
+		unit=`basename $$i | sed -e s/_/-/g`; \
+		echo "  RM     $$unit"; \
+		rm -f $$unit$(SHEXT) ; \
+		rm -f wrappers/$$unit$(SHEXT) ; \
+	done \
+	&& rm -f unit-tests \
+	&& rm -f stamp-test-conformance
+
+# 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
+
+INCLUDES = -I$(top_srcdir)
+
+test_conformance_CPPFLAGS = \
+	-DG_DISABLE_SINGLE_INCLUDES \
+	-DCOGL_ENABLE_EXPERIMENTAL_API \
+	-DCOGL_DISABLE_DEPRECATED \
+	-DTESTS_DATADIR=\""$(top_srcdir)/tests/data"\"
+
+test_conformance_CFLAGS = -g3 -O0 $(COGL_DEP_CFLAGS) $(COGL_EXTRA_CFLAGS)
+test_conformance_LDADD = $(COGL_DEP_LIBS) $(top_builddir)/cogl/libcogl.la
+test_conformance_LDFLAGS = -export-dynamic
+
+test: wrappers
+	@$(top_srcdir)/tests/conform/run-tests.sh \
+	  ./test-conformance$(EXEEXT) -o test-report.xml
+
+test-verbose: wrappers
+	@$(top_srcdir)/tests/conform/run-tests.sh \
+	  ./test-conformance$(EXEEXT) -o test-report.xml --verbose
+
+GTESTER = gtester
+GTESTER_REPORT = gtester-report
+
+# XXX: we could prevent the conformance test suite from running
+#      by simply defining this variable conditionally
+TEST_PROGS = test-conformance
+
+.PHONY: test
+.PHONY: test-report perf-report full-report
+.PHONY: test-report-npot perf-report-npot full-report-npot
+
+# test-report: run tests and generate report
+# perf-report: run tests with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || { \
+	  export GTESTER_LOGDIR=`mktemp -d "$(srcdir)/.testlogs-XXXXXX"` ; \
+	  if test -d "$(top_srcdir)/.git"; then \
+	    export REVISION="`git describe`" ;  \
+	  else \
+	    export REVISION="$(VERSION) $(CLUTTER_RELEASE_STATUS)" ; \
+	  fi ; \
+	  export TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S%z` ; \
+	  case $@ in \
+	  test-report) test_options="-k";; \
+	  perf-report) test_options="-k -m=perf";; \
+	  full-report) test_options="-k -m=perf -m=slow";; \
+	  esac ; \
+	  $(top_srcdir)/tests/conform/run-tests.sh \
+	    ./test-conformance$(EXEEXT) \
+	    --verbose \
+	    $$test_options \
+	    -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ; \
+	  echo '<?xml version="1.0"?>'              > $  xml ; \
+	  echo '<report-collection>'               >> $  xml ; \
+	  echo '<info>'                            >> $  xml ; \
+	  echo '  <package>$(PACKAGE)</package>'   >> $  xml ; \
+	  echo '  <version>$(VERSION)</version>'   >> $  xml ; \
+	  echo "  <revision>$$REVISION</revision>" >> $  xml ; \
+	  echo "  <date>$$TIMESTAMP</date>"        >> $  xml ; \
+	  echo '</info>'                           >> $  xml ; \
+	  for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+	    sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $  xml ; \
+	  done ; \
+	  echo >> $  xml ; \
+	  echo '</report-collection>' >> $  xml ; \
+	  ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $  xml >$  html ; \
+	  rm -rf "$$GTESTER_LOGDIR" ; \
+	}
+
+# same as above, but with a wrapper that forcibly disables non-power of
+# two textures
+test-report-npot perf-report-npot full-report-npot:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || { \
+	  export COGL_DEBUG="$COGL_DEBUG,disable-npot-textures"; \
+	  export GTESTER_LOGDIR=`mktemp -d "$(srcdir)/.testlogs-XXXXXX"` ; \
+	  if test -d "$(top_srcdir)/.git"; then \
+	    export REVISION="`git describe`" ;  \
+	  else \
+	    export REVISION="$(VERSION) $(CLUTTER_RELEASE_STATUS)" ; \
+	  fi ; \
+	  export TIMESTAMP=`date +%Y-%m-%dT%H:%M:%S%z` ; \
+	  case $@ in \
+	  test-report-npot) test_options="-k";; \
+	  perf-report-npot) test_options="-k -m=perf";; \
+	  full-report-npot) test_options="-k -m=perf -m=slow";; \
+	  esac ; \
+	  $(top_srcdir)/tests/conform/run-tests.sh \
+	    ./test-conformance$(EXEEXT) \
+	    --verbose \
+	    $$test_options \
+	    -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ; \
+	  echo '<?xml version="1.0"?>'              > $  xml ; \
+	  echo '<report-collection>'               >> $  xml ; \
+	  echo '<info>'                            >> $  xml ; \
+	  echo '  <package>$(PACKAGE)</package>'   >> $  xml ; \
+	  echo '  <version>$(VERSION)</version>'   >> $  xml ; \
+	  echo "  <revision>$$REVISION</revision>" >> $  xml ; \
+	  echo "  <date>$$TIMESTAMP</date>"        >> $  xml ; \
+	  echo '</info>'                           >> $  xml ; \
+	  for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+	    sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $  xml ; \
+	  done ; \
+	  echo >> $  xml ; \
+	  echo '</report-collection>' >> $  xml ; \
+	  ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $  xml >$  html ; \
+	  rm -rf "$$GTESTER_LOGDIR" ; \
+	}
+
+XML_REPORTS = \
+	test-report.xml 	\
+	perf-report.xml 	\
+	full-report.xml 	\
+	test-report-npot.xml 	\
+	perf-report-npot.xml 	\
+	full-report-npot.xml
+
+HTML_REPORTS = \
+	test-report.html 	\
+	perf-report.html 	\
+	full-report.html 	\
+	test-report-npot.html 	\
+	perf-report-npot.html 	\
+	full-report-npot.html
+
+EXTRA_DIST = ADDING_NEW_TESTS test-launcher.sh.in run-tests.sh
+DISTCLEANFILES = test-launcher.sh .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 $(XML_REPORTS) $(HTML_REPORTS)
diff --git a/tests/conform/run-tests.sh b/tests/conform/run-tests.sh
new file mode 100755
index 0000000..1104953
--- /dev/null
+++ b/tests/conform/run-tests.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+BINARY=$1
+shift
+
+TMP=`./$BINARY -l -m thorough | grep '^/' | sed -e s/_/-/g`
+for i in $TMP
+do
+  TESTS="$TESTS wrappers/`basename $i`"
+done
+
+exec gtester "$@" $TESTS
diff --git a/tests/conform/test-atlas-migration.c b/tests/conform/test-atlas-migration.c
new file mode 100644
index 0000000..8b6cd98
--- /dev/null
+++ b/tests/conform/test-atlas-migration.c
@@ -0,0 +1,133 @@
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+#define N_TEXTURES 128
+
+#define OPACITY_FOR_ROW(y) \
+  (0xff - ((y) & 0xf) * 0x10)
+
+#define COLOR_FOR_SIZE(size) \
+  (colors + (size) % 3)
+
+static const ClutterColor colors[] =
+  { { 0xff, 0x00, 0x00, 0xff },
+    { 0x00, 0xff, 0x00, 0xff },
+    { 0x00, 0x00, 0xff, 0xff } };
+
+static CoglHandle
+create_texture (int size)
+{
+  CoglHandle texture;
+  const ClutterColor *color;
+  guint8 *data, *p;
+  int x, y;
+
+  /* Create a red, green or blue texture depending on the size */
+  color = COLOR_FOR_SIZE (size);
+
+  p = data = g_malloc (size * size * 4);
+
+  /* Fill the data with the color but fade the opacity out with
+     increasing y coordinates so that we can see the blending it the
+     atlas migration accidentally blends with garbage in the
+     texture */
+  for (y = 0; y < size; y++)
+    {
+      int opacity = OPACITY_FOR_ROW (y);
+
+      for (x = 0; x < size; x++)
+        {
+          /* Store the colors premultiplied */
+          p[0] = color->red * opacity / 255;
+          p[1] = color->green * opacity / 255;
+          p[2] = color->blue * opacity / 255;
+          p[3] = opacity;
+
+          p += 4;
+        }
+    }
+
+  texture = cogl_texture_new_from_data (size, /* width */
+                                        size, /* height */
+                                        COGL_TEXTURE_NONE, /* flags */
+                                        /* format */
+                                        COGL_PIXEL_FORMAT_RGBA_8888,
+                                        /* internal format */
+                                        COGL_PIXEL_FORMAT_RGBA_8888,
+                                        /* rowstride */
+                                        size * 4,
+                                        data);
+
+  g_free (data);
+
+  return texture;
+}
+
+static void
+verify_texture (CoglHandle texture, int size)
+{
+  guint8 *data, *p;
+  int x, y;
+  const ClutterColor *color;
+
+  color = COLOR_FOR_SIZE (size);
+
+  p = data = g_malloc (size * size * 4);
+
+  cogl_texture_get_data (texture,
+                         COGL_PIXEL_FORMAT_RGBA_8888,
+                         size * 4,
+                         data);
+
+  for (y = 0; y < size; y++)
+    {
+      int opacity = OPACITY_FOR_ROW (y);
+
+      for (x = 0; x < size; x++)
+        {
+          g_assert_cmpint (p[0], ==, color->red * opacity / 255);
+          g_assert_cmpint (p[1], ==, color->green * opacity / 255);
+          g_assert_cmpint (p[2], ==, color->blue * opacity / 255);
+          g_assert_cmpint (p[3], ==, opacity);
+
+          p += 4;
+        }
+    }
+
+  g_free (data);
+}
+
+void
+test_cogl_atlas_migration (TestUtilsGTestFixture *fixture,
+                           void *data)
+{
+  CoglHandle textures[N_TEXTURES];
+  int i, tex_num;
+
+  /* Create and destroy all of the textures a few times to increase
+     the chances that we'll end up reusing the buffers for previously
+     discarded atlases */
+  for (i = 0; i < 5; i++)
+    {
+      for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+        textures[tex_num] = create_texture (tex_num + 1);
+      for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+        cogl_object_unref (textures[tex_num]);
+    }
+
+  /* Create all the textures again */
+  for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+    textures[tex_num] = create_texture (tex_num + 1);
+
+  /* Verify that they all still have the right data */
+  for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+    verify_texture (textures[tex_num], tex_num + 1);
+
+  /* Destroy them all */
+  for (tex_num = 0; tex_num < N_TEXTURES; tex_num++)
+    cogl_object_unref (textures[tex_num]);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c
new file mode 100644
index 0000000..d4f3e0d
--- /dev/null
+++ b/tests/conform/test-backface-culling.c
@@ -0,0 +1,339 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#ifdef CLUTTER_COGL_HAS_GL
+
+/* Size the texture so that it is just off a power of two to enourage
+   it so use software tiling when NPOTs aren't available */
+#define TEXTURE_SIZE        257
+
+#else /* CLUTTER_COGL_HAS_GL */
+
+/* We can't use the funny-sized texture on GL ES because it will break
+   cogl_texture_polygon. However there is only one code path for
+   rendering quads so there is no need */
+#define TEXTURE_SIZE        32
+
+#endif /* CLUTTER_COGL_HAS_GL */
+
+/* Amount of pixels to skip off the top, bottom, left and right of the
+   texture when reading back the stage */
+#define TEST_INSET          4
+
+/* Size to actually render the texture at */
+#define TEXTURE_RENDER_SIZE 32
+
+typedef struct _TestState
+{
+  CoglHandle texture;
+  CoglHandle offscreen;
+  CoglHandle offscreen_tex;
+} TestState;
+
+static gboolean
+validate_part (int xnum, int ynum, gboolean shown)
+{
+  guchar *pixels, *p;
+  gboolean ret = TRUE;
+
+  pixels = g_malloc0 ((TEXTURE_RENDER_SIZE - TEST_INSET * 2)
+                      * (TEXTURE_RENDER_SIZE - TEST_INSET * 2) * 4);
+
+  /* Read the appropriate part but skip out a few pixels around the
+     edges */
+  cogl_read_pixels (xnum * TEXTURE_RENDER_SIZE + TEST_INSET,
+                    ynum * TEXTURE_RENDER_SIZE + TEST_INSET,
+                    TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+                    TEXTURE_RENDER_SIZE - TEST_INSET * 2,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixels);
+
+  /* Make sure every pixels is the appropriate color */
+  for (p = pixels;
+       p < pixels + ((TEXTURE_RENDER_SIZE - TEST_INSET * 2)
+                     * (TEXTURE_RENDER_SIZE - TEST_INSET * 2));
+       p += 4)
+    {
+      if (p[0] != (shown ? 255 : 0))
+        ret = FALSE;
+      if (p[1] !=  0)
+        ret = FALSE;
+      if (p[2] != 0)
+        ret = FALSE;
+    }
+
+  g_free (pixels);
+
+  return ret;
+}
+
+static void
+do_test_backface_culling (TestState *state)
+{
+  int i;
+  CoglHandle material = cogl_material_new ();
+
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+
+  cogl_set_backface_culling_enabled (TRUE);
+
+  cogl_push_matrix ();
+
+  /* Render the scene twice - once with backface culling enabled and
+     once without. The second time is translated so that it is below
+     the first */
+  for (i = 0; i < 2; i++)
+    {
+      float x1 = 0, x2, y1 = 0, y2 = (float)(TEXTURE_RENDER_SIZE);
+      CoglTextureVertex verts[4];
+
+      cogl_set_source (material);
+
+      memset (verts, 0, sizeof (verts));
+
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a front-facing texture */
+      cogl_material_set_layer (material, 0, state->texture);
+      cogl_rectangle (x1, y1, x2, y2);
+
+      x1 = x2;
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a front-facing texture with flipped texcoords */
+      cogl_material_set_layer (material, 0, state->texture);
+      cogl_rectangle_with_texture_coords (x1, y1, x2, y2,
+                                          1.0, 0.0, 0.0, 1.0);
+
+      x1 = x2;
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a back-facing texture */
+      cogl_material_set_layer (material, 0, state->texture);
+      cogl_rectangle (x2, y1, x1, y2);
+
+      x1 = x2;
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a front-facing texture polygon */
+      verts[0].x = x1;    verts[0].y = y2;
+      verts[1].x = x2;    verts[1].y = y2;
+      verts[2].x = x2;    verts[2].y = y1;
+      verts[3].x = x1;    verts[3].y = y1;
+      verts[0].tx = 0;    verts[0].ty = 0;
+      verts[1].tx = 1.0;  verts[1].ty = 0;
+      verts[2].tx = 1.0;  verts[2].ty = 1.0;
+      verts[3].tx = 0;    verts[3].ty = 1.0;
+      cogl_material_set_layer (material, 0, state->texture);
+      cogl_polygon (verts, 4, FALSE);
+
+      x1 = x2;
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a back-facing texture polygon */
+      verts[0].x = x1;    verts[0].y = y1;
+      verts[1].x = x2;    verts[1].y = y1;
+      verts[2].x = x2;    verts[2].y = y2;
+      verts[3].x = x1;    verts[3].y = y2;
+      verts[0].tx = 0;    verts[0].ty = 0;
+      verts[1].tx = 1.0;  verts[1].ty = 0;
+      verts[2].tx = 1.0;  verts[2].ty = 1.0;
+      verts[3].tx = 0;    verts[3].ty = 1.0;
+      cogl_material_set_layer (material, 0, state->texture);
+      cogl_polygon (verts, 4, FALSE);
+
+      x1 = x2;
+      x2 = x1 + (float)(TEXTURE_RENDER_SIZE);
+
+      /* Draw a regular rectangle (this should always show) */
+      cogl_set_source_color4f (1.0, 0, 0, 1.0);
+      cogl_rectangle (x1, y1, x2, y2);
+
+      /* The second time round draw beneath the first with backface
+         culling disabled */
+      cogl_translate (0, TEXTURE_RENDER_SIZE, 0);
+      cogl_set_backface_culling_enabled (FALSE);
+    }
+
+  cogl_handle_unref (material);
+
+  cogl_pop_matrix ();
+
+  /* Front-facing texture */
+  g_assert (validate_part (0, 0, TRUE));
+  /* Front-facing texture with flipped tex coords */
+  g_assert (validate_part (1, 0, TRUE));
+  /* Back-facing texture */
+  g_assert (validate_part (2, 0, FALSE));
+  /* Front-facing texture polygon */
+  g_assert (validate_part (3, 0, TRUE));
+  /* Back-facing texture polygon */
+  g_assert (validate_part (4, 0, FALSE));
+  /* Regular rectangle */
+  g_assert (validate_part (5, 0, TRUE));
+
+  /* Backface culling disabled - everything should be shown */
+
+  /* Front-facing texture */
+  g_assert (validate_part (0, 1, TRUE));
+  /* Front-facing texture with flipped tex coords */
+  g_assert (validate_part (1, 1, TRUE));
+  /* Back-facing texture */
+  g_assert (validate_part (2, 1, TRUE));
+  /* Front-facing texture polygon */
+  g_assert (validate_part (3, 1, TRUE));
+  /* Back-facing texture polygon */
+  g_assert (validate_part (4, 1, TRUE));
+  /* Regular rectangle */
+  g_assert (validate_part (5, 1, TRUE));
+
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglColor clr;
+  float stage_viewport[4];
+  CoglMatrix stage_projection;
+  CoglMatrix stage_modelview;
+
+  cogl_color_init_from_4ub (&clr, 0x00, 0x00, 0x00, 0xff);
+
+  do_test_backface_culling (state);
+
+  /* Since we are going to repeat the test rendering offscreen we clear the
+   * stage, just to minimize the chance of a some other bug causing us
+   * mistakenly reading back the results from the stage and giving a false
+   * posistive. */
+  cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL);
+
+  /*
+   * Now repeat the test but rendered to an offscreen framebuffer...
+   */
+
+  cogl_get_viewport (stage_viewport);
+  cogl_get_projection_matrix (&stage_projection);
+  cogl_get_modelview_matrix (&stage_modelview);
+
+  cogl_push_framebuffer (state->offscreen);
+
+  cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL);
+
+  cogl_set_viewport (stage_viewport[0],
+                     stage_viewport[1],
+                     stage_viewport[2],
+                     stage_viewport[3]);
+  cogl_set_projection_matrix (&stage_projection);
+  cogl_set_modelview_matrix (&stage_modelview);
+
+  do_test_backface_culling (state);
+
+  cogl_pop_framebuffer ();
+
+  /* Incase we want feedback of what was drawn offscreen we draw it
+   * to the stage... */
+  cogl_set_source_texture (state->offscreen_tex);
+  cogl_rectangle (0, 0, stage_viewport[2], stage_viewport[3]);
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+static CoglHandle
+make_texture (void)
+{
+  guchar *tex_data, *p;
+  CoglHandle tex;
+
+  tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
+
+  for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;)
+    {
+      *(--p) = 255;
+      *(--p) = 0;
+      *(--p) = 0;
+      *(--p) = 255;
+    }
+
+  tex = cogl_texture_new_from_data (TEXTURE_SIZE,
+                                    TEXTURE_SIZE,
+                                    COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    TEXTURE_SIZE * 4,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  return tex;
+}
+
+void
+test_cogl_backface_culling (TestUtilsGTestFixture *fixture,
+                            void *data)
+{
+  TestState state;
+  CoglHandle tex;
+  ClutterActor *stage;
+  float stage_width;
+  float stage_height;
+  const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+  clutter_actor_get_size (stage, &stage_width, &stage_height);
+
+  state.offscreen = COGL_INVALID_HANDLE;
+
+  state.texture = make_texture ();
+
+  tex = cogl_texture_new_with_size (stage_width, stage_height,
+                                    COGL_TEXTURE_NO_SLICING,
+                                    COGL_PIXEL_FORMAT_ANY); /* internal fmt */
+  state.offscreen = cogl_offscreen_new_to_texture (tex);
+  state.offscreen_tex = tex;
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  cogl_handle_unref (state.offscreen);
+  cogl_handle_unref (state.offscreen_tex);
+  cogl_handle_unref (state.texture);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-blend-strings.c b/tests/conform/test-blend-strings.c
new file mode 100644
index 0000000..77d1dd6
--- /dev/null
+++ b/tests/conform/test-blend-strings.c
@@ -0,0 +1,434 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR)   ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR)  ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+#define BLEND_CONSTANT_UNUSED 0xDEADBEEF
+#define TEX_CONSTANT_UNUSED   0xDEADBEEF
+
+typedef struct _TestState
+{
+  ClutterGeometry stage_geom;
+} TestState;
+
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+
+  if (g_test_verbose ())
+    g_print ("  expected = %x, %x, %x, %x\n",
+             r, g, b, a);
+  /* FIXME - allow for hardware in-precision */
+  g_assert_cmpint (pixel[RED], ==, r);
+  g_assert_cmpint (pixel[GREEN], ==, g);
+  g_assert_cmpint (pixel[BLUE], ==, b);
+
+  /* FIXME
+   * We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+  /* g_assert (pixel[ALPHA] == a); */
+}
+
+static void
+test_blend (TestState *state,
+            int x,
+            int y,
+            guint32 src_color,
+            guint32 dst_color,
+            const char *blend_string,
+            guint32 blend_constant,
+            guint32 expected_result)
+{
+  /* src color */
+  guint8 Sr = MASK_RED (src_color);
+  guint8 Sg = MASK_GREEN (src_color);
+  guint8 Sb = MASK_BLUE (src_color);
+  guint8 Sa = MASK_ALPHA (src_color);
+  /* dest color */
+  guint8 Dr = MASK_RED (dst_color);
+  guint8 Dg = MASK_GREEN (dst_color);
+  guint8 Db = MASK_BLUE (dst_color);
+  guint8 Da = MASK_ALPHA (dst_color);
+  /* blend constant - when applicable */
+  guint8 Br = MASK_RED (blend_constant);
+  guint8 Bg = MASK_GREEN (blend_constant);
+  guint8 Bb = MASK_BLUE (blend_constant);
+  guint8 Ba = MASK_ALPHA (blend_constant);
+  CoglColor blend_const_color;
+
+  CoglHandle material;
+  gboolean status;
+  GError *error = NULL;
+  GLubyte pixel[4];
+  GLint y_off;
+  GLint x_off;
+
+  /* First write out the destination color without any blending... */
+  material = cogl_material_new ();
+  cogl_material_set_color4ub (material, Dr, Dg, Db, Da);
+  cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+  cogl_set_source (material);
+  cogl_rectangle (x * QUAD_WIDTH,
+                  y * QUAD_WIDTH,
+                  x * QUAD_WIDTH + QUAD_WIDTH,
+                  y * QUAD_WIDTH + QUAD_WIDTH);
+  cogl_handle_unref (material);
+
+  /*
+   * Now blend a rectangle over our well defined destination:
+   */
+
+  material = cogl_material_new ();
+  cogl_material_set_color4ub (material, Sr, Sg, Sb, Sa);
+
+  status = cogl_material_set_blend (material, blend_string, &error);
+  if (!status)
+    {
+      /* It's not strictly a test failure; you need a more capable GPU or
+       * driver to test this blend string. */
+      g_debug ("Failed to test blend string %s: %s",
+               blend_string, error->message);
+    }
+
+  cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba);
+  cogl_material_set_blend_constant (material, &blend_const_color);
+
+  cogl_set_source (material);
+  cogl_rectangle (x * QUAD_WIDTH,
+                  y * QUAD_WIDTH,
+                  x * QUAD_WIDTH + QUAD_WIDTH,
+                  y * QUAD_WIDTH + QUAD_WIDTH);
+  cogl_handle_unref (material);
+
+  /* See what we got... */
+
+  y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+  x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+  cogl_read_pixels (x_off, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    {
+      g_print ("test_blend (%d, %d):\n%s\n", x, y, blend_string);
+      g_print ("  src color = %02x, %02x, %02x, %02x\n", Sr, Sg, Sb, Sa);
+      g_print ("  dst color = %02x, %02x, %02x, %02x\n", Dr, Dg, Db, Da);
+      if (blend_constant != BLEND_CONSTANT_UNUSED)
+        g_print ("  blend constant = %02x, %02x, %02x, %02x\n",
+                 Br, Bg, Bb, Ba);
+      else
+        g_print ("  blend constant = UNUSED\n");
+      g_print ("  result = %x, %x, %x, %x\n",
+               pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+    }
+
+  check_pixel (pixel, expected_result);
+}
+
+static CoglHandle
+make_texture (guint32 color)
+{
+  guchar *tex_data, *p;
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+  CoglHandle tex;
+
+  tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
+
+  for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
+    {
+      *(--p) = a;
+      *(--p) = b;
+      *(--p) = g;
+      *(--p) = r;
+    }
+
+  /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here
+   * since we don't want to allow Cogl to premultiply our data. */
+  tex = cogl_texture_new_from_data (QUAD_WIDTH,
+                                    QUAD_WIDTH,
+                                    COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    QUAD_WIDTH * 4,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  return tex;
+}
+
+static void
+test_tex_combine (TestState *state,
+                  int x,
+                  int y,
+                  guint32 tex0_color,
+                  guint32 tex1_color,
+                  guint32 combine_constant,
+                  const char *combine_string,
+                  guint32 expected_result)
+{
+  CoglHandle tex0, tex1;
+
+  /* combine constant - when applicable */
+  guint8 Cr = MASK_RED (combine_constant);
+  guint8 Cg = MASK_GREEN (combine_constant);
+  guint8 Cb = MASK_BLUE (combine_constant);
+  guint8 Ca = MASK_ALPHA (combine_constant);
+  CoglColor combine_const_color;
+
+  CoglHandle material;
+  gboolean status;
+  GError *error = NULL;
+  GLubyte pixel[4];
+  GLint y_off;
+  GLint x_off;
+
+
+  tex0 = make_texture (tex0_color);
+  tex1 = make_texture (tex1_color);
+
+  material = cogl_material_new ();
+
+  cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80);
+  cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+
+  cogl_material_set_layer (material, 0, tex0);
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA = REPLACE (TEXTURE)", NULL);
+
+  cogl_material_set_layer (material, 1, tex1);
+  status = cogl_material_set_layer_combine (material, 1,
+                                            combine_string, &error);
+  if (!status)
+    {
+      /* It's not strictly a test failure; you need a more capable GPU or
+       * driver to test this texture combine string. */
+      g_debug ("Failed to test texture combine string %s: %s",
+               combine_string, error->message);
+    }
+
+  cogl_color_init_from_4ub (&combine_const_color, Cr, Cg, Cb, Ca);
+  cogl_material_set_layer_combine_constant (material, 1, &combine_const_color);
+
+  cogl_set_source (material);
+  cogl_rectangle (x * QUAD_WIDTH,
+                  y * QUAD_WIDTH,
+                  x * QUAD_WIDTH + QUAD_WIDTH,
+                  y * QUAD_WIDTH + QUAD_WIDTH);
+
+  cogl_handle_unref (material);
+  cogl_handle_unref (tex0);
+  cogl_handle_unref (tex1);
+
+  /* See what we got... */
+
+  y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+  x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+  cogl_read_pixels (x_off, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    {
+      g_print ("test_tex_combine (%d, %d):\n%s\n", x, y, combine_string);
+      g_print ("  texture 0 color = 0x%08lX\n", (unsigned long)tex0_color);
+      g_print ("  texture 1 color = 0x%08lX\n", (unsigned long)tex1_color);
+      if (combine_constant != TEX_CONSTANT_UNUSED)
+        g_print ("  combine constant = %02x, %02x, %02x, %02x\n",
+                 Cr, Cg, Cb, Ca);
+      else
+        g_print ("  combine constant = UNUSED\n");
+      g_print ("  result = %02x, %02x, %02x, %02x\n",
+               pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+    }
+
+  check_pixel (pixel, expected_result);
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  test_blend (state, 0, 0, /* position */
+              0xff0000ff, /* src */
+              0xffffffff, /* dst */
+              "RGBA = ADD (SRC_COLOR, 0)",
+              BLEND_CONSTANT_UNUSED,
+              0xff0000ff); /* expected */
+
+  test_blend (state, 1, 0, /* position */
+              0x11223344, /* src */
+              0x11223344, /* dst */
+              "RGBA = ADD (SRC_COLOR, DST_COLOR)",
+              BLEND_CONSTANT_UNUSED,
+              0x22446688); /* expected */
+
+  test_blend (state, 2, 0, /* position */
+              0x80808080, /* src */
+              0xffffffff, /* dst */
+              "RGBA = ADD (SRC_COLOR * (CONSTANT), 0)",
+              0x80808080, /* constant (RGBA all = 0.5 when normalized) */
+              0x40404040); /* expected */
+
+  test_blend (state, 3, 0, /* position */
+              0x80000080, /* src (alpha = 0.5 when normalized) */
+              0x40000000, /* dst */
+              "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A]),"
+              "            DST_COLOR * (1-SRC_COLOR[A]))",
+              BLEND_CONSTANT_UNUSED,
+              0x60000040); /* expected */
+
+  /* XXX:
+   * For all texture combine tests tex0 will use a combine mode of
+   * "RGBA = REPLACE (TEXTURE)"
+   */
+
+  test_tex_combine (state, 4, 0, /* position */
+                    0x11111111, /* texture 0 color */
+                    0x22222222, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = ADD (PREVIOUS, TEXTURE)", /* tex combine */
+                    0x33333333); /* expected */
+
+  test_tex_combine (state, 5, 0, /* position */
+                    0x40404040, /* texture 0 color */
+                    0x80808080, /* texture 1 color (RGBA all = 0.5) */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */
+                    0x20202020); /* expected */
+
+  test_tex_combine (state, 6, 0, /* position */
+                    0xffffff80, /* texture 0 color (alpha = 0.5) */
+                    0xDEADBE40, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGB = REPLACE (PREVIOUS)"
+                    "A = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */
+                    0xffffff20); /* expected */
+
+  /* XXX: we are assuming test_tex_combine creates a material with
+   * a color of 0x80808080 (i.e. the "PRIMARY" color) */
+  test_tex_combine (state, 7, 0, /* position */
+                    0xffffff80, /* texture 0 color (alpha = 0.5) */
+                    0xDEADBE20, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGB = REPLACE (PREVIOUS)"
+                    "A = MODULATE (PRIMARY, TEXTURE)", /* tex combine */
+                    0xffffff10); /* expected */
+
+  test_tex_combine (state, 8, 0, /* position */
+                    0x11111111, /* texture 0 color */
+                    0x22222222, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = ADD (PREVIOUS, 1-TEXTURE)", /* tex combine */
+                    0xeeeeeeee); /* expected */
+
+  /* this is again assuming a primary color of 0x80808080 */
+  test_tex_combine (state, 9, 0, /* position */
+                    0x10101010, /* texture 0 color */
+                    0x20202020, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = INTERPOLATE (PREVIOUS, TEXTURE, PRIMARY)",
+                    0x18181818); /* expected */
+
+#if 0 /* using TEXTURE_N appears to be broken in cogl-blend-string.c */
+  test_tex_combine (state, 0, 1, /* position */
+                    0xDEADBEEF, /* texture 0 color (not used) */
+                    0x11223344, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = ADD (TEXTURE_1, TEXTURE)", /* tex combine */
+                    0x22446688); /* expected */
+#endif
+
+  test_tex_combine (state, 1, 1, /* position */
+                    0x21314151, /* texture 0 color */
+                    0x99999999, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = ADD_SIGNED (PREVIOUS, TEXTURE)", /* tex combine */
+                    0x3a4a5a6a); /* expected */
+
+  test_tex_combine (state, 2, 1, /* position */
+                    0xfedcba98, /* texture 0 color */
+                    0x11111111, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGBA = SUBTRACT (PREVIOUS, TEXTURE)", /* tex combine */
+                    0xedcba987); /* expected */
+
+  test_tex_combine (state, 3, 1, /* position */
+                    0x8899aabb, /* texture 0 color */
+                    0xbbaa9988, /* texture 1 color */
+                    TEX_CONSTANT_UNUSED,
+                    "RGB = DOT3_RGBA (PREVIOUS, TEXTURE)"
+                    "A = REPLACE (PREVIOUS)",
+                    0x2a2a2abb); /* expected */
+
+  /* Comment this out if you want visual feedback for what this test paints */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_blend_strings (TestUtilsGTestFixture *fixture,
+                         void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c
new file mode 100644
index 0000000..1dd9b68
--- /dev/null
+++ b/tests/conform/test-conform-main.c
@@ -0,0 +1,170 @@
+#include "config.h"
+
+#include <cogl/cogl.h>
+
+#include <glib.h>
+#include <locale.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+#if 0
+void
+skip_init (TestUtilsGTestFixture *fixture,
+           const void *data)
+{
+  /* void */
+}
+
+static void
+skip_test (TestUtilsGTestFixture *fixture,
+           const void *data)
+{
+  /* void */
+}
+
+void
+skip_fini (TestUtilsGTestFixture *fixture,
+           const void *data)
+{
+  /* void */
+}
+#endif
+
+static void
+run_todo_test (TestUtilsGTestFixture *fixture,
+               void *data)
+{
+#ifdef G_OS_UNIX
+  TestUtilsSharedState *state = data;
+
+  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
+    {
+      state->todo_func (fixture, data);
+      exit (0);
+    }
+
+  g_test_trap_assert_failed ();
+#endif
+}
+
+void
+verify_failure (TestUtilsGTestFixture *fixture,
+                void *data)
+{
+  g_assert (FALSE);
+}
+
+static TestUtilsSharedState *shared_state = NULL;
+
+/* This is a bit of sugar for adding new conformance tests:
+ *
+ * - It adds an extern function definition just to save maintaining a header
+ *   that lists test entry points.
+ * - It sets up callbacks for a fixture, which lets us share initialization
+ *   code between tests. (see test-utils.c)
+ * - It passes in a shared data pointer that is initialised once in main(),
+ *   that gets passed to the fixture setup and test functions. (See the
+ *   definition in test-utils.h)
+ */
+#define ADD_TEST(NAMESPACE, FUNC)            G_STMT_START {             \
+  extern void FUNC (TestUtilsGTestFixture *, void *);                   \
+  g_test_add ("/conform" NAMESPACE "/" #FUNC,                           \
+	      TestUtilsGTestFixture,                                    \
+	      shared_state, /* data argument for test */                \
+	      test_utils_init,                                          \
+	      (void *)(FUNC),                                           \
+	      test_utils_fini);    } G_STMT_END
+
+/* this is a macro that conditionally executes a test if CONDITION
+ * evaluates to TRUE; otherwise, it will put the test under the
+ * "/skip" namespace and execute a dummy function that will always
+ * pass.
+ */
+#define ADD_CONDITIONAL_TEST(CONDITION, NAMESPACE, FUNC)   G_STMT_START {   \
+  if (!(CONDITION)) {                                                       \
+    g_test_add ("/skipped" NAMESPACE "/" #FUNC,                             \
+                TestUtilsGTestFixture,                                      \
+                shared_state, /* data argument for test */                  \
+                skip_init,                                                  \
+                skip_test,                                                  \
+                skip_fini);                                                 \
+  } else { ADD_TEST (NAMESPACE, FUNC); }     } G_STMT_END
+
+#define ADD_TODO_TEST(NAMESPACE, FUNC)              G_STMT_START {          \
+   extern void FUNC (TestUtilsGTestFixture *, void *);                      \
+   shared_state->todo_func = FUNC;                                          \
+   g_test_add ("/todo" NAMESPACE "/" #FUNC,                                 \
+              TestUtilsGTestFixture,                                        \
+              shared_state,                                                 \
+              test_utils_init,                                              \
+              (void *)(run_todo_test),                                      \
+              test_utils_fini);    } G_STMT_END
+
+#define UNPORTED_TEST(NAMESPACE, FUNC)
+
+gchar *
+clutter_test_get_data_file (const gchar *filename)
+{
+  return g_build_filename (TESTS_DATADIR, filename, NULL);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s";);
+
+  /* Initialise the state you need to share with everything.
+   */
+  shared_state = g_new0 (TestUtilsSharedState, 1);
+  shared_state->argc_addr = &argc;
+  shared_state->argv_addr = &argv;
+
+  /* This file is run through a sed script during the make step so the
+   * lines containing the tests need to be formatted on a single line
+   * each. To comment out a test use the SKIP or TODO macros. Using
+   * #if 0 would break the script. */
+
+  /* sanity check for the test suite itself */
+  ADD_TODO_TEST ("/suite", verify_failure);
+
+  UNPORTED_TEST ("/cogl", test_cogl_object);
+  UNPORTED_TEST ("/cogl", test_cogl_fixed);
+  UNPORTED_TEST ("/cogl", test_cogl_backface_culling);
+  UNPORTED_TEST ("/cogl", test_cogl_materials);
+  UNPORTED_TEST ("/cogl", test_cogl_pipeline_user_matrix);
+  UNPORTED_TEST ("/cogl", test_cogl_blend_strings);
+  UNPORTED_TEST ("/cogl", test_cogl_premult);
+  UNPORTED_TEST ("/cogl", test_cogl_readpixels);
+  UNPORTED_TEST ("/cogl", test_cogl_path);
+  ADD_TEST ("/cogl", test_cogl_depth_test);
+
+  UNPORTED_TEST ("/cogl/texture", test_cogl_npot_texture);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_multitexture);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_texture_mipmaps);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_sub_texture);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_pixel_array);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_texture_rectangle);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_texture_3d);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_wrap_modes);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_texture_pixmap_x11);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_texture_get_set_data);
+  UNPORTED_TEST ("/cogl/texture", test_cogl_atlas_migration);
+
+  UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_contiguous);
+  UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved);
+  UNPORTED_TEST ("/cogl/vertex-buffer", test_cogl_vertex_buffer_mutability);
+
+  UNPORTED_TEST ("/cogl/vertex-array", test_cogl_primitive);
+
+  UNPORTED_TEST ("/cogl/shaders", test_cogl_just_vertex_shader);
+
+  /* left to the end because they aren't currently very orthogonal and tend to
+   * break subsequent tests! */
+  UNPORTED_TEST ("/cogl", test_cogl_viewport);
+  UNPORTED_TEST ("/cogl", test_cogl_offscreen);
+
+  return g_test_run ();
+}
diff --git a/tests/conform/test-depth-test.c b/tests/conform/test-depth-test.c
new file mode 100644
index 0000000..1cbfd96
--- /dev/null
+++ b/tests/conform/test-depth-test.c
@@ -0,0 +1,291 @@
+#include <cogl/cogl.h>
+
+#include <string.h>
+
+#include "test-utils.h"
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR)   ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR)  ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef struct _TestState
+{
+  int width;
+  int height;
+} TestState;
+
+typedef struct
+{
+  guint32               color;
+  float                 depth;
+  gboolean              test_enable;
+  CoglDepthTestFunction test_function;
+  gboolean              write_enable;
+  float                 range_near;
+  float                 range_far;
+} TestDepthState;
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+
+  if (g_test_verbose ())
+    g_print ("  expected = %x, %x, %x, %x\n",
+             r, g, b, a);
+  /* FIXME - allow for hardware in-precision */
+  g_assert_cmpint (pixel[RED], ==, r);
+  g_assert_cmpint (pixel[GREEN], ==, g);
+  g_assert_cmpint (pixel[BLUE], ==, b);
+
+  /* FIXME
+   * We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+  /* g_assert (pixel[ALPHA] == a); */
+}
+
+static gboolean
+draw_rectangle (TestState *state,
+                int x,
+                int y,
+                TestDepthState *rect_state)
+{
+  guint8 Cr = MASK_RED (rect_state->color);
+  guint8 Cg = MASK_GREEN (rect_state->color);
+  guint8 Cb = MASK_BLUE (rect_state->color);
+  guint8 Ca = MASK_ALPHA (rect_state->color);
+  CoglHandle pipeline;
+  CoglDepthState depth_state;
+
+  cogl_depth_state_init (&depth_state);
+  cogl_depth_state_set_test_enabled (&depth_state, rect_state->test_enable);
+  cogl_depth_state_set_test_function (&depth_state, rect_state->test_function);
+  cogl_depth_state_set_write_enabled (&depth_state, rect_state->write_enable);
+  cogl_depth_state_set_range (&depth_state,
+                              rect_state->range_near,
+                              rect_state->range_far);
+
+  pipeline = cogl_pipeline_new ();
+  if (!cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL))
+    {
+      cogl_object_unref (pipeline);
+      return FALSE;
+    }
+
+  cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca);
+
+  cogl_set_source (pipeline);
+
+  cogl_push_matrix ();
+  cogl_translate (0, 0, rect_state->depth);
+  cogl_rectangle (x * QUAD_WIDTH,
+                  y * QUAD_WIDTH,
+                  x * QUAD_WIDTH + QUAD_WIDTH,
+                  y * QUAD_WIDTH + QUAD_WIDTH);
+  cogl_pop_matrix ();
+
+  cogl_object_unref (pipeline);
+
+  return TRUE;
+}
+
+static void
+test_depth (TestState *state,
+            int x,
+            int y,
+            TestDepthState *rect0_state,
+            TestDepthState *rect1_state,
+            TestDepthState *rect2_state,
+            guint32 expected_result)
+{
+  GLubyte pixel[4];
+  int y_off;
+  int x_off;
+  gboolean missing_feature = FALSE;
+
+  if (rect0_state)
+    missing_feature |= !draw_rectangle (state, x, y, rect0_state);
+  if (rect1_state)
+    missing_feature |= !draw_rectangle (state, x, y, rect1_state);
+  if (rect2_state)
+    missing_feature |= !draw_rectangle (state, x, y, rect2_state);
+
+  /* We don't consider it an error that we can't test something
+   * the driver doesn't support. */
+  if (missing_feature)
+    return;
+
+  /* See what we got... */
+
+  y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+  x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+  cogl_read_pixels (x_off, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+
+  check_pixel (pixel, expected_result);
+}
+
+static void
+paint (TestState *state)
+{
+  CoglMatrix identity;
+
+  cogl_ortho (0, state->width, /* left, right */
+              state->height, 0, /* bottom, top */
+              -1, 100 /* z near, far */);
+
+  cogl_push_matrix ();
+  cogl_matrix_init_identity (&identity);
+  cogl_set_modelview_matrix (&identity);
+
+  /* Sanity check a few of the different depth test functions
+   * and that depth writing can be disabled... */
+
+  {
+    /* Closest */
+    TestDepthState rect0_state = {
+      0xff0000ff, /* rgba color */
+      -10, /* depth */
+      FALSE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+      TRUE, /* depth write enable */
+      0, 1 /* depth range */
+    };
+    /* Furthest */
+    TestDepthState rect1_state = {
+      0x00ff00ff, /* rgba color */
+      -70, /* depth */
+      TRUE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+      TRUE, /* depth write enable */
+      0, 1 /* depth range */
+    };
+    /* In the middle */
+    TestDepthState rect2_state = {
+      0x0000ffff, /* rgba color */
+      -20, /* depth */
+      TRUE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_NEVER,
+      TRUE, /* depth write enable */
+      0, 1 /* depth range */
+    };
+
+    test_depth (state, 0, 0, /* position */
+                &rect0_state, &rect1_state, &rect2_state,
+                0x00ff00ff); /* expected */
+
+    rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS;
+    test_depth (state, 1, 0, /* position */
+                &rect0_state, &rect1_state, &rect2_state,
+                0x0000ffff); /* expected */
+
+    rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS;
+    test_depth (state, 2, 0, /* position */
+                &rect0_state, &rect1_state, &rect2_state,
+                0x0000ffff); /* expected */
+
+    rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER;
+    test_depth (state, 3, 0, /* position */
+                &rect0_state, &rect1_state, &rect2_state,
+                0x00ff00ff); /* expected */
+
+    rect0_state.test_enable = TRUE;
+    rect1_state.write_enable = FALSE;
+    test_depth (state, 4, 0, /* position */
+                &rect0_state, &rect1_state, &rect2_state,
+                0x0000ffff); /* expected */
+  }
+
+  /* Check that the depth buffer values can be mapped into different
+   * ranges... */
+
+  {
+    /* Closest by depth, furthest by depth range */
+    TestDepthState rect0_state = {
+      0xff0000ff, /* rgba color */
+      -10, /* depth */
+      TRUE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_ALWAYS,
+      TRUE, /* depth write enable */
+      0.5, 1 /* depth range */
+    };
+    /* Furthest by depth, nearest by depth range */
+    TestDepthState rect1_state = {
+      0x00ff00ff, /* rgba color */
+      -70, /* depth */
+      TRUE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_GREATER,
+      TRUE, /* depth write enable */
+      0, 0.5 /* depth range */
+    };
+
+    test_depth (state, 0, 1, /* position */
+                &rect0_state, &rect1_state, NULL,
+                0xff0000ff); /* expected */
+  }
+
+  /* Test that the legacy cogl_set_depth_test_enabled() API still
+   * works... */
+
+  {
+    /* Nearest */
+    TestDepthState rect0_state = {
+      0xff0000ff, /* rgba color */
+      -10, /* depth */
+      FALSE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_LESS,
+      TRUE, /* depth write enable */
+      0, 1 /* depth range */
+    };
+    /* Furthest */
+    TestDepthState rect1_state = {
+      0x00ff00ff, /* rgba color */
+      -70, /* depth */
+      FALSE, /* depth test enable */
+      COGL_DEPTH_TEST_FUNCTION_LESS,
+      TRUE, /* depth write enable */
+      0, 1 /* depth range */
+    };
+
+    cogl_set_depth_test_enabled (TRUE);
+    test_depth (state, 0, 2, /* position */
+                &rect0_state, &rect1_state, NULL,
+                0xff0000ff); /* expected */
+    cogl_set_depth_test_enabled (FALSE);
+    test_depth (state, 1, 2, /* position */
+                &rect0_state, &rect1_state, NULL,
+                0x00ff00ff); /* expected */
+  }
+
+  cogl_pop_matrix ();
+}
+
+void
+test_cogl_depth_test (TestUtilsGTestFixture *fixture,
+                      void *data)
+{
+  TestUtilsSharedState *shared_state = data;
+  TestState state;
+
+  state.width = cogl_framebuffer_get_width (shared_state->fb);
+  state.height = cogl_framebuffer_get_height (shared_state->fb);
+  paint (&state);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-fixed.c b/tests/conform/test-fixed.c
new file mode 100644
index 0000000..78c3445
--- /dev/null
+++ b/tests/conform/test-fixed.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+void
+test_cogl_fixed (TestUtilsGTestFixture *fixture,
+		 void *data)
+{
+  g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_FLOAT (1.0));
+  g_assert_cmpint (COGL_FIXED_1, ==, COGL_FIXED_FROM_INT (1));
+
+  g_assert_cmpint (COGL_FIXED_0_5, ==, COGL_FIXED_FROM_FLOAT (0.5));
+
+  g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_1), ==, 1.0);
+  g_assert_cmpfloat (COGL_FIXED_TO_FLOAT (COGL_FIXED_0_5), ==, 0.5);
+}
+
diff --git a/tests/conform/test-fixtures.c b/tests/conform/test-fixtures.c
new file mode 100644
index 0000000..24178d2
--- /dev/null
+++ b/tests/conform/test-fixtures.c
@@ -0,0 +1,12 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+void
+test_cogl_simple_rig (void)
+{
+  ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+}
diff --git a/tests/conform/test-just-vertex-shader.c b/tests/conform/test-just-vertex-shader.c
new file mode 100644
index 0000000..886f354
--- /dev/null
+++ b/tests/conform/test-just-vertex-shader.c
@@ -0,0 +1,137 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0xff, 0xff };
+
+static void
+draw_frame (void)
+{
+  CoglHandle material = cogl_material_new ();
+  CoglColor color;
+  GError *error = NULL;
+  CoglHandle shader, program;
+
+  /* Set the primary vertex color as red */
+  cogl_color_set_from_4ub (&color, 0xff, 0x00, 0x00, 0xff);
+  cogl_material_set_color (material, &color);
+
+  /* Override the vertex color in the texture environment with a
+     constant green color */
+  cogl_color_set_from_4ub (&color, 0x00, 0xff, 0x00, 0xff);
+  cogl_material_set_layer_combine_constant (material, 0, &color);
+  if (!cogl_material_set_layer_combine (material, 0,
+                                        "RGBA=REPLACE(CONSTANT)",
+                                        &error))
+    {
+      g_warning ("Error setting blend constant: %s", error->message);
+      g_assert_not_reached ();
+    }
+
+  /* Set up a dummy vertex shader that does nothing but the usual
+     fixed function transform */
+  shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
+  cogl_shader_source (shader,
+                      "void\n"
+                      "main ()\n"
+                      "{\n"
+                      "  cogl_position_out = "
+                      "cogl_modelview_projection_matrix * "
+                      "cogl_position_in;\n"
+                      "  cogl_color_out = cogl_color_in;\n"
+                      "}\n");
+  cogl_shader_compile (shader);
+  if (!cogl_shader_is_compiled (shader))
+    {
+      char *log = cogl_shader_get_info_log (shader);
+      g_warning ("Shader compilation failed:\n%s", log);
+      g_free (log);
+      g_assert_not_reached ();
+    }
+
+  program = cogl_create_program ();
+  cogl_program_attach_shader (program, shader);
+  cogl_program_link (program);
+
+  cogl_handle_unref (shader);
+
+  /* Draw something using the material */
+  cogl_set_source (material);
+  cogl_rectangle (0, 0, 50, 50);
+
+  /* Draw it again using the program. It should look exactly the same */
+  cogl_program_use (program);
+  cogl_rectangle (50, 0, 100, 50);
+  cogl_program_use (COGL_INVALID_HANDLE);
+
+  cogl_handle_unref (material);
+  cogl_handle_unref (program);
+}
+
+static void
+validate_pixel (int x, int y)
+{
+  guint8 pixels[4];
+
+  cogl_read_pixels (x, y, 1, 1, COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE, pixels);
+
+  /* The final color should be green. If it's blue then the layer
+     state is being ignored. If it's green then the stage is showing
+     through */
+  g_assert_cmpint (pixels[0], ==, 0x00);
+  g_assert_cmpint (pixels[1], ==, 0xff);
+  g_assert_cmpint (pixels[2], ==, 0x00);
+}
+
+static void
+validate_result (void)
+{
+  /* Non-shader version */
+  validate_pixel (25, 25);
+  /* Shader version */
+  validate_pixel (75, 25);
+}
+
+static void
+on_paint (void)
+{
+  draw_frame ();
+
+  validate_result ();
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+void
+test_cogl_just_vertex_shader (TestUtilsGTestFixture *fixture,
+                              void *data)
+{
+  ClutterActor *stage;
+  unsigned int paint_handler;
+
+  stage = clutter_stage_get_default ();
+
+  /* If shaders aren't supported then we can't run the test */
+  if (cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
+    {
+      clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+      paint_handler = g_signal_connect_after (stage, "paint",
+                                              G_CALLBACK (on_paint), NULL);
+
+      clutter_actor_show (stage);
+
+      clutter_main ();
+
+      g_signal_handler_disconnect (stage, paint_handler);
+
+      if (g_test_verbose ())
+        g_print ("OK\n");
+    }
+  else if (g_test_verbose ())
+    g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-launcher.sh.in b/tests/conform/test-launcher.sh.in
new file mode 100755
index 0000000..0be13bd
--- /dev/null
+++ b/tests/conform/test-launcher.sh.in
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+UNIT_TEST_PATH=$1
+shift
+
+test -z ${UNIT_TEST_PATH} && {
+        echo "Usage: $0 /path/to/unit_test"
+        exit 1
+}
+
+UNIT_TEST=`basename ${UNIT_TEST_PATH}`
+
+echo "Running: ./test-conformance -p ${UNIT_TEST_PATH} $@"
+echo ""
+ abs_builddir@/test-conformance -p ${UNIT_TEST_PATH} "$@"
+
+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 -p ${UNIT_TEST_PATH}"
+echo "or:"
+echo "$ env G_SLICE=always-malloc \\"
+echo "  libtool --mode=execute \\"
+echo "          valgrind ./test-conformance -p ${UNIT_TEST_PATH}"
diff --git a/tests/conform/test-materials.c b/tests/conform/test-materials.c
new file mode 100644
index 0000000..5dbb30c
--- /dev/null
+++ b/tests/conform/test-materials.c
@@ -0,0 +1,285 @@
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR)   ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR)  ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef struct _TestState
+{
+  ClutterGeometry stage_geom;
+} TestState;
+
+
+static void
+check_pixel (TestState *state, int x, int y, guint32 color)
+{
+  GLint y_off;
+  GLint x_off;
+  GLubyte pixel[4];
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+
+  /* See what we got... */
+
+  /* NB: glReadPixels is done in GL screen space so y = 0 is at the bottom */
+  y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+  x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+  cogl_read_pixels (x_off, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("  result = %02x, %02x, %02x, %02x\n",
+             pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+
+  if (g_test_verbose ())
+    g_print ("  expected = %x, %x, %x, %x\n",
+             r, g, b, a);
+  /* FIXME - allow for hardware in-precision */
+  g_assert (pixel[RED] == r);
+  g_assert (pixel[GREEN] == g);
+  g_assert (pixel[BLUE] == b);
+
+  /* FIXME
+   * We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+  /* g_assert (pixel[ALPHA] == a); */
+}
+
+static void
+test_material_with_primitives (TestState *state,
+                               int x, int y,
+                               guint32 color)
+{
+  CoglTextureVertex verts[4] = {
+    { .x = 0,          .y = 0,          .z = 0 },
+    { .x = 0,          .y = QUAD_WIDTH, .z = 0 },
+    { .x = QUAD_WIDTH, .y = QUAD_WIDTH, .z = 0 },
+    { .x = QUAD_WIDTH, .y = 0,          .z = 0 },
+  };
+  CoglHandle vbo;
+
+  cogl_push_matrix ();
+
+  cogl_translate (x * QUAD_WIDTH, y * QUAD_WIDTH, 0);
+
+  cogl_rectangle (0, 0, QUAD_WIDTH, QUAD_WIDTH);
+
+  cogl_translate (0, QUAD_WIDTH, 0);
+  cogl_polygon (verts, 4, FALSE);
+
+  cogl_translate (0, QUAD_WIDTH, 0);
+  vbo = cogl_vertex_buffer_new (4);
+  cogl_vertex_buffer_add (vbo,
+                          "gl_Vertex",
+                          2, /* n components */
+                          COGL_ATTRIBUTE_TYPE_FLOAT,
+                          FALSE, /* normalized */
+                          sizeof (CoglTextureVertex), /* stride */
+                          verts);
+  cogl_vertex_buffer_draw (vbo,
+                           COGL_VERTICES_MODE_TRIANGLE_FAN,
+                           0, /* first */
+                           4); /* count */
+  cogl_handle_unref (vbo);
+
+  cogl_pop_matrix ();
+
+  check_pixel (state, x, y,   color);
+  check_pixel (state, x, y+1, color);
+  check_pixel (state, x, y+2, color);
+}
+
+static void
+test_invalid_texture_layers (TestState *state, int x, int y)
+{
+  CoglHandle        material = cogl_material_new ();
+
+  /* explicitly create a layer with an invalid handle. This may be desireable
+   * if the user also sets a texture combine string that e.g. refers to a
+   * constant color. */
+  cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE);
+
+  cogl_set_source (material);
+
+  cogl_handle_unref (material);
+
+  /* We expect a white fallback material to be used */
+  test_material_with_primitives (state, x, y, 0xffffffff);
+}
+
+static void
+test_using_all_layers (TestState *state, int x, int y)
+{
+  CoglHandle material = cogl_material_new ();
+  guint8 white_pixel[] = { 0xff, 0xff, 0xff, 0xff };
+  guint8 red_pixel[] = { 0xff, 0x00, 0x00, 0xff };
+  CoglHandle white_texture;
+  CoglHandle red_texture;
+  GLint n_layers;
+  int i;
+
+  /* Create a material that uses the maximum number of layers. All but
+     the last layer will use a solid white texture. The last layer
+     will use a red texture. The layers will all be modulated together
+     so the final fragment should be red. */
+
+  white_texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE,
+                                              COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                              COGL_PIXEL_FORMAT_ANY,
+                                              4, white_pixel);
+  red_texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE,
+                                            COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                            COGL_PIXEL_FORMAT_ANY,
+                                            4, red_pixel);
+
+  /* FIXME: Cogl doesn't provide a way to query the maximum number of
+     texture layers so for now we'll just ask GL directly. */
+#ifdef HAVE_COGL_GLES2
+  {
+    GLint n_image_units, n_attribs;
+    /* GLES 2 doesn't have GL_MAX_TEXTURE_UNITS and it uses
+       GL_MAX_TEXTURE_IMAGE_UNITS instead */
+    glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &n_image_units);
+    /* Cogl needs a vertex attrib for each layer to upload the texture
+       coordinates */
+    glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, &n_attribs);
+    /* We can't use two of the attribs because they are used by the
+       position and color */
+    n_attribs -= 2;
+    n_layers = MIN (n_attribs, n_image_units);
+  }
+#else
+  glGetIntegerv (GL_MAX_TEXTURE_UNITS, &n_layers);
+#endif
+  /* FIXME: is this still true? */
+  /* Cogl currently can't cope with more than 32 layers so we'll also
+     limit the maximum to that. */
+  if (n_layers > 32)
+    n_layers = 32;
+
+  for (i = 0; i < n_layers; i++)
+    {
+      cogl_material_set_layer_filters (material, i,
+                                       COGL_MATERIAL_FILTER_NEAREST,
+                                       COGL_MATERIAL_FILTER_NEAREST);
+      cogl_material_set_layer (material, i,
+                               i == n_layers - 1 ? red_texture : white_texture);
+    }
+
+  cogl_set_source (material);
+
+  cogl_handle_unref (material);
+  cogl_handle_unref (white_texture);
+  cogl_handle_unref (red_texture);
+
+  /* We expect the final fragment to be red */
+  test_material_with_primitives (state, x, y, 0xff0000ff);
+}
+
+static void
+test_invalid_texture_layers_with_constant_colors (TestState *state,
+                                                  int x, int y)
+{
+  CoglHandle material = cogl_material_new ();
+  CoglColor constant_color;
+
+  /* explicitly create a layer with an invalid handle */
+  cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE);
+
+  /* ignore the fallback texture on the layer and use a constant color
+     instead */
+  cogl_color_init_from_4ub (&constant_color, 0, 0, 255, 255);
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA=REPLACE(CONSTANT)",
+                                   NULL);
+  cogl_material_set_layer_combine_constant (material, 0, &constant_color);
+
+  cogl_set_source (material);
+
+  cogl_handle_unref (material);
+
+  /* We expect the final fragments to be green */
+  test_material_with_primitives (state, x, y, 0x0000ffff);
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  test_invalid_texture_layers (state,
+                               0, 0 /* position */
+                               );
+  test_invalid_texture_layers_with_constant_colors (state,
+                                                    1, 0 /* position */
+                                                    );
+  test_using_all_layers (state,
+                         2, 0 /* position */
+                         );
+
+  /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+  clutter_main_quit ();
+#endif
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_materials (TestUtilsGTestFixture *fixture,
+                     void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-multitexture.c b/tests/conform/test-multitexture.c
new file mode 100644
index 0000000..79717bc
--- /dev/null
+++ b/tests/conform/test-multitexture.c
@@ -0,0 +1,206 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED   0
+#define GREEN 1
+#define BLUE  2
+#define ALPHA 3
+
+typedef struct _TestState
+{
+  unsigned int padding;
+} TestState;
+
+static void
+assert_region_color (int x,
+                     int y,
+                     int width,
+                     int height,
+                     guint8 red,
+                     guint8 green,
+                     guint8 blue,
+                     guint8 alpha)
+{
+  guint8 *data = g_malloc0 (width * height * 4);
+  cogl_read_pixels (x, y, width, height,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    data);
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        guint8 *pixel = &data[y * width * 4 + x * 4];
+#if 1
+        g_assert (pixel[RED] == red &&
+                  pixel[GREEN] == green &&
+                  pixel[BLUE] == blue);
+#endif
+      }
+  g_free (data);
+}
+
+/* Creates a texture divided into 4 quads with colors arranged as follows:
+ * (The same value are used in all channels for each texel)
+ *
+ * |-----------|
+ * |0x11 |0x00 |
+ * |+ref |     |
+ * |-----------|
+ * |0x00 |0x33 |
+ * |     |+ref |
+ * |-----------|
+ *
+ *
+ */
+static CoglHandle
+make_texture (guchar ref)
+{
+  int x;
+  int y;
+  guchar *tex_data, *p;
+  CoglHandle tex;
+  guchar val;
+
+  tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 16);
+
+  for (y = 0; y < QUAD_WIDTH * 2; y++)
+    for (x = 0; x < QUAD_WIDTH * 2; x++)
+      {
+        p = tex_data + (QUAD_WIDTH * 8 * y) + x * 4;
+        if (x < QUAD_WIDTH && y < QUAD_WIDTH)
+          val = 0x11 + ref;
+        else if (x >= QUAD_WIDTH && y >= QUAD_WIDTH)
+          val = 0x33 + ref;
+        else
+          val = 0x00;
+        p[0] = p[1] = p[2] = p[3] = val;
+      }
+
+  /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here
+   * since we don't want to allow Cogl to premultiply our data. */
+  tex = cogl_texture_new_from_data (QUAD_WIDTH * 2,
+                                    QUAD_WIDTH * 2,
+                                    COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    QUAD_WIDTH * 8,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  return tex;
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglHandle tex0, tex1;
+  CoglHandle material;
+  gboolean status;
+  GError *error = NULL;
+  float tex_coords[] = {
+    0, 0, 0.5, 0.5, /* tex0 */
+    0.5, 0.5, 1, 1 /* tex1 */
+  };
+
+  tex0 = make_texture (0x00);
+  tex1 = make_texture (0x11);
+
+  material = cogl_material_new ();
+
+  /* An arbitrary color which should be replaced by the first texture layer */
+  cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80);
+  cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL);
+
+  cogl_material_set_layer (material, 0, tex0);
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA = REPLACE (TEXTURE)", NULL);
+  /* We'll use nearest filtering mode on the textures, otherwise the
+     edge of the quad can pull in texels from the neighbouring
+     quarters of the texture due to imprecision */
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+
+  cogl_material_set_layer (material, 1, tex1);
+  cogl_material_set_layer_filters (material, 1,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+  status = cogl_material_set_layer_combine (material, 1,
+                                            "RGBA = ADD (PREVIOUS, TEXTURE)",
+                                            &error);
+  if (!status)
+    {
+      /* It's not strictly a test failure; you need a more capable GPU or
+       * driver to test this texture combine string. */
+      g_debug ("Failed to setup texture combine string "
+               "RGBA = ADD (PREVIOUS, TEXTURE): %s",
+               error->message);
+    }
+
+  cogl_set_source (material);
+  cogl_rectangle_with_multitexture_coords (0, 0, QUAD_WIDTH, QUAD_WIDTH,
+                                           tex_coords, 8);
+
+  cogl_handle_unref (material);
+  cogl_handle_unref (tex0);
+  cogl_handle_unref (tex1);
+
+  /* See what we got... */
+
+  assert_region_color (0, 0, QUAD_WIDTH, QUAD_WIDTH,
+                       0x55, 0x55, 0x55, 0x55);
+
+  /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+  clutter_main_quit ();
+#endif
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_multitexture (TestUtilsGTestFixture *fixture,
+                        void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/conform/test-npot-texture.c b/tests/conform/test-npot-texture.c
new file mode 100644
index 0000000..49db94f
--- /dev/null
+++ b/tests/conform/test-npot-texture.c
@@ -0,0 +1,236 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+/* Non-power-of-two sized texture that should cause slicing */
+#define TEXTURE_SIZE        384
+/* Number of times to split the texture up on each axis */
+#define PARTS               2
+/* The texture is split into four parts, each with a different colour */
+#define PART_SIZE           (TEXTURE_SIZE / PARTS)
+
+/* Amount of pixels to skip off the top, bottom, left and right of the
+   texture when reading back the stage */
+#define TEST_INSET          4
+
+/* Size to actually render the texture at */
+#define TEXTURE_RENDER_SIZE TEXTURE_SIZE
+/* The size of a part once rendered */
+#define PART_RENDER_SIZE    (TEXTURE_RENDER_SIZE / PARTS)
+
+static const ClutterColor corner_colors[PARTS * PARTS] =
+  {
+    /* Top left     - red */    { 255, 0,   0,   255 },
+    /* Top right    - green */  { 0,   255, 0,   255 },
+    /* Bottom left  - blue */   { 0,   0,   255, 255 },
+    /* Bottom right - yellow */ { 255, 255, 0,   255 }
+  };
+
+typedef struct _TestState
+{
+  unsigned int frame;
+  CoglHandle texture;
+} TestState;
+
+static gboolean
+validate_part (int xnum, int ynum, const ClutterColor *color)
+{
+  guchar *pixels, *p;
+  ClutterActor *stage = clutter_stage_get_default ();
+  gboolean ret = TRUE;
+
+  /* Read the appropriate part but skip out a few pixels around the
+     edges */
+  pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage),
+                                      xnum * PART_RENDER_SIZE + TEST_INSET,
+                                      ynum * PART_RENDER_SIZE + TEST_INSET,
+                                      PART_RENDER_SIZE - TEST_INSET * 2,
+                                      PART_RENDER_SIZE - TEST_INSET * 2);
+
+  /* Make sure every pixels is the appropriate color */
+  for (p = pixels;
+       p < pixels + ((PART_RENDER_SIZE - TEST_INSET * 2)
+                     * (PART_RENDER_SIZE - TEST_INSET * 2));
+       p += 4)
+    {
+      if (p[0] != color->red)
+        ret = FALSE;
+      if (p[1] != color->green)
+        ret = FALSE;
+      if (p[2] != color->blue)
+        ret = FALSE;
+    }
+
+  g_free (pixels);
+
+  return ret;
+}
+
+static void
+validate_result (TestState *state)
+{
+  /* Validate that all four corners of the texture are drawn in the
+     right color */
+  g_assert (validate_part (0, 0, corner_colors + 0));
+  g_assert (validate_part (1, 0, corner_colors + 1));
+  g_assert (validate_part (0, 1, corner_colors + 2));
+  g_assert (validate_part (1, 1, corner_colors + 3));
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  int frame_num;
+  int y, x;
+
+  /* Just render the texture in the top left corner */
+  cogl_set_source_texture (state->texture);
+  /* Render the texture using four separate rectangles */
+  for (y = 0; y < 2; y++)
+    for (x = 0; x < 2; x++)
+      cogl_rectangle_with_texture_coords (x * TEXTURE_RENDER_SIZE / 2,
+                                          y * TEXTURE_RENDER_SIZE / 2,
+                                          (x + 1) * TEXTURE_RENDER_SIZE / 2,
+                                          (y + 1) * TEXTURE_RENDER_SIZE / 2,
+                                          x / 2.0f,
+                                          y / 2.0f,
+                                          (x + 1) / 2.0f,
+                                          (y + 1) / 2.0f);
+
+  /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+   * another paint run so to avoid infinite recursion we only aim to validate
+   * the first frame. */
+  frame_num = state->frame++;
+  if (frame_num == 1)
+    validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+static CoglHandle
+make_texture (void)
+{
+  guchar *tex_data, *p;
+  CoglHandle tex;
+  int partx, party, width, height;
+
+  p = tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4);
+
+  /* Make a texture with a different color for each part */
+  for (party = 0; party < PARTS; party++)
+    {
+      height = (party < PARTS - 1
+                ? PART_SIZE
+                : TEXTURE_SIZE - PART_SIZE * (PARTS - 1));
+
+      for (partx = 0; partx < PARTS; partx++)
+        {
+          const ClutterColor *color = corner_colors + party * PARTS + partx;
+          width = (partx < PARTS - 1
+                   ? PART_SIZE
+                   : TEXTURE_SIZE - PART_SIZE * (PARTS - 1));
+
+          while (width-- > 0)
+            {
+              *(p++) = color->red;
+              *(p++) = color->green;
+              *(p++) = color->blue;
+              *(p++) = color->alpha;
+            }
+        }
+
+      while (--height > 0)
+        {
+          memcpy (p, p - TEXTURE_SIZE * 4, TEXTURE_SIZE * 4);
+          p += TEXTURE_SIZE * 4;
+        }
+    }
+
+  tex = cogl_texture_new_from_data (TEXTURE_SIZE,
+                                    TEXTURE_SIZE,
+                                    COGL_TEXTURE_NO_ATLAS,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    TEXTURE_SIZE * 4,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  if (g_test_verbose ())
+    {
+      if (cogl_texture_is_sliced (tex))
+        g_print ("Texture is sliced\n");
+      else
+        g_print ("Texture is not sliced\n");
+    }
+
+  /* The texture should be sliced unless NPOTs are supported */
+  g_assert (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)
+            ? !cogl_texture_is_sliced (tex)
+            : cogl_texture_is_sliced (tex));
+
+  return tex;
+}
+
+void
+test_cogl_npot_texture (TestUtilsGTestFixture *fixture,
+                        void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  if (g_test_verbose ())
+    {
+      if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT))
+        g_print ("NPOT textures are supported\n");
+      else
+        g_print ("NPOT textures are not supported\n");
+    }
+
+  state.frame = 0;
+
+  state.texture = make_texture ();
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  cogl_handle_unref (state.texture);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-object.c b/tests/conform/test-object.c
new file mode 100644
index 0000000..b66b83e
--- /dev/null
+++ b/tests/conform/test-object.c
@@ -0,0 +1,86 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+CoglUserDataKey private_key0;
+CoglUserDataKey private_key1;
+CoglUserDataKey private_key2;
+
+static int user_data0;
+static int user_data1;
+static int user_data2;
+
+static int destroy0_count = 0;
+static int destroy1_count = 0;
+static int destroy2_count = 0;
+
+static void
+destroy0_cb (void *user_data)
+{
+  g_assert (user_data == &user_data0);
+  destroy0_count++;
+}
+
+static void
+destroy1_cb (void *user_data)
+{
+  g_assert (user_data == &user_data1);
+  destroy1_count++;
+}
+
+static void
+destroy2_cb (void *user_data)
+{
+  g_assert (user_data == &user_data2);
+  destroy2_count++;
+}
+
+void
+test_cogl_object (TestUtilsGTestFixture *fixture,
+                  void *data)
+{
+  CoglPath *path;
+
+  /* Assuming that COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES == 2
+   * test associating 2 pointers to private data with an object */
+  cogl_path_new ();
+  path = cogl_get_path ();
+
+  cogl_object_set_user_data (COGL_OBJECT (path),
+                             &private_key0,
+                             &user_data0,
+                             destroy0_cb);
+
+  cogl_object_set_user_data (COGL_OBJECT (path),
+                             &private_key1,
+                             &user_data1,
+                             destroy1_cb);
+
+  cogl_object_set_user_data (COGL_OBJECT (path),
+                             &private_key2,
+                             &user_data2,
+                             destroy2_cb);
+
+  cogl_object_set_user_data (COGL_OBJECT (path),
+                             &private_key1,
+                             NULL,
+                             destroy1_cb);
+
+  cogl_object_set_user_data (COGL_OBJECT (path),
+                             &private_key1,
+                             &user_data1,
+                             destroy1_cb);
+
+  cogl_object_unref (path);
+
+  g_assert_cmpint (destroy0_count, ==, 1);
+  g_assert_cmpint (destroy1_count, ==, 2);
+  g_assert_cmpint (destroy2_count, ==, 1);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-offscreen.c b/tests/conform/test-offscreen.c
new file mode 100644
index 0000000..07ab6d6
--- /dev/null
+++ b/tests/conform/test-offscreen.c
@@ -0,0 +1,167 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+#define FRAMEBUFFER_WIDTH  640
+#define FRAMEBUFFER_HEIGHT 480
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+
+static void
+on_paint (ClutterActor *actor, void *state)
+{
+  float saved_viewport[4];
+  CoglMatrix saved_projection;
+  CoglMatrix projection;
+  CoglMatrix modelview;
+  guchar *data;
+  CoglHandle tex;
+  CoglHandle offscreen;
+  guint8 pixel[4];
+
+  /* Save the Clutter viewport/matrices and load identity matrices */
+
+  cogl_get_viewport (saved_viewport);
+  cogl_get_projection_matrix (&saved_projection);
+  cogl_push_matrix ();
+
+  cogl_matrix_init_identity (&projection);
+  cogl_matrix_init_identity (&modelview);
+
+  cogl_set_projection_matrix (&projection);
+  cogl_set_modelview_matrix (&modelview);
+
+  data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+  tex = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                                    COGL_TEXTURE_NO_SLICING,
+                                    COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */
+                                    COGL_PIXEL_FORMAT_ANY, /* internal fmt */
+                                    FRAMEBUFFER_WIDTH * 4, /* rowstride */
+                                    data);
+  g_free (data);
+  offscreen = cogl_offscreen_new_to_texture (tex);
+
+  /* Set a scale and translate transform on the window framebuffer before
+   * switching to the offscreen framebuffer so we can verify it gets restored
+   * when we switch back
+   *
+   * The test is going to draw a grid of 4 colors to a texture which we
+   * subsequently draw to the window with a fullscreen rectangle. This
+   * transform will flip the texture left to right, scale it to a quater of the
+   * window size and slide it to the top right of the window.
+   */
+  cogl_translate (0.5, 0.5, 0);
+  cogl_scale (-0.5, 0.5, 1);
+
+  cogl_push_framebuffer (offscreen);
+
+  /* Cogl should release the last reference when we call cogl_pop_framebuffer()
+   */
+  cogl_handle_unref (offscreen);
+
+  /* Setup something other than the identity matrix for the modelview so we can
+   * verify it gets restored when we call cogl_pop_framebuffer () */
+  cogl_scale (2, 2, 1);
+
+  /* red, top left */
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_rectangle (-0.5, 0.5, 0, 0);
+  /* green, top right */
+  cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+  cogl_rectangle (0, 0.5, 0.5, 0);
+  /* blue, bottom left */
+  cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+  cogl_rectangle (-0.5, 0, 0, -0.5);
+  /* white, bottom right */
+  cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+  cogl_rectangle (0, 0, 0.5, -0.5);
+
+  cogl_pop_framebuffer ();
+
+  cogl_set_source_texture (tex);
+  cogl_rectangle (-1, 1, 1, -1);
+
+  cogl_handle_unref (tex);
+
+  /* NB: The texture is drawn flipped horizontally and scaled to fit in the
+   * top right corner of the window. */
+
+  /* red, top right */
+  cogl_read_pixels (FRAMEBUFFER_WIDTH - 1, 0, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0x00 && pixel[BLUE] == 0x00);
+
+  /* green, top left */
+  cogl_read_pixels ((FRAMEBUFFER_WIDTH/2), 0, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0xff && pixel[BLUE] == 0x00);
+
+  /* blue, bottom right */
+  cogl_read_pixels (FRAMEBUFFER_WIDTH - 1, (FRAMEBUFFER_HEIGHT/2) - 1, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0x00 && pixel[BLUE] == 0xff);
+
+  /* white, bottom left */
+  cogl_read_pixels ((FRAMEBUFFER_WIDTH/2), (FRAMEBUFFER_HEIGHT/2) - 1, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0xff && pixel[BLUE] == 0xff);
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_offscreen (TestUtilsGTestFixture *fixture,
+                     void *data)
+{
+  unsigned int idle_source;
+  ClutterActor *stage;
+
+  stage = clutter_stage_get_default ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+  g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL);
+
+  clutter_actor_show (stage);
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-path.c b/tests/conform/test-path.c
new file mode 100644
index 0000000..363482f
--- /dev/null
+++ b/tests/conform/test-path.c
@@ -0,0 +1,234 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define BLOCK_SIZE 16
+
+/* Number of pixels at the border of a block quadrant to skip when verifying */
+#define TEST_INSET 1
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+static const ClutterColor block_color = { 0xff, 0xff, 0xff, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  unsigned int frame;
+} TestState;
+
+static void
+draw_path_at (int x, int y)
+{
+  cogl_push_matrix ();
+  cogl_translate (x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f);
+  cogl_path_fill ();
+  cogl_pop_matrix ();
+}
+
+static void
+verify_block (int block_x, int block_y, int block_mask)
+{
+  guint8 data[BLOCK_SIZE * BLOCK_SIZE * 4];
+  int qx, qy;
+
+  /* Block mask represents which quarters of the block should be
+     filled. The bits from 0->3 represent the top left, top right,
+     bottom left and bottom right respectively */
+
+  cogl_read_pixels (block_x * BLOCK_SIZE,
+                    block_y * BLOCK_SIZE,
+                    BLOCK_SIZE, BLOCK_SIZE,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    data);
+
+  for (qy = 0; qy < 2; qy++)
+    for (qx = 0; qx < 2; qx++)
+      {
+        int bit = qx | (qy << 1);
+        const ClutterColor *color =
+          ((block_mask & (1 << bit)) ? &block_color : &stage_color);
+        int x, y;
+
+        for (x = 0; x < BLOCK_SIZE / 2 - TEST_INSET * 2; x++)
+          for (y = 0; y < BLOCK_SIZE / 2 - TEST_INSET * 2; y++)
+            {
+              const guint8 *p = data + (qx * BLOCK_SIZE / 2 * 4 +
+                                        qy * BLOCK_SIZE * 4 * BLOCK_SIZE / 2 +
+                                        (x + TEST_INSET) * 4 +
+                                        (y + TEST_INSET) * BLOCK_SIZE * 4);
+              g_assert_cmpint (p[0], ==, color->red);
+              g_assert_cmpint (p[1], ==, color->green);
+              g_assert_cmpint (p[2], ==, color->blue);
+            }
+      }
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglHandle path_a, path_b, path_c;
+
+  if (state->frame++ < 2)
+    return;
+
+  cogl_set_source_color4ub (255, 255, 255, 255);
+
+  /* Create a path filling just a quarter of a block. It will use two
+     rectangles so that we have a sub path in the path */
+  cogl_path_new ();
+  cogl_path_rectangle (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2,
+                       BLOCK_SIZE, BLOCK_SIZE);
+  cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2,
+                       BLOCK_SIZE * 3 / 4, BLOCK_SIZE);
+  path_a = cogl_handle_ref (cogl_get_path ());
+  draw_path_at (0, 0);
+
+  /* Create another path filling the whole block */
+  cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+  path_b = cogl_handle_ref (cogl_get_path ());
+  draw_path_at (1, 0);
+
+  /* Draw the first path again */
+  cogl_set_path (path_a);
+  draw_path_at (2, 0);
+
+  /* Draw a copy of path a */
+  path_c = cogl_path_copy (path_a);
+  cogl_set_path (path_c);
+  draw_path_at (3, 0);
+
+  /* Add another rectangle to path a. We'll use line_to's instead of
+     cogl_rectangle so that we don't create another sub-path because
+     that is more likely to break the copy */
+  cogl_set_path (path_a);
+  cogl_path_line_to (0, BLOCK_SIZE / 2);
+  cogl_path_line_to (0, 0);
+  cogl_path_line_to (BLOCK_SIZE / 2, 0);
+  cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+  draw_path_at (4, 0);
+
+  /* Draw the copy again. It should not have changed */
+  cogl_set_path (path_c);
+  draw_path_at (5, 0);
+
+  /* Add another rectangle to path c. It will be added in two halves,
+     one as an extension of the previous path and the other as a new
+     sub path */
+  cogl_set_path (path_c);
+  cogl_path_line_to (BLOCK_SIZE / 2, 0);
+  cogl_path_line_to (BLOCK_SIZE * 3 / 4, 0);
+  cogl_path_line_to (BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2);
+  cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+  cogl_path_rectangle (BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2);
+  draw_path_at (6, 0);
+
+  /* Draw the original path again. It should not have changed */
+  cogl_set_path (path_a);
+  draw_path_at (7, 0);
+
+  cogl_handle_unref (path_a);
+  cogl_handle_unref (path_b);
+  cogl_handle_unref (path_c);
+
+  /* Draw a self-intersecting path. The part that intersects should be
+     inverted */
+  cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+  cogl_path_line_to (0, BLOCK_SIZE / 2);
+  cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+  cogl_path_line_to (BLOCK_SIZE / 2, 0);
+  cogl_path_close ();
+  draw_path_at (8, 0);
+
+  /* Draw two sub paths. Where the paths intersect it should be
+     inverted */
+  cogl_path_rectangle (0, 0, BLOCK_SIZE, BLOCK_SIZE);
+  cogl_path_rectangle (BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE);
+  draw_path_at (9, 0);
+
+  /* Draw a clockwise outer path */
+  cogl_path_move_to (0, 0);
+  cogl_path_line_to (BLOCK_SIZE, 0);
+  cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE);
+  cogl_path_line_to (0, BLOCK_SIZE);
+  cogl_path_close ();
+  /* Add a clockwise sub path in the upper left quadrant */
+  cogl_path_move_to (0, 0);
+  cogl_path_line_to (BLOCK_SIZE / 2, 0);
+  cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+  cogl_path_line_to (0, BLOCK_SIZE / 2);
+  cogl_path_close ();
+  /* Add a counter-clockwise sub path in the upper right quadrant */
+  cogl_path_move_to (BLOCK_SIZE / 2, 0);
+  cogl_path_line_to (BLOCK_SIZE / 2, BLOCK_SIZE / 2);
+  cogl_path_line_to (BLOCK_SIZE, BLOCK_SIZE / 2);
+  cogl_path_line_to (BLOCK_SIZE, 0);
+  cogl_path_close ();
+  /* Retain the path for the next test */
+  path_a = cogl_handle_ref (cogl_get_path ());
+  draw_path_at (10, 0);
+
+  /* Draw the same path again with the other fill rule */
+  cogl_set_path (path_a);
+  cogl_path_set_fill_rule (COGL_PATH_FILL_RULE_NON_ZERO);
+  draw_path_at (11, 0);
+
+  cogl_handle_unref (path_a);
+
+  verify_block (0, 0, 0x8 /* bottom right */);
+  verify_block (1, 0, 0xf /* all of them */);
+  verify_block (2, 0, 0x8 /* bottom right */);
+  verify_block (3, 0, 0x8 /* bottom right */);
+  verify_block (4, 0, 0x9 /* top left and bottom right */);
+  verify_block (5, 0, 0x8 /* bottom right */);
+  verify_block (6, 0, 0xa /* bottom right and top right */);
+  verify_block (7, 0, 0x9 /* top_left and bottom right */);
+  verify_block (8, 0, 0xe /* all but top left */);
+  verify_block (9, 0, 0x7 /* all but bottom right */);
+  verify_block (10, 0, 0xc /* bottom two */);
+  verify_block (11, 0, 0xd /* all but top right */);
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_path (TestUtilsGTestFixture *fixture,
+                void *data)
+{
+  TestState state;
+  unsigned int idle_source;
+  unsigned int paint_handler;
+
+  state.frame = 0;
+  state.stage = clutter_stage_get_default ();
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, state.stage);
+  paint_handler = g_signal_connect_after (state.stage, "paint",
+                                          G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show (state.stage);
+  clutter_main ();
+
+  g_signal_handler_disconnect (state.stage, paint_handler);
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-pipeline-user-matrix.c b/tests/conform/test-pipeline-user-matrix.c
new file mode 100644
index 0000000..8e48fd2
--- /dev/null
+++ b/tests/conform/test-pipeline-user-matrix.c
@@ -0,0 +1,144 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+static void
+paint_cb (ClutterActor *stage)
+{
+  /* This texture is painted mirrored around the x-axis */
+  guint8 data0[] = {
+    0xff, 0x00, 0x00, /* red -> becomes bottom left */
+    0x00, 0xff, 0x00, /* green -> becomes bottom right */
+    0x00, 0x00, 0xff, /* blue -> becomes top left */
+    0xff, 0x00, 0xff  /* magenta -> becomes top right */
+  };
+  /* This texture is painted mirrored about the y-axis */
+  guint8 data1[] = {
+    0x00, 0xff, 0x00, /* green -> becomes top right */
+    0xff, 0xff, 0x00, /* yellow -> becomes top left */
+    0xff, 0x00, 0xff, /* magenta -> becomes bottom right */
+    0x00, 0xff, 0xff  /* cyan -> becomes bottom left */
+  };
+  CoglHandle tex0, tex1;
+  CoglPipeline *pipeline;
+  CoglMatrix matrix;
+  int width, height;
+  guint8 *pixels, *p;
+
+  width = clutter_actor_get_width (stage);
+  height = clutter_actor_get_height (stage);
+
+  tex0 = cogl_texture_new_from_data (2, 2,
+                                     COGL_TEXTURE_NO_ATLAS,
+                                     COGL_PIXEL_FORMAT_RGB_888,
+                                     COGL_PIXEL_FORMAT_ANY,
+                                     6,
+                                     data0);
+  tex1 = cogl_texture_new_from_data (2, 2,
+                                     COGL_TEXTURE_NO_ATLAS,
+                                     COGL_PIXEL_FORMAT_RGB_888,
+                                     COGL_PIXEL_FORMAT_ANY,
+                                     6,
+                                     data1);
+
+  pipeline = cogl_pipeline_new ();
+
+  /* Set the two textures as layers */
+  cogl_pipeline_set_layer_texture (pipeline, 0, tex0);
+  cogl_pipeline_set_layer_filters (pipeline, 0,
+                                   COGL_PIPELINE_FILTER_NEAREST,
+                                   COGL_PIPELINE_FILTER_NEAREST);
+  cogl_pipeline_set_layer_texture (pipeline, 1, tex1);
+  cogl_pipeline_set_layer_filters (pipeline, 1,
+                                   COGL_PIPELINE_FILTER_NEAREST,
+                                   COGL_PIPELINE_FILTER_NEAREST);
+
+  /* Set a combine mode so that the two textures get added together */
+  cogl_pipeline_set_layer_combine (pipeline, 1,
+                                   "RGBA=ADD(PREVIOUS, TEXTURE)",
+                                   NULL);
+
+  /* Set a matrix on the first layer so that it will mirror about the y-axis */
+  cogl_matrix_init_identity (&matrix);
+  cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
+  cogl_matrix_scale (&matrix, 1.0f, -1.0f, 1.0f);
+  cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+
+  /* Set a matrix on the second layer so that it will mirror about the x-axis */
+  cogl_matrix_init_identity (&matrix);
+  cogl_matrix_translate (&matrix, 1.0f, 0.0f, 0.0f);
+  cogl_matrix_scale (&matrix, -1.0f, 1.0f, 1.0f);
+  cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix);
+
+  cogl_set_source (pipeline);
+  cogl_rectangle (0, 0, width, height);
+
+  cogl_handle_unref (tex1);
+  cogl_handle_unref (tex0);
+  cogl_object_unref (pipeline);
+
+  /* The textures are setup so that when added together with the
+     correct matrices then all of the pixels should be white. We can
+     verify this by reading back the entire stage */
+  pixels = g_malloc (width * height * 4);
+
+  cogl_read_pixels (0, 0, width, height,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixels);
+
+  for (p = pixels + width * height * 4; p > pixels;)
+    {
+      p -= 4;
+      g_assert_cmpint (p[0], ==, 0xff);
+      g_assert_cmpint (p[1], ==, 0xff);
+      g_assert_cmpint (p[2], ==, 0xff);
+    }
+
+  g_free (pixels);
+
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_pipeline_user_matrix (TestUtilsGTestFixture *fixture,
+                                void *data)
+{
+  ClutterActor *stage;
+  unsigned int idle_source;
+  unsigned int paint_handler;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  paint_handler = g_signal_connect_after (stage, "paint",
+                                          G_CALLBACK (paint_cb),
+                                          NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+  g_signal_handler_disconnect (stage, paint_handler);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/conform/test-pixel-buffer.c b/tests/conform/test-pixel-buffer.c
new file mode 100644
index 0000000..05e8433
--- /dev/null
+++ b/tests/conform/test-pixel-buffer.c
@@ -0,0 +1,330 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#define TILE_SIZE        32.0f
+
+enum
+{
+  TILE_MAP,
+  TILE_SET_DATA,
+  NB_TILES,
+  TILE_SET_REGION,
+};
+
+typedef struct test_tile
+{
+  ClutterColor color;
+  gfloat x, y;
+  CoglHandle buffer;
+  CoglHandle texture;
+} TestTile;
+
+static const ClutterColor
+buffer_colors[] =
+  {
+  };
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  unsigned int frame;
+
+  TestTile *tiles;
+
+} TestState;
+
+static CoglHandle
+create_texture_from_buffer (CoglHandle buffer)
+{
+  CoglHandle texture;
+
+  texture = cogl_texture_new_from_buffer (buffer,
+                                          TILE_SIZE, TILE_SIZE,
+                                          COGL_TEXTURE_NO_SLICING,
+                                          COGL_PIXEL_FORMAT_RGBA_8888,
+                                          COGL_PIXEL_FORMAT_RGBA_8888,
+                                          TILE_SIZE * 4,
+                                          0);
+
+  g_assert (texture != COGL_INVALID_HANDLE);
+
+  return texture;
+}
+
+static void
+create_map_tile (TestTile *tile)
+{
+  CoglHandle buffer;
+  guchar *map;
+  unsigned int i;
+  unsigned int stride = 0;
+  guint8 *line;
+
+  buffer = cogl_pixel_array_new_with_size (TILE_SIZE,
+                                           TILE_SIZE,
+                                           COGL_PIXEL_FORMAT_RGBA_8888,
+                                           &stride);
+
+  g_assert (cogl_is_pixel_array (buffer));
+  g_assert (cogl_is_buffer (buffer));
+
+  cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+  g_assert_cmpint (cogl_buffer_get_update_hint (buffer),
+            ==,
+            COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+
+  map = cogl_buffer_map (buffer,
+                         COGL_BUFFER_ACCESS_WRITE,
+                         COGL_BUFFER_MAP_HINT_DISCARD);
+  g_assert (map);
+
+  line = g_alloca (TILE_SIZE * 4);
+  for (i = 0; i < TILE_SIZE * 4; i += 4)
+    memcpy (line + i, &tile->color, 4);
+
+  for (i = 0; i < TILE_SIZE; i++)
+    memcpy (map + stride * i, line, TILE_SIZE * 4);
+
+  cogl_buffer_unmap (buffer);
+
+  tile->buffer = buffer;
+  tile->texture = create_texture_from_buffer (tile->buffer);
+}
+
+#if 0
+static void
+create_set_region_tile (TestTile *tile)
+{
+  CoglHandle buffer;
+  ClutterColor bottom_color;
+  unsigned int rowstride = 0;
+  guchar *data;
+  unsigned int i;
+
+  buffer = cogl_pixel_array_with_size (TILE_SIZE,
+                                       TILE_SIZE,
+                                       COGL_PIXEL_FORMAT_RGBA_8888,
+                                       &rowstride);
+
+  g_assert (cogl_is_pixel_array (buffer));
+  g_assert (cogl_is_buffer (buffer));
+
+  /* while at it, set/get the hint */
+  cogl_buffer_set_hint (buffer, COGL_BUFFER_HINT_STATIC_TEXTURE);
+  g_assert (cogl_buffer_get_hint (buffer) == COGL_BUFFER_HINT_STATIC_TEXTURE);
+
+  data = g_malloc (TILE_SIZE * TILE_SIZE * 4);
+  /* create a buffer with the data we want to copy to the buffer */
+  for (i = 0; i < TILE_SIZE * TILE_SIZE * 4; i += 4)
+      memcpy (data + i, &tile->color, 4);
+
+  cogl_pixel_array_set_region (buffer,
+                                data,
+                                TILE_SIZE, TILE_SIZE,
+                                TILE_SIZE,
+                                0, 0);
+
+  bottom_color.red = tile->color.red;
+  bottom_color.green = tile->color.blue;
+  bottom_color.blue = tile->color.green;
+  bottom_color.alpha = tile->color.alpha;
+  for (i = 0; i < TILE_SIZE / 2; i++)
+    memcpy (data + i, &bottom_color, 4);
+
+  cogl_buffer_set_data (buffer, data, 0, TILE_SIZE * TILE_SIZE * 4 / 2);
+
+  g_free (data);
+
+  tile->buffer = buffer;
+  tile->texture = create_texture_from_buffer (tile->buffer);
+}
+#endif
+
+static void
+create_set_data_tile (TestTile *tile)
+{
+  CoglHandle buffer;
+  unsigned int rowstride = 0;
+  gboolean res;
+  guchar *data;
+  unsigned int i;
+
+  buffer = cogl_pixel_array_new_with_size (TILE_SIZE,
+                                           TILE_SIZE,
+                                           COGL_PIXEL_FORMAT_RGBA_8888,
+                                           &rowstride);
+
+  g_assert (cogl_is_pixel_array (buffer));
+  g_assert (cogl_is_buffer (buffer));
+  g_assert_cmpint (cogl_buffer_get_size (buffer), ==, rowstride * TILE_SIZE);
+
+  /* create a buffer with the data we want to copy to the buffer */
+  data = g_malloc (TILE_SIZE * TILE_SIZE * 4);
+  for (i = 0; i < TILE_SIZE * TILE_SIZE * 4; i += 4)
+      memcpy (data + i, &tile->color, 4);
+
+  /* FIXME: this doesn't consider the rowstride */
+  res = cogl_buffer_set_data (buffer, 0, data, TILE_SIZE * TILE_SIZE * 4);
+  g_assert (res);
+
+  g_free (data);
+
+  tile->buffer = buffer;
+  tile->texture = create_texture_from_buffer (tile->buffer);
+}
+
+static void
+draw_frame (TestState *state)
+{
+  unsigned int i;
+
+  /* Paint the textures */
+  for (i = 0; i < NB_TILES; i++)
+    {
+      cogl_set_source_texture (state->tiles[i].texture);
+      cogl_rectangle (state->tiles[i].x,
+                      state->tiles[i].y,
+                      state->tiles[i].x + TILE_SIZE,
+                      state->tiles[i].y + TILE_SIZE);
+    }
+
+}
+
+static gboolean
+validate_tile (TestState *state,
+               TestTile  *tile)
+{
+  int x, y;
+  guchar *pixels, *p;
+
+  p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+                                          tile->x,
+                                          tile->y,
+                                          TILE_SIZE,
+                                          TILE_SIZE);
+
+  /* Check whether the center of each division is the right color */
+  for (y = 0; y < TILE_SIZE; y++)
+    for (x = 0; x < TILE_SIZE; x++)
+      {
+        if (p[0] != tile->color.red ||
+            p[1] != tile->color.green ||
+            p[2] != tile->color.blue ||
+            p[3] != tile->color.alpha)
+          {
+            return FALSE;
+          }
+
+        p += 4;
+      }
+
+  return TRUE;
+}
+
+static void
+validate_result (TestState *state)
+{
+  unsigned int i;
+
+  for (i = 0; i < NB_TILES; i++)
+    g_assert (validate_tile (state, &state->tiles[i]));
+
+  /* comment this if you want to see what's being drawn */
+#if 1
+  clutter_main_quit ();
+#endif
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  int frame_num;
+
+  draw_frame (state);
+
+  /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+   * another paint run so to avoid infinite recursion we only aim to validate
+   * the first frame. */
+  frame_num = state->frame++;
+  if (frame_num == 1)
+    validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_pixel_array (TestUtilsGTestFixture *fixture,
+		       void *            data)
+{
+  TestState state;
+  unsigned int idle_source;
+  unsigned int paint_handler, i;
+  static TestTile tiles[NB_TILES] =
+    {
+        /*         color             x  y buffer tex */
+
+        /* MAP */
+        { { 0xff, 0x00, 0x00, 0xff }, 0.0f, 0.0f, NULL, NULL },
+#if 0
+        /* SET_REGION */
+        { { 0x7e, 0x7e, 0xff, 0x7e }, 0.0f, TILE_SIZE, NULL, NULL },
+#endif
+        /* SET_DATA */
+        { { 0x7e, 0xff, 0x7e, 0xff }, 0.0f, TILE_SIZE, NULL, NULL }
+    };
+
+  state.frame = 0;
+
+  state.stage = clutter_stage_get_default ();
+
+  create_map_tile (&tiles[TILE_MAP]);
+#if 0
+  create_set_region_tile (&tiles[TILE_SET_REGION]);
+#endif
+  create_set_data_tile (&tiles[TILE_SET_DATA]);
+
+  state.tiles = tiles;
+
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, state.stage);
+
+  paint_handler = g_signal_connect_after (state.stage, "paint",
+                                          G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (state.stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+  g_signal_handler_disconnect (state.stage, paint_handler);
+
+  for (i = 0; i < NB_TILES; i++)
+    {
+      cogl_handle_unref (state.tiles[i].buffer);
+      cogl_handle_unref (state.tiles[i].texture);
+    }
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-premult.c b/tests/conform/test-premult.c
new file mode 100644
index 0000000..6b73aca
--- /dev/null
+++ b/tests/conform/test-premult.c
@@ -0,0 +1,367 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+#define QUAD_WIDTH 20
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define MASK_RED(COLOR)   ((COLOR & 0xff000000) >> 24)
+#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16)
+#define MASK_BLUE(COLOR)  ((COLOR & 0xff00) >> 8)
+#define MASK_ALPHA(COLOR) (COLOR & 0xff)
+
+typedef struct _TestState
+{
+  ClutterGeometry stage_geom;
+  CoglHandle passthrough_material;
+} TestState;
+
+
+static void
+check_pixel (GLubyte *pixel, guint32 color)
+{
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+
+  if (g_test_verbose ())
+    g_print ("  expected = %x, %x, %x, %x\n",
+             r, g, b, a);
+  /* FIXME - allow for hardware in-precision */
+  g_assert (pixel[RED] == r);
+  g_assert (pixel[GREEN] == g);
+  g_assert (pixel[BLUE] == b);
+
+  /* FIXME
+   * We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+  /* g_assert (pixel[ALPHA] == a); */
+}
+
+static guchar *
+gen_tex_data (guint32 color)
+{
+  guchar *tex_data, *p;
+  guint8 r = MASK_RED (color);
+  guint8 g = MASK_GREEN (color);
+  guint8 b = MASK_BLUE (color);
+  guint8 a = MASK_ALPHA (color);
+
+  tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
+
+  for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
+    {
+      *(--p) = a;
+      *(--p) = b;
+      *(--p) = g;
+      *(--p) = r;
+    }
+
+  return tex_data;
+}
+
+static CoglHandle
+make_texture (guint32 color,
+              CoglPixelFormat src_format,
+              CoglPixelFormat internal_format)
+{
+  CoglHandle tex;
+  guchar *tex_data = gen_tex_data (color);
+
+  tex = cogl_texture_new_from_data (QUAD_WIDTH,
+                                    QUAD_WIDTH,
+                                    COGL_TEXTURE_NONE,
+                                    src_format,
+                                    internal_format,
+                                    QUAD_WIDTH * 4,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  return tex;
+}
+
+static void
+check_texture (TestState *state,
+               int x,
+               int y,
+               CoglHandle tex,
+               guint32 expected_result)
+{
+  guchar        pixel[4];
+  int           y_off;
+  int           x_off;
+
+  cogl_material_set_layer (state->passthrough_material, 0, tex);
+
+  cogl_set_source (state->passthrough_material);
+  cogl_rectangle (x * QUAD_WIDTH,
+                  y * QUAD_WIDTH,
+                  x * QUAD_WIDTH + QUAD_WIDTH,
+                  y * QUAD_WIDTH + QUAD_WIDTH);
+
+  /* See what we got... */
+
+  y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2);
+  x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
+
+  cogl_read_pixels (x_off, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    {
+      g_print ("check texture (%d, %d):\n", x, y);
+      g_print ("  result = %02x, %02x, %02x, %02x\n",
+               pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
+    }
+
+  check_pixel (pixel, expected_result);
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglHandle tex;
+  guchar *tex_data;
+
+  /* If the user explicitly specifies an unmultiplied internal format then
+   * Cogl shouldn't automatically premultiply the given texture data... */
+  if (g_test_verbose ())
+    g_print ("make_texture (0xff00ff80, "
+                            "src = RGBA_8888, internal = RGBA_8888)\n");
+  tex = make_texture (0xff00ff80,
+                      COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+  check_texture (state, 0, 0, /* position */
+                 tex,
+                 0xff00ff80); /* expected */
+
+  /* If the user explicitly requests a premultiplied internal format and
+   * gives unmultiplied src data then Cogl should always premultiply that
+   * for us */
+  if (g_test_verbose ())
+    g_print ("make_texture (0xff00ff80, "
+                            "src = RGBA_8888, internal = RGBA_8888_PRE)\n");
+  tex = make_texture (0xff00ff80,
+                      COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+  check_texture (state, 1, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+  /* If the user gives COGL_PIXEL_FORMAT_ANY for the internal format then
+   * by default Cogl should premultiply the given texture data...
+   * (In the future there will be additional Cogl API to control this
+   *  behaviour) */
+  if (g_test_verbose ())
+    g_print ("make_texture (0xff00ff80, "
+                            "src = RGBA_8888, internal = ANY)\n");
+  tex = make_texture (0xff00ff80,
+                      COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+                      COGL_PIXEL_FORMAT_ANY); /* internal format */
+  check_texture (state, 2, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+  /* If the user requests a premultiplied internal texture format and supplies
+   * premultiplied source data, Cogl should never modify that source data...
+   */
+  if (g_test_verbose ())
+    g_print ("make_texture (0x80008080, "
+                            "src = RGBA_8888_PRE, "
+                            "internal = RGBA_8888_PRE)\n");
+  tex = make_texture (0x80008080,
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+  check_texture (state, 3, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+  /* If the user requests an unmultiplied internal texture format, but
+   * supplies premultiplied source data, then Cogl should always
+   * un-premultiply the source data... */
+  if (g_test_verbose ())
+    g_print ("make_texture (0x80008080, "
+                            "src = RGBA_8888_PRE, internal = RGBA_8888)\n");
+  tex = make_texture (0x80008080,
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+  check_texture (state, 4, 0, /* position */
+                 tex,
+                 0xff00ff80); /* expected */
+
+  /* If the user allows any internal texture format and provides premultipled
+   * source data then by default Cogl shouldn't modify the source data...
+   * (In the future there will be additional Cogl API to control this
+   *  behaviour) */
+  if (g_test_verbose ())
+    g_print ("make_texture (0x80008080, "
+                            "src = RGBA_8888_PRE, internal = ANY)\n");
+  tex = make_texture (0x80008080,
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+                      COGL_PIXEL_FORMAT_ANY); /* internal format */
+  check_texture (state, 5, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+  /*
+   * Test cogl_texture_set_region() ....
+   */
+
+  if (g_test_verbose ())
+    g_print ("make_texture (0xDEADBEEF, "
+                            "src = RGBA_8888, internal = RGBA_8888)\n");
+  tex = make_texture (0xDEADBEEF,
+                      COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+  if (g_test_verbose ())
+    g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+  tex_data = gen_tex_data (0xff00ff80);
+  cogl_texture_set_region (tex,
+                           0, 0, /* src x, y */
+                           0, 0, /* dst x, y */
+                           QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
+                           QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
+                           COGL_PIXEL_FORMAT_RGBA_8888,
+                           0, /* auto compute row stride */
+                           tex_data);
+  check_texture (state, 6, 0, /* position */
+                 tex,
+                 0xff00ff80); /* expected */
+
+  /* Updating a texture region for an unmultiplied texture using premultiplied
+   * region data should result in Cogl unmultiplying the given region data...
+   */
+  if (g_test_verbose ())
+    g_print ("make_texture (0xDEADBEEF, "
+                            "src = RGBA_8888, internal = RGBA_8888)\n");
+  tex = make_texture (0xDEADBEEF,
+                      COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
+  if (g_test_verbose ())
+    g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+  tex_data = gen_tex_data (0x80008080);
+  cogl_texture_set_region (tex,
+                           0, 0, /* src x, y */
+                           0, 0, /* dst x, y */
+                           QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
+                           QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
+                           COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                           0, /* auto compute row stride */
+                           tex_data);
+  check_texture (state, 7, 0, /* position */
+                 tex,
+                 0xff00ff80); /* expected */
+
+
+  if (g_test_verbose ())
+    g_print ("make_texture (0xDEADBEEF, "
+                            "src = RGBA_8888_PRE, "
+                            "internal = RGBA_8888_PRE)\n");
+  tex = make_texture (0xDEADBEEF,
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+  if (g_test_verbose ())
+    g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
+  tex_data = gen_tex_data (0x80008080);
+  cogl_texture_set_region (tex,
+                           0, 0, /* src x, y */
+                           0, 0, /* dst x, y */
+                           QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
+                           QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
+                           COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                           0, /* auto compute row stride */
+                           tex_data);
+  check_texture (state, 8, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+
+  /* Updating a texture region for a premultiplied texture using unmultiplied
+   * region data should result in Cogl premultiplying the given region data...
+   */
+  if (g_test_verbose ())
+    g_print ("make_texture (0xDEADBEEF, "
+                            "src = RGBA_8888_PRE, "
+                            "internal = RGBA_8888_PRE)\n");
+  tex = make_texture (0xDEADBEEF,
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
+                      COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
+  if (g_test_verbose ())
+    g_print ("set_region (0xff00ff80, RGBA_8888)\n");
+  tex_data = gen_tex_data (0xff00ff80);
+  cogl_texture_set_region (tex,
+                           0, 0, /* src x, y */
+                           0, 0, /* dst x, y */
+                           QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
+                           QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
+                           COGL_PIXEL_FORMAT_RGBA_8888,
+                           0, /* auto compute row stride */
+                           tex_data);
+  check_texture (state, 9, 0, /* position */
+                 tex,
+                 0x80008080); /* expected */
+
+  /* Comment this out if you want visual feedback for what this test paints */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_premult (TestUtilsGTestFixture *fixture,
+                   void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  state.passthrough_material = cogl_material_new ();
+  cogl_material_set_blend (state.passthrough_material,
+                           "RGBA = ADD (SRC_COLOR, 0)", NULL);
+  cogl_material_set_layer_combine (state.passthrough_material, 0,
+                                   "RGBA = REPLACE (TEXTURE)", NULL);
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-primitive.c b/tests/conform/test-primitive.c
new file mode 100644
index 0000000..cffbfd3
--- /dev/null
+++ b/tests/conform/test-primitive.c
@@ -0,0 +1,230 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0xff, 0x00, 0xff };
+static const ClutterColor prim_color = { 0xff, 0x00, 0xff, 0xff };
+static const ClutterColor tex_color = { 0x00, 0x00, 0xff, 0xff };
+
+typedef CoglPrimitive * (* TestPrimFunc) (ClutterColor *expected_color);
+
+static CoglPrimitive *
+test_prim_p2 (ClutterColor *expected_color)
+{
+  static const CoglVertexP2 verts[] =
+    { { 0, 0 }, { 0, 10 }, { 10, 0 } };
+
+  return cogl_primitive_new_p2 (COGL_VERTICES_MODE_TRIANGLES,
+                                3, /* n_vertices */
+                                verts);
+}
+
+static CoglPrimitive *
+test_prim_p3 (ClutterColor *expected_color)
+{
+  static const CoglVertexP3 verts[] =
+    { { 0, 0, 0 }, { 0, 10, 0 }, { 10, 0, 0 } };
+
+  return cogl_primitive_new_p3 (COGL_VERTICES_MODE_TRIANGLES,
+                                3, /* n_vertices */
+                                verts);
+}
+
+static CoglPrimitive *
+test_prim_p2c4 (ClutterColor *expected_color)
+{
+  static const CoglVertexP2C4 verts[] =
+    { { 0, 0, 255, 255, 0, 255 },
+      { 0, 10, 255, 255, 0, 255 },
+      { 10, 0, 255, 255, 0, 255 } };
+
+  expected_color->red = 255;
+  expected_color->green = 255;
+  expected_color->blue = 0;
+
+  return cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
+                                  3, /* n_vertices */
+                                  verts);
+}
+
+static CoglPrimitive *
+test_prim_p3c4 (ClutterColor *expected_color)
+{
+  static const CoglVertexP3C4 verts[] =
+    { { 0, 0, 0, 255, 255, 0, 255 },
+      { 0, 10, 0, 255, 255, 0, 255 },
+      { 10, 0, 0, 255, 255, 0, 255 } };
+
+  expected_color->red = 255;
+  expected_color->green = 255;
+  expected_color->blue = 0;
+
+  return cogl_primitive_new_p3c4 (COGL_VERTICES_MODE_TRIANGLES,
+                                  3, /* n_vertices */
+                                  verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2 (ClutterColor *expected_color)
+{
+  static const CoglVertexP2T2 verts[] =
+    { { 0, 0, 1, 0 },
+      { 0, 10, 1, 0 },
+      { 10, 0, 1, 0 } };
+
+  *expected_color = tex_color;
+
+  return cogl_primitive_new_p2t2 (COGL_VERTICES_MODE_TRIANGLES,
+                                  3, /* n_vertices */
+                                  verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2 (ClutterColor *expected_color)
+{
+  static const CoglVertexP3T2 verts[] =
+    { { 0, 0, 0, 1, 0 },
+      { 0, 10, 0, 1, 0 },
+      { 10, 0, 0, 1, 0 } };
+
+  *expected_color = tex_color;
+
+  return cogl_primitive_new_p3t2 (COGL_VERTICES_MODE_TRIANGLES,
+                                  3, /* n_vertices */
+                                  verts);
+}
+
+static CoglPrimitive *
+test_prim_p2t2c4 (ClutterColor *expected_color)
+{
+  static const CoglVertexP2T2C4 verts[] =
+    { { 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+      { 0, 10, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+      { 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } };
+
+  *expected_color = tex_color;
+  expected_color->blue = 0xf0;
+
+  return cogl_primitive_new_p2t2c4 (COGL_VERTICES_MODE_TRIANGLES,
+                                    3, /* n_vertices */
+                                    verts);
+}
+
+static CoglPrimitive *
+test_prim_p3t2c4 (ClutterColor *expected_color)
+{
+  static const CoglVertexP3T2C4 verts[] =
+    { { 0, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+      { 0, 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff },
+      { 10, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } };
+
+  *expected_color = tex_color;
+  expected_color->blue = 0xf0;
+
+  return cogl_primitive_new_p3t2c4 (COGL_VERTICES_MODE_TRIANGLES,
+                                    3, /* n_vertices */
+                                    verts);
+}
+
+static const TestPrimFunc
+test_prim_funcs[] =
+  {
+    test_prim_p2,
+    test_prim_p3,
+    test_prim_p2c4,
+    test_prim_p3c4,
+    test_prim_p2t2,
+    test_prim_p3t2,
+    test_prim_p2t2c4,
+    test_prim_p3t2c4
+  };
+
+static void
+paint_cb (void)
+{
+  CoglPipeline *pipeline;
+  CoglHandle tex;
+  guint8 tex_data[6];
+  int i;
+
+  /* Create a two pixel texture. The first pixel is white and the
+     second pixel is tex_color. The assumption is that if no texture
+     coordinates are specified then it will default to 0,0 and get
+     white */
+  tex_data[0] = 255;
+  tex_data[1] = 255;
+  tex_data[2] = 255;
+  tex_data[3] = tex_color.red;
+  tex_data[4] = tex_color.green;
+  tex_data[5] = tex_color.blue;
+  tex = cogl_texture_new_from_data (2, 1, /* size */
+                                    COGL_TEXTURE_NO_ATLAS,
+                                    COGL_PIXEL_FORMAT_RGB_888,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    6, /* rowstride */
+                                    tex_data);
+  pipeline = cogl_pipeline_new ();
+  cogl_pipeline_set_color4ub (pipeline,
+                              prim_color.red,
+                              prim_color.green,
+                              prim_color.blue,
+                              prim_color.alpha);
+  cogl_pipeline_set_layer_texture (pipeline, 0, tex);
+  cogl_handle_unref (tex);
+  cogl_set_source (pipeline);
+  cogl_object_unref (pipeline);
+
+  for (i = 0; i < G_N_ELEMENTS (test_prim_funcs); i++)
+    {
+      CoglPrimitive *prim;
+      ClutterColor expected_color = prim_color;
+      guint8 pixel[4];
+
+      prim = test_prim_funcs[i] (&expected_color);
+
+      cogl_push_matrix ();
+      cogl_translate (i * 10, 0, 0);
+      cogl_primitive_draw (prim);
+      cogl_pop_matrix ();
+
+      cogl_read_pixels (i * 10 + 2, 2, 1, 1,
+                        COGL_READ_PIXELS_COLOR_BUFFER,
+                        COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                        pixel);
+
+      g_assert_cmpint (pixel[0], ==, expected_color.red);
+      g_assert_cmpint (pixel[1], ==, expected_color.green);
+      g_assert_cmpint (pixel[2], ==, expected_color.blue);
+
+      cogl_object_unref (prim);
+    }
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+void
+test_cogl_primitive (TestUtilsGTestFixture *fixture,
+                     void *data)
+{
+  ClutterActor *stage;
+  unsigned int paint_handler;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  paint_handler = g_signal_connect_after (stage, "paint",
+                                          G_CALLBACK (paint_cb), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_main ();
+
+  g_signal_handler_disconnect (stage, paint_handler);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-readpixels.c b/tests/conform/test-readpixels.c
new file mode 100644
index 0000000..0890f9a
--- /dev/null
+++ b/tests/conform/test-readpixels.c
@@ -0,0 +1,178 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+#define FRAMEBUFFER_WIDTH  640
+#define FRAMEBUFFER_HEIGHT 480
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+
+static void
+on_paint (ClutterActor *actor, void *state)
+{
+  float saved_viewport[4];
+  CoglMatrix saved_projection;
+  CoglMatrix projection;
+  CoglMatrix modelview;
+  guchar *data;
+  CoglHandle tex;
+  CoglHandle offscreen;
+  guint32 *pixels;
+  guint8 *pixelsc;
+
+  /* Save the Clutter viewport/matrices and load identity matrices */
+
+  cogl_get_viewport (saved_viewport);
+  cogl_get_projection_matrix (&saved_projection);
+  cogl_push_matrix ();
+
+  cogl_matrix_init_identity (&projection);
+  cogl_matrix_init_identity (&modelview);
+
+  cogl_set_projection_matrix (&projection);
+  cogl_set_modelview_matrix (&modelview);
+
+  /* All offscreen rendering is done upside down so the first thing we
+   * verify is reading back grid of colors from a CoglOffscreen framebuffer
+   */
+
+  data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+  tex = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                                    COGL_TEXTURE_NO_SLICING,
+                                    COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */
+                                    COGL_PIXEL_FORMAT_ANY, /* internal fmt */
+                                    FRAMEBUFFER_WIDTH * 4, /* rowstride */
+                                    data);
+  g_free (data);
+  offscreen = cogl_offscreen_new_to_texture (tex);
+
+  cogl_push_framebuffer (offscreen);
+
+  /* red, top left */
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_rectangle (-1, 1, 0, 0);
+  /* green, top right */
+  cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+  cogl_rectangle (0, 1, 1, 0);
+  /* blue, bottom left */
+  cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+  cogl_rectangle (-1, 0, 0, -1);
+  /* white, bottom right */
+  cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+  cogl_rectangle (0, 0, 1, -1);
+
+  pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+  cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    (guchar *)pixels);
+
+  g_assert_cmpint (pixels[0], ==, 0xff0000ff);
+  g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00);
+  g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000);
+  g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff);
+  g_free (pixels);
+
+  cogl_pop_framebuffer ();
+  cogl_handle_unref (offscreen);
+
+  /* Now verify reading back from an onscreen framebuffer...
+   */
+
+  cogl_set_source_texture (tex);
+  cogl_rectangle (-1, 1, 1, -1);
+
+  pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+  cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    (guchar *)pixels);
+
+  g_assert_cmpint (pixels[0], ==, 0xff0000ff);
+  g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00);
+  g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000);
+  g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff);
+  g_free (pixels);
+
+  /* Verify using BGR format */
+
+  cogl_set_source_texture (tex);
+  cogl_rectangle (-1, 1, 1, -1);
+
+  pixelsc = g_malloc0 (FRAMEBUFFER_WIDTH * 3 * FRAMEBUFFER_HEIGHT);
+  cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_BGR_888,
+                    (guchar *)pixelsc);
+
+  g_assert_cmpint (pixelsc[0], ==, 0x00);
+  g_assert_cmpint (pixelsc[1], ==, 0x00);
+  g_assert_cmpint (pixelsc[2], ==, 0xff);
+
+  g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 0], ==, 0x00);
+  g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 1], ==, 0xff);
+  g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 2], ==, 0x00);
+
+  g_free (pixelsc);
+
+  cogl_handle_unref (tex);
+
+  /* Restore the viewport and matrices state */
+  cogl_set_viewport (saved_viewport[0],
+                     saved_viewport[1],
+                     saved_viewport[2],
+                     saved_viewport[3]);
+  cogl_set_projection_matrix (&saved_projection);
+  cogl_pop_matrix ();
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_readpixels (TestUtilsGTestFixture *fixture,
+                      void *data)
+{
+  unsigned int idle_source;
+  ClutterActor *stage;
+
+  stage = clutter_stage_get_default ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+  g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL);
+
+  clutter_actor_show (stage);
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-sub-texture.c b/tests/conform/test-sub-texture.c
new file mode 100644
index 0000000..038b4ac
--- /dev/null
+++ b/tests/conform/test-sub-texture.c
@@ -0,0 +1,371 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#define SOURCE_SIZE        32
+#define SOURCE_DIVISIONS_X 2
+#define SOURCE_DIVISIONS_Y 2
+#define DIVISION_WIDTH     (SOURCE_SIZE / SOURCE_DIVISIONS_X)
+#define DIVISION_HEIGHT    (SOURCE_SIZE / SOURCE_DIVISIONS_Y)
+
+#define TEST_INSET         1
+
+static const ClutterColor
+corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] =
+  {
+    { 0xff, 0x00, 0x00, 0xff }, /* red top left */
+    { 0x00, 0xff, 0x00, 0xff }, /* green top right */
+    { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */
+    { 0xff, 0x00, 0xff, 0xff }  /* purple bottom right */
+  };
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  unsigned int frame;
+
+  CoglHandle tex;
+} TestState;
+
+static CoglHandle
+create_source (void)
+{
+  int dx, dy;
+  guchar *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4);
+
+  /* Create a texture with a different coloured rectangle at each
+     corner */
+  for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++)
+    for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++)
+      {
+        guchar *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 +
+                     dx * DIVISION_WIDTH * 4);
+        int x, y;
+
+        for (y = 0; y < DIVISION_HEIGHT; y++)
+          {
+            for (x = 0; x < DIVISION_WIDTH; x++)
+              {
+                memcpy (p, corner_colors + dx + dy * SOURCE_DIVISIONS_X, 4);
+                p += 4;
+              }
+
+            p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4;
+          }
+      }
+
+  return cogl_texture_new_from_data (SOURCE_SIZE, SOURCE_SIZE,
+                                     COGL_TEXTURE_NONE,
+                                     COGL_PIXEL_FORMAT_RGBA_8888,
+                                     COGL_PIXEL_FORMAT_ANY,
+                                     SOURCE_SIZE * 4,
+                                     data);
+}
+
+static CoglHandle
+create_test_texture (void)
+{
+  CoglHandle tex;
+  guint8 *data = g_malloc (256 * 256 * 4), *p = data;
+  int x, y;
+
+  /* Create a texture that is 256x256 where the red component ranges
+     from 0->255 along the x axis and the green component ranges from
+     0->255 along the y axis. The blue and alpha components are all
+     255 */
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+        *(p++) = x;
+        *(p++) = y;
+        *(p++) = 255;
+        *(p++) = 255;
+      }
+
+  tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    256 * 4,
+                                    data);
+
+  g_free (data);
+
+  return tex;
+}
+
+static void
+draw_frame (TestState *state)
+{
+  CoglHandle full_texture, sub_texture, sub_sub_texture;
+
+  /* Create a sub texture of the bottom right quarter of the texture */
+  sub_texture = cogl_texture_new_from_sub_texture (state->tex,
+                                                   DIVISION_WIDTH,
+                                                   DIVISION_HEIGHT,
+                                                   DIVISION_WIDTH,
+                                                   DIVISION_HEIGHT);
+
+  /* Paint it */
+  cogl_set_source_texture (sub_texture);
+  cogl_rectangle (0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT);
+
+  cogl_handle_unref (sub_texture);
+
+  /* Repeat a sub texture of the top half of the full texture. This is
+     documented to be undefined so it doesn't technically have to work
+     but it will with the current implementation */
+  sub_texture = cogl_texture_new_from_sub_texture (state->tex,
+                                                   0, 0,
+                                                   SOURCE_SIZE,
+                                                   DIVISION_HEIGHT);
+  cogl_set_source_texture (sub_texture);
+  cogl_rectangle_with_texture_coords (0.0f, SOURCE_SIZE,
+                                      SOURCE_SIZE * 2.0f, SOURCE_SIZE * 1.5f,
+                                      0.0f, 0.0f,
+                                      2.0f, 1.0f);
+  cogl_handle_unref (sub_texture);
+
+  /* Create a sub texture of a sub texture */
+  full_texture = create_test_texture ();
+  sub_texture = cogl_texture_new_from_sub_texture (full_texture,
+                                                   20, 10, 30, 20);
+  sub_sub_texture = cogl_texture_new_from_sub_texture (sub_texture,
+                                                       20, 10, 10, 10);
+  cogl_set_source_texture (sub_sub_texture);
+  cogl_rectangle (0.0f, SOURCE_SIZE * 2.0f,
+                  10.0f, SOURCE_SIZE * 2.0f + 10.0f);
+  cogl_handle_unref (sub_sub_texture);
+  cogl_handle_unref (sub_texture);
+  cogl_handle_unref (full_texture);
+}
+
+static gboolean
+validate_part (TestState *state,
+               int xpos, int ypos,
+               int width, int height,
+               const ClutterColor *color)
+{
+  int x, y;
+  gboolean pass = TRUE;
+  guchar *pixels, *p;
+
+  p = pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+                                          xpos + TEST_INSET,
+                                          ypos + TEST_INSET,
+                                          width - TEST_INSET - 2,
+                                          height - TEST_INSET - 2);
+
+  /* Check whether the center of each division is the right color */
+  for (y = 0; y < height - TEST_INSET - 2; y++)
+    for (x = 0; x < width - TEST_INSET - 2; x++)
+      {
+        if (p[0] != color->red ||
+            p[1] != color->green ||
+            p[2] != color->blue)
+          pass = FALSE;
+
+        p += 4;
+      }
+
+  return pass;
+}
+
+static guint8 *
+create_update_data (void)
+{
+  guint8 *data = g_malloc (256 * 256 * 4), *p = data;
+  int x, y;
+
+  /* Create some image data that is 256x256 where the blue component
+     ranges from 0->255 along the x axis and the alpha component
+     ranges from 0->255 along the y axis. The red and green components
+     are all zero */
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+        *(p++) = 0;
+        *(p++) = 0;
+        *(p++) = x;
+        *(p++) = y;
+      }
+
+  return data;
+}
+
+static void
+validate_result (TestState *state)
+{
+  int i, division_num, x, y;
+  CoglHandle sub_texture, test_tex;
+  guchar *texture_data, *p;
+  int tex_width, tex_height;
+
+  /* Sub texture of the bottom right corner of the texture */
+  g_assert (validate_part (state, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT,
+                           corner_colors +
+                           (SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X +
+                           SOURCE_DIVISIONS_X - 1));
+
+  /* Sub texture of the top half repeated horizontally */
+  for (i = 0; i < 2; i++)
+    for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++)
+      g_assert (validate_part (state,
+                               i * SOURCE_SIZE + division_num * DIVISION_WIDTH,
+                               SOURCE_SIZE,
+                               DIVISION_WIDTH, DIVISION_HEIGHT,
+                               corner_colors + division_num));
+
+  /* Sub sub texture */
+  p = texture_data = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage),
+                                                0, SOURCE_SIZE * 2, 10, 10);
+  for (y = 0; y < 10; y++)
+    for (x = 0; x < 10; x++)
+      {
+        g_assert (*(p++) == x + 40);
+        g_assert (*(p++) == y + 20);
+        p += 2;
+      }
+  g_free (texture_data);
+
+  /* Try reading back the texture data */
+  sub_texture = cogl_texture_new_from_sub_texture (state->tex,
+                                                   SOURCE_SIZE / 4,
+                                                   SOURCE_SIZE / 4,
+                                                   SOURCE_SIZE / 2,
+                                                   SOURCE_SIZE / 2);
+  tex_width = cogl_texture_get_width (sub_texture);
+  tex_height = cogl_texture_get_height (sub_texture);
+  p = texture_data = g_malloc (tex_width * tex_height * 4);
+  cogl_texture_get_data (sub_texture, COGL_PIXEL_FORMAT_RGBA_8888,
+                         tex_width * 4,
+                         texture_data);
+  for (y = 0; y < tex_height; y++)
+    for (x = 0; x < tex_width; x++)
+      {
+        int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) /
+                     DIVISION_WIDTH);
+        int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) /
+                     DIVISION_HEIGHT);
+        const ClutterColor *color = (corner_colors + div_x +
+                                     div_y * SOURCE_DIVISIONS_X);
+        g_assert (p[0] == color->red);
+        g_assert (p[1] == color->green);
+        g_assert (p[2] == color->blue);
+        p += 4;
+      }
+  g_free (texture_data);
+  cogl_handle_unref (sub_texture);
+
+  /* Create a 256x256 test texture */
+  test_tex = create_test_texture ();
+  /* Create a sub texture the views the center half of the texture */
+  sub_texture = cogl_texture_new_from_sub_texture (test_tex,
+                                                   64, 64, 128, 128);
+  /* Update the center half of the sub texture */
+  texture_data = create_update_data ();
+  cogl_texture_set_region (sub_texture, 0, 0, 32, 32, 64, 64, 256, 256,
+                           COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4,
+                           texture_data);
+  g_free (texture_data);
+  cogl_handle_unref (sub_texture);
+  /* Get the texture data */
+  p = texture_data = g_malloc (256 * 256 * 4);
+  cogl_texture_get_data (test_tex, COGL_PIXEL_FORMAT_RGBA_8888,
+                         256 * 4, texture_data);
+
+  /* Verify the texture data */
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+        /* If we're in the center quarter */
+        if (x >= 96 && x < 160 && y >= 96 && y < 160)
+          {
+            g_assert ((*p++) == 0);
+            g_assert ((*p++) == 0);
+            g_assert ((*p++) == x - 96);
+            g_assert ((*p++) == y - 96);
+          }
+        else
+          {
+            g_assert ((*p++) == x);
+            g_assert ((*p++) == y);
+            g_assert ((*p++) == 255);
+            g_assert ((*p++) == 255);
+          }
+      }
+  g_free (texture_data);
+  cogl_handle_unref (test_tex);
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  int frame_num;
+
+  draw_frame (state);
+
+  /* XXX: validate_result calls clutter_stage_read_pixels which will result in
+   * another paint run so to avoid infinite recursion we only aim to validate
+   * the first frame. */
+  frame_num = state->frame++;
+  if (frame_num == 1)
+    validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_sub_texture (TestUtilsGTestFixture *fixture,
+                       void *data)
+{
+  TestState state;
+  unsigned int idle_source;
+  unsigned int paint_handler;
+
+  state.frame = 0;
+
+  state.stage = clutter_stage_get_default ();
+  state.tex = create_source ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, state.stage);
+
+  paint_handler = g_signal_connect_after (state.stage, "paint",
+                                          G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (state.stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+  g_signal_handler_disconnect (state.stage, paint_handler);
+
+  cogl_handle_unref (state.tex);
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (state.stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-texture-3d.c b/tests/conform/test-texture-3d.c
new file mode 100644
index 0000000..d8a5ee4
--- /dev/null
+++ b/tests/conform/test-texture-3d.c
@@ -0,0 +1,230 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0xff, 0x0, 0xff };
+
+#define TEX_WIDTH        4
+#define TEX_HEIGHT       8
+#define TEX_DEPTH        16
+/* Leave four bytes of padding between each row */
+#define TEX_ROWSTRIDE    (TEX_WIDTH * 4 + 4)
+/* Leave four rows of padding between each image */
+#define TEX_IMAGE_STRIDE ((TEX_HEIGHT + 4) * TEX_ROWSTRIDE)
+
+static CoglHandle
+create_texture_3d (void)
+{
+  int x, y, z;
+  guint8 *data = g_malloc (TEX_IMAGE_STRIDE * TEX_DEPTH);
+  guint8 *p = data;
+  CoglHandle tex;
+  GError *error = NULL;
+
+  for (z = 0; z < TEX_DEPTH; z++)
+    {
+      for (y = 0; y < TEX_HEIGHT; y++)
+        {
+          for (x = 0; x < TEX_WIDTH; x++)
+            {
+              /* Set red, green, blue to values based on x, y, z */
+              *(p++) = 255 - x * 8;
+              *(p++) = y * 8;
+              *(p++) = 255 - z * 8;
+              /* Fully opaque */
+              *(p++) = 0xff;
+            }
+
+          /* Set the padding between rows to 0xde */
+          memset (p, 0xde, TEX_ROWSTRIDE - (TEX_WIDTH * 4));
+          p += TEX_ROWSTRIDE - (TEX_WIDTH * 4);
+        }
+      /* Set the padding between images to 0xad */
+      memset (p, 0xba, TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE));
+      p += TEX_IMAGE_STRIDE - (TEX_HEIGHT * TEX_ROWSTRIDE);
+    }
+
+  tex = cogl_texture_3d_new_from_data (TEX_WIDTH, TEX_HEIGHT, TEX_DEPTH,
+                                       COGL_TEXTURE_NO_AUTO_MIPMAP,
+                                       COGL_PIXEL_FORMAT_RGBA_8888,
+                                       COGL_PIXEL_FORMAT_ANY,
+                                       TEX_ROWSTRIDE,
+                                       TEX_IMAGE_STRIDE,
+                                       data,
+                                       &error);
+
+  if (tex == COGL_INVALID_HANDLE)
+    {
+      g_assert (error != NULL);
+      g_warning ("Failed to create 3D texture: %s", error->message);
+      g_assert_not_reached ();
+    }
+
+  g_free (data);
+
+  return tex;
+}
+
+static void
+draw_frame (void)
+{
+  CoglHandle tex = create_texture_3d ();
+  CoglHandle material = cogl_material_new ();
+  typedef struct { float x, y, s, t, r; } Vert;
+  CoglHandle vbo, indices;
+  Vert *verts, *v;
+  int i;
+
+  cogl_material_set_layer (material, 0, tex);
+  cogl_handle_unref (tex);
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+  cogl_set_source (material);
+  cogl_handle_unref (material);
+
+  /* Render the texture repeated horizontally twice using a regular
+     cogl rectangle. This should end up with the r texture coordinates
+     as zero */
+  cogl_rectangle_with_texture_coords (0.0f, 0.0f, TEX_WIDTH * 2, TEX_HEIGHT,
+                                      0.0f, 0.0f, 2.0f, 1.0f);
+
+  /* Render all of the images in the texture using coordinates from a VBO */
+  v = verts = g_new (Vert, 4 * TEX_DEPTH);
+  for (i = 0; i < TEX_DEPTH; i++)
+    {
+      float r = (i + 0.5f) / TEX_DEPTH;
+
+      v->x = i * TEX_WIDTH;
+      v->y = TEX_HEIGHT;
+      v->s = 0;
+      v->t = 0;
+      v->r = r;
+      v++;
+
+      v->x = i * TEX_WIDTH;
+      v->y = TEX_HEIGHT * 2;
+      v->s = 0;
+      v->t = 1;
+      v->r = r;
+      v++;
+
+      v->x = i * TEX_WIDTH + TEX_WIDTH;
+      v->y = TEX_HEIGHT * 2;
+      v->s = 1;
+      v->t = 1;
+      v->r = r;
+      v++;
+
+      v->x = i * TEX_WIDTH + TEX_WIDTH;
+      v->y = TEX_HEIGHT;
+      v->s = 1;
+      v->t = 0;
+      v->r = r;
+      v++;
+    }
+
+  vbo = cogl_vertex_buffer_new (4 * TEX_DEPTH);
+  cogl_vertex_buffer_add (vbo, "gl_Vertex",
+                          2, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+                          sizeof (Vert),
+                          &verts->x);
+  cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0",
+                          3, COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+                          sizeof (Vert),
+                          &verts->s);
+  cogl_vertex_buffer_submit (vbo);
+
+  g_free (verts);
+
+  indices = cogl_vertex_buffer_indices_get_for_quads (6 * TEX_DEPTH);
+
+  cogl_vertex_buffer_draw_elements (vbo,
+                                    COGL_VERTICES_MODE_TRIANGLES,
+                                    indices,
+                                    0, TEX_DEPTH * 4 - 1,
+                                    0, TEX_DEPTH * 6);
+
+  cogl_handle_unref (vbo);
+}
+
+static void
+validate_block (int block_x, int block_y, int z)
+{
+  guint8 *data, *p;
+  int x, y;
+
+  p = data = g_malloc (TEX_WIDTH * TEX_HEIGHT * 4);
+
+  cogl_read_pixels (block_x * TEX_WIDTH, block_y * TEX_HEIGHT,
+                    TEX_WIDTH, TEX_HEIGHT,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    data);
+
+  for (y = 0; y < TEX_HEIGHT; y++)
+    for (x = 0; x < TEX_WIDTH; x++)
+      {
+        g_assert_cmpint (p[0], ==, 255 - x * 8);
+        g_assert_cmpint (p[1], ==, y * 8);
+        g_assert_cmpint (p[2], ==, 255 - z * 8);
+        p += 4;
+      }
+
+  g_free (data);
+}
+
+static void
+validate_result (void)
+{
+  int i;
+
+  validate_block (0, 0, 0);
+
+  for (i = 0; i < TEX_DEPTH; i++)
+    validate_block (i, 1, i);
+}
+
+static void
+on_paint (void)
+{
+  draw_frame ();
+
+  validate_result ();
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+void
+test_cogl_texture_3d (TestUtilsGTestFixture *fixture,
+                      void *data)
+{
+  ClutterActor *stage;
+  unsigned int paint_handler;
+
+  stage = clutter_stage_get_default ();
+
+  /* Check whether GL supports the rectangle extension. If not we'll
+     just assume the test passes */
+  if (cogl_features_available (COGL_FEATURE_TEXTURE_3D))
+    {
+      clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+      paint_handler = g_signal_connect_after (stage, "paint",
+                                              G_CALLBACK (on_paint), NULL);
+
+      clutter_actor_show (stage);
+
+      clutter_main ();
+
+      g_signal_handler_disconnect (stage, paint_handler);
+
+      if (g_test_verbose ())
+        g_print ("OK\n");
+    }
+  else if (g_test_verbose ())
+    g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-texture-get-set-data.c b/tests/conform/test-texture-get-set-data.c
new file mode 100644
index 0000000..78e302c
--- /dev/null
+++ b/tests/conform/test-texture-get-set-data.c
@@ -0,0 +1,166 @@
+#include <clutter/clutter.h>
+#include <glib.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static void
+check_texture (int width, int height, CoglTextureFlags flags)
+{
+  CoglHandle tex;
+  guint8 *data, *p;
+  int y, x;
+
+  p = data = g_malloc (width * height * 4);
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        *(p++) = x;
+        *(p++) = y;
+        *(p++) = 128;
+        *(p++) = (x ^ y);
+      }
+
+  tex = cogl_texture_new_from_data (width, height,
+                                    flags,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    COGL_PIXEL_FORMAT_RGBA_8888,
+                                    width * 4,
+                                    data);
+
+  /* Replace the bottom right quarter of the data with negated data to
+     test set_region */
+  p = data + (height + 1) * width * 2;
+  for (y = 0; y < height / 2; y++)
+    {
+      for (x = 0; x < width / 2; x++)
+        {
+          p[0] = ~p[0];
+          p[1] = ~p[1];
+          p[2] = ~p[2];
+          p[3] = ~p[3];
+          p += 4;
+        }
+      p += width * 2;
+    }
+  cogl_texture_set_region (tex,
+                           width / 2, /* src_x */
+                           height / 2, /* src_y */
+                           width / 2, /* dst_x */
+                           height / 2, /* dst_y */
+                           width / 2, /* dst_width */
+                           height / 2, /* dst_height */
+                           width,
+                           height,
+                           COGL_PIXEL_FORMAT_RGBA_8888,
+                           width * 4, /* rowstride */
+                           data);
+
+  /* Check passing a NULL pointer and a zero rowstride. The texture
+     should calculate the needed data size and return it */
+  g_assert_cmpint (cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_ANY, 0, NULL),
+                   ==,
+                   width * height * 4);
+
+  /* Try first receiving the data as RGB. This should cause a
+   * conversion */
+  memset (data, 0, width * height * 4);
+
+  cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGB_888,
+                         width * 3, data);
+
+  p = data;
+
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        if (x >= width / 2 && y >= height / 2)
+          {
+            g_assert_cmpint (p[0], ==, ~x & 0xff);
+            g_assert_cmpint (p[1], ==, ~y & 0xff);
+            g_assert_cmpint (p[2], ==, ~128 & 0xff);
+          }
+        else
+          {
+            g_assert_cmpint (p[0], ==, x & 0xff);
+            g_assert_cmpint (p[1], ==, y & 0xff);
+            g_assert_cmpint (p[2], ==, 128);
+          }
+        p += 3;
+      }
+
+  /* Now try receiving the data as RGBA. This should not cause a
+   * conversion and no unpremultiplication because we explicitly set
+   * the internal format when we created the texture */
+  memset (data, 0, width * height * 4);
+
+  cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGBA_8888,
+                         width * 4, data);
+
+  p = data;
+
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        if (x >= width / 2 && y >= height / 2)
+          {
+            g_assert_cmpint (p[0], ==, ~x & 0xff);
+            g_assert_cmpint (p[1], ==, ~y & 0xff);
+            g_assert_cmpint (p[2], ==, ~128 & 0xff);
+            g_assert_cmpint (p[3], ==, ~(x ^ y) & 0xff);
+          }
+        else
+          {
+            g_assert_cmpint (p[0], ==, x & 0xff);
+            g_assert_cmpint (p[1], ==, y & 0xff);
+            g_assert_cmpint (p[2], ==, 128);
+            g_assert_cmpint (p[3], ==, (x ^ y) & 0xff);
+          }
+        p += 4;
+      }
+
+  cogl_handle_unref (tex);
+  g_free (data);
+}
+
+static void
+paint_cb (void)
+{
+  /* First try without atlasing */
+  check_texture (256, 256, COGL_TEXTURE_NO_ATLAS);
+  /* Try again with atlasing. This should end up testing the atlas
+     backend and the sub texture backend */
+  check_texture (256, 256, 0);
+  /* Try with a really big texture in the hope that it will end up
+     sliced. */
+  check_texture (4, 5128, COGL_TEXTURE_NO_ATLAS);
+  /* And in the other direction. */
+  check_texture (5128, 4, COGL_TEXTURE_NO_ATLAS);
+
+  clutter_main_quit ();
+}
+
+void
+test_cogl_texture_get_set_data (TestUtilsGTestFixture *fixture,
+                                void *data)
+{
+  ClutterActor *stage;
+  unsigned int paint_handler;
+
+  /* We create a stage even though we don't usually need it so that if
+     the draw-and-read texture fallback is needed then it will have
+     something to draw to */
+  stage = clutter_stage_get_default ();
+
+  paint_handler = g_signal_connect_after (stage, "paint",
+                                          G_CALLBACK (paint_cb), NULL);
+
+  clutter_actor_show (stage);
+
+  clutter_main ();
+
+  g_signal_handler_disconnect (stage, paint_handler);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/conform/test-texture-mipmaps.c b/tests/conform/test-texture-mipmaps.c
new file mode 100644
index 0000000..22e27b7
--- /dev/null
+++ b/tests/conform/test-texture-mipmaps.c
@@ -0,0 +1,136 @@
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+#define TEX_SIZE 64
+
+typedef struct _TestState
+{
+  unsigned int padding;
+} TestState;
+
+/* Creates a texture where the pixels are evenly divided between
+   selecting just one of the R,G and B components */
+static CoglHandle
+make_texture (void)
+{
+  guchar *tex_data = g_malloc (TEX_SIZE * TEX_SIZE * 3), *p = tex_data;
+  CoglHandle tex;
+  int x, y;
+
+  for (y = 0; y < TEX_SIZE; y++)
+    for (x = 0; x < TEX_SIZE; x++)
+      {
+        memset (p, 0, 3);
+        /* Set one of the components to full. The components should be
+           evenly represented so that each gets a third of the
+           texture */
+        p[(p - tex_data) / (TEX_SIZE * TEX_SIZE * 3 / 3)] = 255;
+        p += 3;
+      }
+
+  tex = cogl_texture_new_from_data (TEX_SIZE, TEX_SIZE, COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGB_888,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    TEX_SIZE * 3,
+                                    tex_data);
+
+  g_free (tex_data);
+
+  return tex;
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglHandle tex;
+  CoglHandle material;
+  guint8 pixels[8];
+
+  tex = make_texture ();
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, tex);
+  cogl_handle_unref (tex);
+
+  /* Render a 1x1 pixel quad without mipmaps */
+  cogl_set_source (material);
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+  cogl_rectangle (0, 0, 1, 1);
+  /* Then with mipmaps */
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+  cogl_rectangle (1, 0, 2, 1);
+
+  cogl_handle_unref (material);
+
+  /* Read back the two pixels we rendered */
+  cogl_read_pixels (0, 0, 2, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixels);
+
+  /* The first pixel should be just one of the colors from the
+     texture. It doesn't matter which one */
+  g_assert ((pixels[0] == 255 && pixels[1] == 0 && pixels[2] == 0) ||
+            (pixels[0] == 0 && pixels[1] == 255 && pixels[2] == 0) ||
+            (pixels[0] == 0 && pixels[1] == 0 && pixels[2] == 255));
+  /* The second pixel should be more or less the average of all of the
+     pixels in the texture. Each component gets a third of the image
+     so each component should be approximately 255/3 */
+  g_assert (ABS (pixels[4] - 255 / 3) <= 3 &&
+            ABS (pixels[5] - 255 / 3) <= 3 &&
+            ABS (pixels[6] - 255 / 3) <= 3);
+
+  /* Comment this out if you want visual feedback for what this test paints */
+#if 1
+  clutter_main_quit ();
+#endif
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_texture_mipmaps (TestUtilsGTestFixture *fixture,
+                           void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  group = clutter_group_new ();
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/conform/test-texture-pixmap-x11.c b/tests/conform/test-texture-pixmap-x11.c
new file mode 100644
index 0000000..69c58a3
--- /dev/null
+++ b/tests/conform/test-texture-pixmap-x11.c
@@ -0,0 +1,245 @@
+#include <clutter/clutter.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff };
+
+#ifdef COGL_HAS_XLIB
+
+#include <clutter/x11/clutter-x11.h>
+#include <cogl/cogl-texture-pixmap-x11.h>
+
+#define PIXMAP_WIDTH 512
+#define PIXMAP_HEIGHT 256
+#define GRID_SQUARE_SIZE 16
+
+/* Coordinates of a square that we'll update */
+#define PIXMAP_CHANGE_X 1
+#define PIXMAP_CHANGE_Y 1
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  CoglHandle tfp;
+  Pixmap pixmap;
+  unsigned int frame_count;
+  Display *display;
+} TestState;
+
+static Pixmap
+create_pixmap (TestState *state)
+{
+  Pixmap pixmap;
+  XGCValues gc_values = { 0, };
+  GC black_gc, white_gc;
+  int screen = DefaultScreen (state->display);
+  int x, y;
+
+  pixmap = XCreatePixmap (state->display,
+                          DefaultRootWindow (state->display),
+                          PIXMAP_WIDTH, PIXMAP_HEIGHT,
+                          DefaultDepth (state->display, screen));
+
+  gc_values.foreground = BlackPixel (state->display, screen);
+  black_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values);
+  gc_values.foreground = WhitePixel (state->display, screen);
+  white_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values);
+
+  /* Draw a grid of alternative black and white rectangles to the
+     pixmap */
+  for (y = 0; y < PIXMAP_HEIGHT / GRID_SQUARE_SIZE; y++)
+    for (x = 0; x < PIXMAP_WIDTH / GRID_SQUARE_SIZE; x++)
+      XFillRectangle (state->display, pixmap,
+                  ((x ^ y) & 1) ? black_gc : white_gc,
+                  x * GRID_SQUARE_SIZE,
+                  y * GRID_SQUARE_SIZE,
+                  GRID_SQUARE_SIZE,
+                  GRID_SQUARE_SIZE);
+
+  XFreeGC (state->display, black_gc);
+  XFreeGC (state->display, white_gc);
+
+  return pixmap;
+}
+
+static void
+update_pixmap (TestState *state)
+{
+  XGCValues gc_values = { 0, };
+  GC black_gc;
+  int screen = DefaultScreen (state->display);
+
+  gc_values.foreground = BlackPixel (state->display, screen);
+  black_gc = XCreateGC (state->display, state->pixmap,
+                        GCForeground, &gc_values);
+
+  /* Fill in one the rectangles with black */
+  XFillRectangle (state->display, state->pixmap,
+                  black_gc,
+                  PIXMAP_CHANGE_X * GRID_SQUARE_SIZE,
+                  PIXMAP_CHANGE_Y * GRID_SQUARE_SIZE,
+                  GRID_SQUARE_SIZE, GRID_SQUARE_SIZE);
+
+  XFreeGC (state->display, black_gc);
+}
+
+static gboolean
+check_paint (TestState *state, int x, int y, int scale)
+{
+  guint8 *data, *p, update_value = 0;
+
+  p = data = g_malloc (PIXMAP_WIDTH * PIXMAP_HEIGHT * 4);
+
+  cogl_read_pixels (x, y, PIXMAP_WIDTH / scale, PIXMAP_HEIGHT / scale,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    data);
+
+  for (y = 0; y < PIXMAP_HEIGHT / scale; y++)
+    for (x = 0; x < PIXMAP_WIDTH / scale; x++)
+      {
+        int grid_x = x * scale / GRID_SQUARE_SIZE;
+        int grid_y = y * scale / GRID_SQUARE_SIZE;
+
+        /* If this is the updatable square then we'll let it be either
+           color but we'll return which one it was */
+        if (grid_x == PIXMAP_CHANGE_X && grid_y == PIXMAP_CHANGE_Y)
+          {
+            if (x % (GRID_SQUARE_SIZE / scale) == 0 &&
+                y % (GRID_SQUARE_SIZE / scale) == 0)
+              update_value = *p;
+            else
+              g_assert_cmpint (p[0], ==, update_value);
+
+            g_assert (p[1] == update_value);
+            g_assert (p[2] == update_value);
+            p += 4;
+          }
+        else
+          {
+            guint8 value = ((grid_x ^ grid_y) & 1) ? 0x00 : 0xff;
+            g_assert_cmpint (*(p++), ==, value);
+            g_assert_cmpint (*(p++), ==, value);
+            g_assert_cmpint (*(p++), ==, value);
+            p++;
+          }
+      }
+
+  g_free (data);
+
+  return update_value == 0x00;
+}
+
+/* We skip these frames first */
+#define FRAME_COUNT_BASE 5
+/* First paint the tfp with no mipmaps */
+#define FRAME_COUNT_NORMAL 6
+/* Then use mipmaps */
+#define FRAME_COUNT_MIPMAP 7
+/* After this frame will start waiting for the pixmap to change */
+#define FRAME_COUNT_UPDATED 8
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  CoglHandle material;
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, state->tfp);
+  if (state->frame_count == FRAME_COUNT_MIPMAP)
+    {
+      const CoglMaterialFilter min_filter =
+        COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST;
+      cogl_material_set_layer_filters (material, 0,
+                                       min_filter,
+                                       COGL_MATERIAL_FILTER_NEAREST);
+    }
+  else
+    cogl_material_set_layer_filters (material, 0,
+                                     COGL_MATERIAL_FILTER_NEAREST,
+                                     COGL_MATERIAL_FILTER_NEAREST);
+  cogl_set_source (material);
+
+  cogl_rectangle (0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT);
+
+  cogl_rectangle (0, PIXMAP_HEIGHT,
+                  PIXMAP_WIDTH / 4, PIXMAP_HEIGHT * 5 / 4);
+
+  if (state->frame_count >= 5)
+    {
+      gboolean big_updated, small_updated;
+
+      big_updated = check_paint (state, 0, 0, 1);
+      small_updated = check_paint (state, 0, PIXMAP_HEIGHT, 4);
+
+      g_assert (big_updated == small_updated);
+
+      if (state->frame_count < FRAME_COUNT_UPDATED)
+        g_assert (big_updated == FALSE);
+      else if (state->frame_count == FRAME_COUNT_UPDATED)
+        /* Change the pixmap and keep drawing until it updates */
+        update_pixmap (state);
+      else if (big_updated)
+        /* If we successfully got the update then the test is over */
+        clutter_main_quit ();
+    }
+
+  state->frame_count++;
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+#endif /* COGL_HAS_XLIB */
+
+void
+test_cogl_texture_pixmap_x11 (TestUtilsGTestFixture *fixture,
+                              void *data)
+{
+#ifdef COGL_HAS_XLIB
+
+  TestState state;
+  unsigned int idle_handler;
+  unsigned int paint_handler;
+
+  state.frame_count = 0;
+  state.stage = clutter_stage_get_default ();
+
+  state.display = clutter_x11_get_default_display ();
+
+  state.pixmap = create_pixmap (&state);
+  state.tfp = cogl_texture_pixmap_x11_new (state.pixmap, TRUE);
+
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  paint_handler = g_signal_connect_after (state.stage, "paint",
+                                          G_CALLBACK (on_paint), &state);
+
+  idle_handler = g_idle_add (queue_redraw, state.stage);
+
+  clutter_actor_show_all (state.stage);
+
+  clutter_main ();
+
+  g_signal_handler_disconnect (state.stage, paint_handler);
+
+  g_source_remove (idle_handler);
+
+  XFreePixmap (state.display, state.pixmap);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+
+#else /* COGL_HAS_XLIB */
+
+  if (g_test_verbose ())
+   g_print ("Skipping\n");
+
+#endif /* COGL_HAS_XLIB */
+}
+
diff --git a/tests/conform/test-texture-rectangle.c b/tests/conform/test-texture-rectangle.c
new file mode 100644
index 0000000..26b8e28
--- /dev/null
+++ b/tests/conform/test-texture-rectangle.c
@@ -0,0 +1,276 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+} TestState;
+
+static CoglHandle
+create_source_rect (void)
+{
+#ifdef GL_TEXTURE_RECTANGLE_ARB
+
+  int x, y;
+  GLint prev_unpack_row_length;
+  GLint prev_unpack_alignment;
+  GLint prev_unpack_skip_rows;
+  GLint prev_unpack_skip_pixles;
+  GLint prev_rectangle_binding;
+  guint8 *data = g_malloc (256 * 256 * 4), *p = data;
+  CoglHandle tex;
+  GLuint gl_tex;
+
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+        *(p++) = x;
+        *(p++) = y;
+        *(p++) = 0;
+        *(p++) = 255;
+      }
+
+  /* We are about to use OpenGL directly to create a TEXTURE_RECTANGLE
+   * texture so we need to save the state that we modify so we can
+   * restore it afterwards and be sure not to interfere with any state
+   * caching that Cogl may do internally.
+   */
+  glGetIntegerv (GL_UNPACK_ROW_LENGTH, &prev_unpack_row_length);
+  glGetIntegerv (GL_UNPACK_ALIGNMENT, &prev_unpack_alignment);
+  glGetIntegerv (GL_UNPACK_SKIP_ROWS, &prev_unpack_skip_rows);
+  glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &prev_unpack_skip_pixles);
+  glGetIntegerv (GL_TEXTURE_BINDING_RECTANGLE_ARB, &prev_rectangle_binding);
+
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, 256);
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 8);
+  glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+  glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+
+  glGenTextures (1, &gl_tex);
+  glBindTexture (GL_TEXTURE_RECTANGLE_ARB, gl_tex);
+  glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0,
+                GL_RGBA, 256, 256, 0,
+                GL_RGBA,
+                GL_UNSIGNED_BYTE,
+                data);
+
+  /* Now restore the original GL state as Cogl had left it */
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, prev_unpack_row_length);
+  glPixelStorei (GL_UNPACK_ALIGNMENT, prev_unpack_alignment);
+  glPixelStorei (GL_UNPACK_SKIP_ROWS, prev_unpack_skip_rows);
+  glPixelStorei (GL_UNPACK_SKIP_PIXELS, prev_unpack_skip_pixles);
+  glBindTexture (GL_TEXTURE_RECTANGLE_ARB, prev_rectangle_binding);
+
+  g_assert (glGetError () == GL_NO_ERROR);
+
+  g_free (data);
+
+  tex = cogl_texture_new_from_foreign (gl_tex,
+                                       GL_TEXTURE_RECTANGLE_ARB,
+                                       256, 256, 0, 0,
+                                       COGL_PIXEL_FORMAT_RGBA_8888);
+
+  return tex;
+
+#else /* GL_TEXTURE_RECTANGLE_ARB */
+
+  return COGL_INVALID_HANDLE;
+
+#endif /* GL_TEXTURE_RECTANGLE_ARB */
+}
+
+static CoglHandle
+create_source_2d (void)
+{
+  int x, y;
+  guint8 *data = g_malloc (256 * 256 * 4), *p = data;
+  CoglHandle tex;
+
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+        *(p++) = 0;
+        *(p++) = x;
+        *(p++) = y;
+        *(p++) = 255;
+      }
+
+  tex = cogl_texture_new_from_data (256, 256, COGL_TEXTURE_NONE,
+                                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    256 * 4,
+                                    data);
+
+  g_free (data);
+
+  return tex;
+}
+
+static void
+draw_frame (TestState *state)
+{
+  GLuint gl_tex;
+  CoglHandle tex_rect = create_source_rect ();
+  CoglHandle material_rect = cogl_material_new ();
+  CoglHandle tex_2d = create_source_2d ();
+  CoglHandle material_2d = cogl_material_new ();
+
+  g_assert (tex_rect != COGL_INVALID_HANDLE);
+
+  cogl_material_set_layer (material_rect, 0, tex_rect);
+  cogl_material_set_layer_filters (material_rect, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+
+  cogl_material_set_layer (material_2d, 0, tex_2d);
+  cogl_material_set_layer_filters (material_2d, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+
+  cogl_set_source (material_rect);
+
+  /* Render the texture repeated horizontally twice */
+  cogl_rectangle_with_texture_coords (0.0f, 0.0f, 512.0f, 256.0f,
+                                      0.0f, 0.0f, 2.0f, 1.0f);
+  /* Render the top half of the texture to test without repeating */
+  cogl_rectangle_with_texture_coords (0.0f, 256.0f, 256.0f, 384.0f,
+                                      0.0f, 0.0f, 1.0f, 0.5f);
+
+  cogl_set_source (material_2d);
+
+  /* Render the top half of a regular 2D texture */
+  cogl_rectangle_with_texture_coords (256.0f, 256.0f, 512.0f, 384.0f,
+                                      0.0f, 0.0f, 1.0f, 0.5f);
+
+  /* Flush the rendering now so we can safely delete the texture */
+  cogl_flush ();
+
+  cogl_handle_unref (material_rect);
+
+  /* Cogl doesn't destroy foreign textures so we have to do it manually */
+  cogl_texture_get_gl_texture (tex_rect, &gl_tex, NULL);
+  glDeleteTextures (1, &gl_tex);
+  cogl_handle_unref (tex_rect);
+}
+
+static void
+validate_result (TestState *state)
+{
+  guint8 *data, *p;
+  int x, y;
+
+  p = data = g_malloc (512 * 384 * 4);
+
+  cogl_read_pixels (0, 0, 512, 384,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888,
+                    data);
+
+  for (y = 0; y < 384; y++)
+    for (x = 0; x < 512; x++)
+      {
+        if (x >= 256 && y >= 256)
+          {
+            g_assert_cmpint (p[0], ==, 0);
+            g_assert_cmpint (p[1], ==, x & 0xff);
+            g_assert_cmpint (p[2], ==, y & 0xff);
+          }
+        else
+          {
+            g_assert_cmpint (p[0], ==, x & 0xff);
+            g_assert_cmpint (p[1], ==, y & 0xff);
+            g_assert_cmpint (p[2], ==, 0);
+          }
+        p += 4;
+      }
+
+  g_free (data);
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  draw_frame (state);
+
+  validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+static gboolean
+check_rectangle_extension (void)
+{
+  static const char rect_extension[] = "GL_ARB_texture_rectangle";
+  const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
+  const char *extensions_end;
+
+  extensions_end = extensions + strlen (extensions);
+
+  while (extensions < extensions_end)
+    {
+      const char *end = strchr (extensions, ' ');
+
+      if (end == NULL)
+        end = extensions_end;
+
+      if (end - extensions == sizeof (rect_extension) - 1 &&
+          !memcmp (extensions, rect_extension, sizeof (rect_extension) - 1))
+        return TRUE;
+
+      extensions = end + 1;
+    }
+
+  return FALSE;
+}
+
+void
+test_cogl_texture_rectangle (TestUtilsGTestFixture *fixture,
+                             void *data)
+{
+  TestState state;
+  unsigned int idle_source;
+  unsigned int paint_handler;
+
+  state.stage = clutter_stage_get_default ();
+
+  /* Check whether GL supports the rectangle extension. If not we'll
+     just assume the test passes */
+  if (check_rectangle_extension ())
+    {
+      clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+      /* We force continuous redrawing of the stage, since we need to skip
+       * the first few frames, and we wont be doing anything else that
+       * will trigger redrawing. */
+      idle_source = g_idle_add (queue_redraw, state.stage);
+
+      paint_handler = g_signal_connect_after (state.stage, "paint",
+                                              G_CALLBACK (on_paint), &state);
+
+      clutter_actor_show_all (state.stage);
+
+      clutter_main ();
+
+      g_source_remove (idle_source);
+      g_signal_handler_disconnect (state.stage, paint_handler);
+
+      if (g_test_verbose ())
+        g_print ("OK\n");
+    }
+  else if (g_test_verbose ())
+    g_print ("Skipping\n");
+}
+
diff --git a/tests/conform/test-utils.c b/tests/conform/test-utils.c
new file mode 100644
index 0000000..651e4ef
--- /dev/null
+++ b/tests/conform/test-utils.c
@@ -0,0 +1,80 @@
+#include <cogl/cogl.h>
+#include <stdlib.h>
+
+#include "test-utils.h"
+
+#define FB_WIDTH 640
+#define FB_HEIGHT 480
+
+void
+test_utils_init (TestUtilsGTestFixture *fixture,
+                 const void *data)
+{
+  TestUtilsSharedState *state = (TestUtilsSharedState *)data;
+  static int counter = 0;
+  GError *error = NULL;
+  CoglOnscreen *onscreen = NULL;
+
+  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++;
+
+  setenv ("COGL_X11_SYNC", "1", 0);
+
+  state->ctx = cogl_context_new (NULL, &error);
+  if (!state->ctx)
+    g_critical ("Failed to create a CoglContext: %s", error->message);
+
+  if (getenv  ("COGL_TEST_ONSCREEN"))
+    {
+      onscreen = cogl_onscreen_new (state->ctx, 640, 480);
+      state->fb = COGL_FRAMEBUFFER (onscreen);
+    }
+  else
+    {
+      CoglHandle offscreen;
+      CoglHandle tex = cogl_texture_2d_new_with_size (state->ctx,
+                                                      FB_WIDTH, FB_HEIGHT,
+                                                      COGL_PIXEL_FORMAT_ANY,
+                                                      &error);
+      if (!tex)
+        g_critical ("Failed to allocate texture: %s", error->message);
+
+      offscreen = cogl_offscreen_new_to_texture (tex);
+      state->fb = COGL_FRAMEBUFFER (offscreen);
+    }
+
+  if (!cogl_framebuffer_allocate (state->fb, &error))
+    g_critical ("Failed to allocate framebuffer: %s", error->message);
+
+  if (onscreen)
+    cogl_onscreen_show (onscreen);
+
+  cogl_framebuffer_clear4f (state->fb,
+                            COGL_BUFFER_BIT_COLOR |
+                            COGL_BUFFER_BIT_DEPTH |
+                            COGL_BUFFER_BIT_STENCIL,
+                            0, 0, 0, 1);
+
+  cogl_push_framebuffer (state->fb);
+}
+
+void
+test_utils_fini (TestUtilsGTestFixture *fixture,
+                 const void *data)
+{
+  const TestUtilsSharedState *state = (TestUtilsSharedState *)data;
+
+  cogl_pop_framebuffer ();
+
+  if (state->fb)
+    cogl_object_unref (state->fb);
+
+  if (state->ctx)
+    cogl_object_unref (state->ctx);
+}
diff --git a/tests/conform/test-utils.h b/tests/conform/test-utils.h
new file mode 100644
index 0000000..9325051
--- /dev/null
+++ b/tests/conform/test-utils.h
@@ -0,0 +1,41 @@
+#ifndef _TEST_UTILS_H_
+#define _TEST_UTILS_H_
+
+/* This fixture structure is allocated by glib, and before running
+ * each test we get a callback to initialize it.
+ *
+ * Actually we don't use this currently, we instead manage our own
+ * TestUtilsSharedState structure which also gets passed as a private
+ * data argument to the same initialization callback. The advantage of
+ * allocating our own shared state structure is that we can put data
+ * in it before we start running anything.
+ */
+typedef struct _TestUtilsGTestFixture
+{
+  /**/
+  int dummy;
+} TestUtilsGTestFixture;
+
+/* Stuff you put in here is setup once in main() and gets passed around to
+ * all test functions and fixture setup/teardown functions in the data
+ * argument */
+typedef struct _TestUtilsSharedState
+{
+  int    *argc_addr;
+  char ***argv_addr;
+
+  void (* todo_func) (TestUtilsGTestFixture *, void *data);
+
+  CoglContext *ctx;
+  CoglFramebuffer *fb;
+} TestUtilsSharedState;
+
+void
+test_utils_init (TestUtilsGTestFixture *fixture,
+                 const void *data);
+
+void
+test_utils_fini (TestUtilsGTestFixture *fixture,
+                 const void *data);
+
+#endif /* _TEST_UTILS_H_ */
diff --git a/tests/conform/test-vertex-buffer-contiguous.c b/tests/conform/test-vertex-buffer-contiguous.c
new file mode 100644
index 0000000..d189059
--- /dev/null
+++ b/tests/conform/test-vertex-buffer-contiguous.c
@@ -0,0 +1,257 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that the simplest usage of the vertex buffer API,
+ * where we add contiguous (x,y) GLfloat vertices, and RGBA GLubyte color
+ * attributes to a buffer, submit, and draw.
+ *
+ * It also tries to verify that the enable/disable attribute APIs are working
+ * too.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+  CoglHandle buffer;
+  CoglHandle texture;
+  CoglHandle material;
+  ClutterGeometry stage_geom;
+} TestState;
+
+static void
+validate_result (TestState *state)
+{
+  GLubyte pixel[4];
+  GLint y_off = 90;
+
+  if (g_test_verbose ())
+    g_print ("y_off = %d\n", y_off);
+
+  /* NB: We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+  /* Should see a blue pixel */
+  cogl_read_pixels (10, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+  /* Should see a red pixel */
+  cogl_read_pixels (110, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0);
+
+  /* Should see a blue pixel */
+  cogl_read_pixels (210, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 2 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+  /* Should see a green pixel, at bottom of 4th triangle */
+  cogl_read_pixels (310, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 3 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[GREEN] > pixel[RED] && pixel[GREEN] > pixel[BLUE]);
+
+  /* Should see a red pixel, at top of 4th triangle */
+  cogl_read_pixels (310, y_off - 70, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 4 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] > pixel[GREEN] && pixel[RED] > pixel[BLUE]);
+
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  /* Draw a faded blue triangle */
+  cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue");
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  /* Draw a red triangle */
+  /* Here we are testing that the disable attribute works; if it doesn't
+   * the triangle will remain faded blue */
+  cogl_translate (100, 0, 0);
+  cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue");
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  /* Draw a faded blue triangle */
+  /* Here we are testing that the re-enable works; if it doesn't
+   * the triangle will remain red */
+  cogl_translate (100, 0, 0);
+  cogl_vertex_buffer_enable (state->buffer, "gl_Color::blue");
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  /* Draw a textured triangle */
+  cogl_translate (100, 0, 0);
+  cogl_vertex_buffer_disable (state->buffer, "gl_Color::blue");
+  cogl_set_source (state->material);
+  cogl_material_set_color4ub (state->material, 0xff, 0xff, 0xff, 0xff);
+  cogl_vertex_buffer_draw (state->buffer,
+                           GL_TRIANGLE_STRIP, /* mode */
+                           0, /* first */
+                           3); /* count */
+
+  validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+
+
+void
+test_cogl_vertex_buffer_contiguous (TestUtilsGTestFixture *fixture,
+		                    void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+  ClutterActor *group;
+  unsigned int idle_source;
+  guchar tex_data[] = {
+    0xff, 0x00, 0x00, 0xff,
+    0xff, 0x00, 0x00, 0xff,
+    0x00, 0xff, 0x00, 0xff,
+    0x00, 0xff, 0x00, 0xff
+  };
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_actor_set_size (group,
+			  state.stage_geom.width,
+			  state.stage_geom.height);
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  state.texture = cogl_texture_new_from_data (2, 2,
+                                              COGL_TEXTURE_NO_SLICING,
+                                              COGL_PIXEL_FORMAT_RGBA_8888,
+                                              COGL_PIXEL_FORMAT_ANY,
+                                              0, /* auto calc row stride */
+                                              tex_data);
+
+  state.material = cogl_material_new ();
+  cogl_material_set_color4ub (state.material, 0x00, 0xff, 0x00, 0xff);
+  cogl_material_set_layer (state.material, 0, state.texture);
+
+  {
+    GLfloat triangle_verts[3][2] =
+      {
+	{0.0,	0.0},
+	{100.0, 100.0},
+	{0.0,	100.0}
+      };
+    GLbyte triangle_colors[3][4] =
+      {
+	{0x00, 0x00, 0xff, 0xff}, /* blue */
+	{0x00, 0x00, 0xff, 0x00}, /* transparent blue */
+	{0x00, 0x00, 0xff, 0x00}  /* transparent blue */
+      };
+    GLfloat triangle_tex_coords[3][2] =
+      {
+        {0.0, 0.0},
+        {1.0, 1.0},
+        {0.0, 1.0}
+      };
+    state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_Vertex",
+			    2, /* n components */
+			    GL_FLOAT,
+			    FALSE, /* normalized */
+			    0, /* stride */
+			    triangle_verts);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_Color::blue",
+			    4, /* n components */
+			    GL_UNSIGNED_BYTE,
+			    FALSE, /* normalized */
+			    0, /* stride */
+			    triangle_colors);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_MultiTexCoord0",
+			    2, /* n components */
+			    GL_FLOAT,
+			    FALSE, /* normalized */
+			    0, /* stride */
+			    triangle_tex_coords);
+
+    cogl_vertex_buffer_submit (state.buffer);
+  }
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  cogl_handle_unref (state.buffer);
+  cogl_handle_unref (state.material);
+  cogl_handle_unref (state.texture);
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-vertex-buffer-interleved.c b/tests/conform/test-vertex-buffer-interleved.c
new file mode 100644
index 0000000..e548506
--- /dev/null
+++ b/tests/conform/test-vertex-buffer-interleved.c
@@ -0,0 +1,162 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that interleved attributes work with the vertex buffer
+ * API. We add (x,y) GLfloat vertices, interleved with RGBA GLubyte color
+ * attributes to a buffer, submit and draw.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+  CoglHandle buffer;
+  ClutterGeometry stage_geom;
+} TestState;
+
+typedef struct _InterlevedVertex
+{
+  GLfloat x;
+  GLfloat y;
+
+  GLubyte r;
+  GLubyte g;
+  GLubyte b;
+  GLubyte a;
+} InterlevedVertex;
+
+
+static void
+validate_result (TestState *state)
+{
+  GLubyte pixel[4];
+  GLint y_off = 90;
+
+  /* NB: We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+  /* Should see a blue pixel */
+  cogl_read_pixels (10, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] == 0 && pixel[GREEN] == 0 && pixel[BLUE] != 0);
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  /* Draw a faded blue triangle */
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_vertex_buffer_interleved (TestUtilsGTestFixture *fixture,
+		                    void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_actor_set_size (group,
+			  state.stage_geom.width,
+			  state.stage_geom.height);
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  {
+    InterlevedVertex verts[3] =
+      {
+	{ /* .x = */ 0.0, /* .y = */ 0.0,
+	  /* blue */
+	  /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0xff },
+
+	{ /* .x = */ 100.0, /* .y = */ 100.0,
+	  /* transparent blue */
+	  /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 },
+
+	{ /* .x = */ 0.0, /* .y = */ 100.0,
+	  /* transparent blue */
+	  /* .r = */ 0x00, /* .g = */ 0x00, /* .b = */ 0xff, /* .a = */ 0x00 },
+      };
+
+    /* We assume the compiler is doing no funny struct padding for this test:
+     */
+    g_assert (sizeof (InterlevedVertex) == 12);
+
+    state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_Vertex",
+			    2, /* n components */
+			    GL_FLOAT,
+			    FALSE, /* normalized */
+                            12, /* stride */
+			    &verts[0].x);
+    cogl_vertex_buffer_add (state.buffer,
+                            "gl_Color",
+			    4, /* n components */
+			    GL_UNSIGNED_BYTE,
+			    FALSE, /* normalized */
+			    12, /* stride */
+			    &verts[0].r);
+    cogl_vertex_buffer_submit (state.buffer);
+  }
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  cogl_handle_unref (state.buffer);
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-vertex-buffer-mutability.c b/tests/conform/test-vertex-buffer-mutability.c
new file mode 100644
index 0000000..c55ee56
--- /dev/null
+++ b/tests/conform/test-vertex-buffer-mutability.c
@@ -0,0 +1,198 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+/* This test verifies that modifying a vertex buffer works, by updating
+ * vertex positions, and deleting and re-adding different color attributes.
+ *
+ * If you want visual feedback of what this test paints for debugging purposes,
+ * then remove the call to clutter_main_quit() in validate_result.
+ */
+
+typedef struct _TestState
+{
+  CoglHandle buffer;
+  ClutterGeometry stage_geom;
+} TestState;
+
+static void
+validate_result (TestState *state)
+{
+  GLubyte pixel[4];
+  GLint y_off = 90;
+
+  /* NB: We ignore the alpha, since we don't know if our render target is
+   * RGB or RGBA */
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+
+  /* Should see a red pixel */
+  cogl_read_pixels (110, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 0 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] != 0 && pixel[GREEN] == 0 && pixel[BLUE] == 0);
+
+  /* Should see a green pixel */
+  cogl_read_pixels (210, y_off, 1, 1,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    pixel);
+  if (g_test_verbose ())
+    g_print ("pixel 1 = %x, %x, %x\n", pixel[RED], pixel[GREEN], pixel[BLUE]);
+  g_assert (pixel[RED] == 0 && pixel[GREEN] != 0 && pixel[BLUE] == 0);
+
+#undef RED
+#undef GREEN
+#undef BLUE
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  GLfloat triangle_verts[3][2] =
+    {
+      {100.0, 0.0},
+      {200.0, 100.0},
+      {100.0, 100.0}
+    };
+  GLbyte triangle_colors[3][4] =
+    {
+      {0x00, 0xff, 0x00, 0xff}, /* blue */
+      {0x00, 0xff, 0x00, 0x00}, /* transparent blue */
+      {0x00, 0xff, 0x00, 0x00}  /* transparent blue */
+    };
+
+  /*
+   * Draw a red triangle
+   */
+
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+
+  cogl_vertex_buffer_add (state->buffer,
+			  "gl_Vertex",
+			  2, /* n components */
+			  GL_FLOAT,
+			  FALSE, /* normalized */
+			  0, /* stride */
+			  triangle_verts);
+  cogl_vertex_buffer_delete (state->buffer, "gl_Color");
+  cogl_vertex_buffer_submit (state->buffer);
+
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  /*
+   * Draw a faded green triangle
+   */
+
+  cogl_vertex_buffer_add (state->buffer,
+			  "gl_Color",
+			  4, /* n components */
+			  GL_UNSIGNED_BYTE,
+			  FALSE, /* normalized */
+			  0, /* stride */
+			  triangle_colors);
+  cogl_vertex_buffer_submit (state->buffer);
+
+  cogl_translate (100, 0, 0);
+  cogl_vertex_buffer_draw (state->buffer,
+			   GL_TRIANGLE_STRIP, /* mode */
+			   0, /* first */
+			   3); /* count */
+
+  validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_vertex_buffer_mutability (TestUtilsGTestFixture *fixture,
+		                    void *data)
+{
+  TestState state;
+  ClutterActor *stage;
+  ClutterColor stage_clr = {0x0, 0x0, 0x0, 0xff};
+  ClutterActor *group;
+  unsigned int idle_source;
+
+  stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
+  clutter_actor_get_geometry (stage, &state.stage_geom);
+
+  group = clutter_group_new ();
+  clutter_actor_set_size (group,
+			  state.stage_geom.width,
+			  state.stage_geom.height);
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
+
+  /* We force continuous redrawing incase someone comments out the
+   * clutter_main_quit and wants visual feedback for the test since we
+   * wont be doing anything else that will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+
+  g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
+
+  {
+    GLfloat triangle_verts[3][2] =
+      {
+	{0.0,	0.0},
+	{100.0, 100.0},
+	{0.0,	100.0}
+      };
+    GLbyte triangle_colors[3][4] =
+      {
+	{0x00, 0x00, 0xff, 0xff}, /* blue */
+	{0x00, 0x00, 0xff, 0x00}, /* transparent blue */
+	{0x00, 0x00, 0xff, 0x00}  /* transparent blue */
+      };
+    state.buffer = cogl_vertex_buffer_new (3 /* n vertices */);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_Vertex",
+			    2, /* n components */
+			    GL_FLOAT,
+			    FALSE, /* normalized */
+			    0, /* stride */
+			    triangle_verts);
+    cogl_vertex_buffer_add (state.buffer,
+			    "gl_Color",
+			    4, /* n components */
+			    GL_UNSIGNED_BYTE,
+			    FALSE, /* normalized */
+			    0, /* stride */
+			    triangle_colors);
+    cogl_vertex_buffer_submit (state.buffer);
+  }
+
+  clutter_actor_show_all (stage);
+
+  clutter_main ();
+
+  cogl_handle_unref (state.buffer);
+
+  g_source_remove (idle_source);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-viewport.c b/tests/conform/test-viewport.c
new file mode 100644
index 0000000..b247a59
--- /dev/null
+++ b/tests/conform/test-viewport.c
@@ -0,0 +1,416 @@
+
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+
+#include "test-conform-common.h"
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+
+#define FRAMEBUFFER_WIDTH  640
+#define FRAMEBUFFER_HEIGHT 480
+
+static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
+
+static void
+assert_region_color (int x,
+                     int y,
+                     int width,
+                     int height,
+                     guint8 red,
+                     guint8 green,
+                     guint8 blue,
+                     guint8 alpha)
+{
+  guint8 *data = g_malloc0 (width * height * 4);
+  cogl_read_pixels (x, y, width, height,
+                    COGL_READ_PIXELS_COLOR_BUFFER,
+                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                    data);
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+        guint8 *pixel = &data[y*width*4 + x*4];
+#if 1
+        g_assert (pixel[RED] == red &&
+                  pixel[GREEN] == green &&
+                  pixel[BLUE] == blue &&
+                  pixel[ALPHA] == alpha);
+#endif
+      }
+  g_free (data);
+}
+
+static void
+assert_rectangle_color_and_black_border (int x,
+                                         int y,
+                                         int width,
+                                         int height,
+                                         guint8 red,
+                                         guint8 green,
+                                         guint8 blue)
+{
+  /* check the rectangle itself... */
+  assert_region_color (x, y, width, height, red, green, blue, 0xff);
+  /* black to left of the rectangle */
+  assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff);
+  /* black to right of the rectangle */
+  assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff);
+  /* black above the rectangle */
+  assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff);
+  /* and black below the rectangle */
+  assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff);
+}
+
+
+static void
+on_paint (ClutterActor *actor, void *state)
+{
+  float saved_viewport[4];
+  CoglMatrix saved_projection;
+  CoglMatrix projection;
+  CoglMatrix modelview;
+  guchar *data;
+  CoglHandle tex;
+  CoglHandle offscreen;
+  CoglColor black;
+  float x0;
+  float y0;
+  float width;
+  float height;
+
+  /* for clearing the offscreen framebuffer to black... */
+  cogl_color_init_from_4ub (&black, 0x00, 0x00, 0x00, 0xff);
+
+  cogl_get_viewport (saved_viewport);
+  cogl_get_projection_matrix (&saved_projection);
+  cogl_push_matrix ();
+
+  cogl_matrix_init_identity (&projection);
+  cogl_matrix_init_identity (&modelview);
+
+  cogl_set_projection_matrix (&projection);
+  cogl_set_modelview_matrix (&modelview);
+
+  /* - Create a 100x200 viewport (i.e. smaller than the onscreen framebuffer)
+   *   and position it a (20, 10) inside the framebuffer.
+   * - Fill the whole viewport with a purple rectangle
+   * - Verify that the framebuffer is black with a 100x200 purple rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     100, /* width */
+                     200); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* fill the viewport with purple.. */
+  cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0xff, 0x00, 0xff);
+
+
+  /* - Create a viewport twice the size of the onscreen framebuffer with
+   *   a negative offset positioning it at (-20, -10) relative to the
+   *   buffer itself.
+   * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which
+   *   is (20, 10) within the framebuffer)
+   * - Verify that the framebuffer is black with a 100x200 green rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (-20, /* x */
+                     -10, /* y */
+                     FRAMEBUFFER_WIDTH * 2, /* width */
+                     FRAMEBUFFER_HEIGHT * 2); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* draw a 100x200 green rectangle offset into the viewport such that its
+   * top left corner should be found at (20, 10) in the offscreen buffer */
+  /* (offset 40 pixels right from the left of the viewport) */
+  x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f;
+  /* (offset 20 pixels down from the top of the viewport) */
+  y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f;
+  width = (1.0f / FRAMEBUFFER_WIDTH) * 100;
+  height = (1.0f / FRAMEBUFFER_HEIGHT) * 200;
+  cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+  cogl_rectangle (x0, y0, x0 + width, y0 - height);
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0x00, 0xff, 0x00);
+
+
+  /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+   *   buffer.
+   * - Push a 100x200 window space clip rectangle at (20, 10)
+   * - Fill the whole viewport with a blue rectangle
+   * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     200, /* width */
+                     400); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  cogl_clip_push_window_rectangle (20, 10, 100, 200);
+  /* fill the viewport with blue.. */
+  cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  cogl_clip_pop ();
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0x00, 0x00, 0xff);
+
+
+  /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+   *   buffer.
+   * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport
+   *   (i.e. (40, 20) inside the framebuffer)
+   * - Fill the whole viewport with a green rectangle
+   * - Verify that the framebuffer is black with a 100x200 green rectangle at
+   *   (40, 20)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     200, /* width */
+                     400); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* figure out where to position our clip rectangle in model space
+   * coordinates... */
+  /* (offset 40 pixels right from the left of the viewport) */
+  x0 = -1.0f + (2.0f / 200) * 20.f;
+  /* (offset 20 pixels down from the top of the viewport) */
+  y0 = 1.0f - (2.0f / 400) * 10.0f;
+  width = (2.0f / 200) * 100;
+  height = (2.0f / 400) * 200;
+  /* add the clip rectangle... */
+  cogl_push_matrix ();
+  cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0);
+  /* XXX: Rotate just enough to stop Cogl from converting our model space
+   * rectangle into a window space rectangle.. */
+  cogl_rotate (0.1, 0, 0, 1);
+  cogl_clip_push_rectangle (-(width/2.0), -(height/2.0),
+                            width/2.0, height/2.0);
+  cogl_pop_matrix ();
+  /* fill the viewport with green.. */
+  cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  cogl_clip_pop ();
+  assert_rectangle_color_and_black_border (40, 20, 100, 200,
+                                           0x00, 0xff, 0x00);
+
+
+  /* Set the viewport to something specific so we can verify that it gets
+   * restored after we are done testing with an offscreen framebuffer... */
+  cogl_set_viewport (20, 10, 100, 200);
+
+  /*
+   * Next test offscreen drawing...
+   */
+  data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT);
+  tex = cogl_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT,
+                                    COGL_TEXTURE_NO_SLICING,
+                                    COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */
+                                    COGL_PIXEL_FORMAT_ANY, /* internal fmt */
+                                    FRAMEBUFFER_WIDTH * 4, /* rowstride */
+                                    data);
+  g_free (data);
+  offscreen = cogl_offscreen_new_to_texture (tex);
+
+  cogl_push_framebuffer (offscreen);
+
+
+  /* - Create a 100x200 viewport (i.e. smaller than the offscreen framebuffer)
+   *   and position it a (20, 10) inside the framebuffer.
+   * - Fill the whole viewport with a blue rectangle
+   * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     100, /* width */
+                     200); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* fill the viewport with blue.. */
+  cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0x00, 0x00, 0xff);
+
+
+  /* - Create a viewport twice the size of the offscreen framebuffer with
+   *   a negative offset positioning it at (-20, -10) relative to the
+   *   buffer itself.
+   * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which
+   *   is (20, 10) within the framebuffer)
+   * - Verify that the framebuffer is black with a 100x200 red rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (-20, /* x */
+                     -10, /* y */
+                     FRAMEBUFFER_WIDTH * 2, /* width */
+                     FRAMEBUFFER_HEIGHT * 2); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* draw a 100x200 red rectangle offset into the viewport such that its
+   * top left corner should be found at (20, 10) in the offscreen buffer */
+  /* (offset 40 pixels right from the left of the viewport) */
+  x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f;
+  /* (offset 20 pixels down from the top of the viewport) */
+  y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f;
+  width = (1.0f / FRAMEBUFFER_WIDTH) * 100;
+  height = (1.0f / FRAMEBUFFER_HEIGHT) * 200;
+  cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
+  cogl_rectangle (x0, y0, x0 + width, y0 - height);
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0xff, 0x00, 0x00);
+
+
+  /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+   *   buffer.
+   * - Push a 100x200 window space clip rectangle at (20, 10)
+   * - Fill the whole viewport with a blue rectangle
+   * - Verify that the framebuffer is black with a 100x200 blue rectangle at
+   *   (20, 10)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     200, /* width */
+                     400); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  cogl_clip_push_window_rectangle (20, 10, 100, 200);
+  /* fill the viewport with blue.. */
+  cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  cogl_clip_pop ();
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0x00, 0x00, 0xff);
+
+
+  /* - Create a 200x400 viewport and position it a (20, 10) inside the draw
+   *   buffer.
+   * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport
+   *   (i.e. (40, 20) inside the framebuffer)
+   * - Fill the whole viewport with a green rectangle
+   * - Verify that the framebuffer is black with a 100x200 green rectangle at
+   *   (40, 20)
+   */
+  cogl_set_viewport (20, /* x */
+                     10, /* y */
+                     200, /* width */
+                     400); /* height */
+  /* clear everything... */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  /* figure out where to position our clip rectangle in model space
+   * coordinates... */
+  /* (offset 40 pixels right from the left of the viewport) */
+  x0 = -1.0f + (2.0f / 200) * 20.f;
+  /* (offset 20 pixels down from the top of the viewport) */
+  y0 = 1.0f - (2.0f / 400) * 10.0f;
+  width = (2.0f / 200) * 100;
+  height = (2.0f / 400) * 200;
+  /* add the clip rectangle... */
+  cogl_push_matrix ();
+  cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0);
+  /* XXX: Rotate just enough to stop Cogl from converting our model space
+   * rectangle into a window space rectangle.. */
+  cogl_rotate (0.1, 0, 0, 1);
+  cogl_clip_push_rectangle (-(width/2.0), -(height/2.0),
+                            width/2, height/2);
+  cogl_pop_matrix ();
+  /* fill the viewport with green.. */
+  cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  cogl_clip_pop ();
+  assert_rectangle_color_and_black_border (40, 20, 100, 200,
+                                           0x00, 0xff, 0x00);
+
+
+  /* Set the viewport to something obscure to verify that it gets
+   * replace when we switch back to the onscreen framebuffer... */
+  cogl_set_viewport (0, 0, 10, 10);
+
+  cogl_pop_framebuffer ();
+  cogl_handle_unref (offscreen);
+
+  /*
+   * Verify that the previous onscreen framebuffer's viewport was restored
+   * by drawing a white rectangle across the whole viewport. This should
+   * draw a 100x200 rectangle at (20,10) relative to the onscreen draw
+   * buffer...
+   */
+  cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
+  cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
+  cogl_rectangle (-1, 1, 1, -1);
+  assert_rectangle_color_and_black_border (20, 10, 100, 200,
+                                           0xff, 0xff, 0xff);
+
+
+  /* Uncomment to display the last contents of the offscreen framebuffer */
+#if 1
+  cogl_matrix_init_identity (&projection);
+  cogl_matrix_init_identity (&modelview);
+  cogl_set_viewport (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
+  cogl_set_projection_matrix (&projection);
+  cogl_set_modelview_matrix (&modelview);
+  cogl_set_source_texture (tex);
+  cogl_rectangle (-1, 1, 1, -1);
+#endif
+
+  cogl_handle_unref (tex);
+
+  /* Finally restore the stage's original state... */
+  cogl_pop_matrix ();
+  cogl_set_projection_matrix (&saved_projection);
+  cogl_set_viewport (saved_viewport[0], saved_viewport[1],
+                     saved_viewport[2], saved_viewport[3]);
+
+
+  /* Comment this out if you want visual feedback of what this test
+   * paints.
+   */
+  clutter_main_quit ();
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_viewport (TestUtilsGTestFixture *fixture,
+                    void *data)
+{
+  unsigned int idle_source;
+  ClutterActor *stage;
+
+  stage = clutter_stage_get_default ();
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, stage);
+  g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL);
+
+  clutter_actor_show (stage);
+  clutter_main ();
+
+  g_source_remove (idle_source);
+
+  /* Remove all of the actors from the stage */
+  clutter_container_foreach (CLUTTER_CONTAINER (stage),
+                             (ClutterCallback) clutter_actor_destroy,
+                             NULL);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
+
diff --git a/tests/conform/test-wrap-modes.c b/tests/conform/test-wrap-modes.c
new file mode 100644
index 0000000..9404596
--- /dev/null
+++ b/tests/conform/test-wrap-modes.c
@@ -0,0 +1,317 @@
+#include <clutter/clutter.h>
+#include <string.h>
+
+#include "test-conform-common.h"
+
+#define TEX_SIZE 4
+
+static const ClutterColor stage_color = { 0x80, 0x80, 0x80, 0xff };
+
+typedef struct _TestState
+{
+  ClutterActor *stage;
+  CoglHandle texture;
+} TestState;
+
+static CoglHandle
+create_texture (CoglTextureFlags flags)
+{
+  guint8 *data = g_malloc (TEX_SIZE * TEX_SIZE * 4), *p = data;
+  CoglHandle tex;
+  int x, y;
+
+  for (y = 0; y < TEX_SIZE; y++)
+    for (x = 0; x < TEX_SIZE; x++)
+      {
+        *(p++) = 0;
+        *(p++) = (x & 1) * 255;
+        *(p++) = (y & 1) * 255;
+        *(p++) = 255;
+      }
+
+  tex = cogl_texture_new_from_data (TEX_SIZE, TEX_SIZE, flags,
+                                    COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+                                    COGL_PIXEL_FORMAT_ANY,
+                                    TEX_SIZE * 4,
+                                    data);
+
+  g_free (data);
+
+  return tex;
+}
+
+static CoglHandle
+create_material (TestState *state,
+                 CoglMaterialWrapMode wrap_mode_s,
+                 CoglMaterialWrapMode wrap_mode_t)
+{
+  CoglHandle material;
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, state->texture);
+  cogl_material_set_layer_filters (material, 0,
+                                   COGL_MATERIAL_FILTER_NEAREST,
+                                   COGL_MATERIAL_FILTER_NEAREST);
+  cogl_material_set_layer_wrap_mode_s (material, 0, wrap_mode_s);
+  cogl_material_set_layer_wrap_mode_t (material, 0, wrap_mode_t);
+
+  return material;
+}
+
+static CoglMaterialWrapMode
+test_wrap_modes[] =
+  {
+    COGL_MATERIAL_WRAP_MODE_REPEAT,
+    COGL_MATERIAL_WRAP_MODE_REPEAT,
+
+    COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+    COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+
+    COGL_MATERIAL_WRAP_MODE_REPEAT,
+    COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+
+    COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE,
+    COGL_MATERIAL_WRAP_MODE_REPEAT,
+
+    COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+    COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+
+    COGL_MATERIAL_WRAP_MODE_AUTOMATIC,
+    COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE
+  };
+
+static void
+draw_tests (TestState *state)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+    {
+      CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+      CoglHandle material;
+
+      /* Create a separate material for each pair of wrap modes so
+         that we can verify whether the batch splitting works */
+      wrap_mode_s = test_wrap_modes[i];
+      wrap_mode_t = test_wrap_modes[i + 1];
+      material = create_material (state, wrap_mode_s, wrap_mode_t);
+      cogl_set_source (material);
+      cogl_handle_unref (material);
+      /* Render the material at four times the size of the texture */
+      cogl_rectangle_with_texture_coords (i * TEX_SIZE, 0,
+                                          (i + 2) * TEX_SIZE, TEX_SIZE * 2,
+                                          0, 0, 2, 2);
+    }
+}
+
+static const CoglTextureVertex vertices[4] =
+  {
+    { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+    { 0.0f, TEX_SIZE * 2, 0.0f, 0.0f, 2.0f },
+    { TEX_SIZE * 2, TEX_SIZE * 2, 0.0f, 2.0f, 2.0f },
+    { TEX_SIZE * 2, 0.0f, 0.0f, 2.0f, 0.0f }
+  };
+
+static void
+draw_tests_polygon (TestState *state)
+{
+  int i;
+
+  for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+    {
+      CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+      CoglHandle material;
+
+      wrap_mode_s = test_wrap_modes[i];
+      wrap_mode_t = test_wrap_modes[i + 1];
+      material = create_material (state, wrap_mode_s, wrap_mode_t);
+      cogl_set_source (material);
+      cogl_handle_unref (material);
+      cogl_push_matrix ();
+      cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+      /* Render the material at four times the size of the texture */
+      cogl_polygon (vertices, G_N_ELEMENTS (vertices), FALSE);
+      cogl_pop_matrix ();
+    }
+}
+
+static void
+draw_tests_vbo (TestState *state)
+{
+  CoglHandle vbo;
+  int i;
+
+  vbo = cogl_vertex_buffer_new (4);
+  cogl_vertex_buffer_add (vbo, "gl_Vertex", 3,
+                          COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+                          sizeof (vertices[0]),
+                          &vertices[0].x);
+  cogl_vertex_buffer_add (vbo, "gl_MultiTexCoord0", 2,
+                          COGL_ATTRIBUTE_TYPE_FLOAT, FALSE,
+                          sizeof (vertices[0]),
+                          &vertices[0].tx);
+  cogl_vertex_buffer_submit (vbo);
+
+  for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+    {
+      CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+      CoglHandle material;
+
+      wrap_mode_s = test_wrap_modes[i];
+      wrap_mode_t = test_wrap_modes[i + 1];
+      material = create_material (state, wrap_mode_s, wrap_mode_t);
+      cogl_set_source (material);
+      cogl_handle_unref (material);
+      cogl_push_matrix ();
+      cogl_translate (TEX_SIZE * i, 0.0f, 0.0f);
+      /* Render the material at four times the size of the texture */
+      cogl_vertex_buffer_draw (vbo, COGL_VERTICES_MODE_TRIANGLE_FAN, 0, 4);
+      cogl_pop_matrix ();
+    }
+
+  cogl_handle_unref (vbo);
+}
+
+static void
+draw_frame (TestState *state)
+{
+  /* Draw the tests first with a non atlased texture */
+  state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+  draw_tests (state);
+  cogl_handle_unref (state->texture);
+
+  /* Draw the tests again with a possible atlased texture. This should
+     end up testing software repeats */
+  state->texture = create_texture (COGL_TEXTURE_NONE);
+  cogl_push_matrix ();
+  cogl_translate (0.0f, TEX_SIZE * 2.0f, 0.0f);
+  draw_tests (state);
+  cogl_pop_matrix ();
+  cogl_handle_unref (state->texture);
+
+  /* Draw the tests using cogl_polygon */
+  state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+  cogl_push_matrix ();
+  cogl_translate (0.0f, TEX_SIZE * 4.0f, 0.0f);
+  draw_tests_polygon (state);
+  cogl_pop_matrix ();
+  cogl_handle_unref (state->texture);
+
+  /* Draw the tests using a vertex buffer */
+  state->texture = create_texture (COGL_TEXTURE_NO_ATLAS);
+  cogl_push_matrix ();
+  cogl_translate (0.0f, TEX_SIZE * 6.0f, 0.0f);
+  draw_tests_vbo (state);
+  cogl_pop_matrix ();
+  cogl_handle_unref (state->texture);
+}
+
+static void
+validate_set (TestState *state, int offset)
+{
+  guint8 data[TEX_SIZE * 2 * TEX_SIZE * 2 * 4], *p;
+  int x, y, i;
+
+  for (i = 0; i < G_N_ELEMENTS (test_wrap_modes); i += 2)
+    {
+      CoglMaterialWrapMode wrap_mode_s, wrap_mode_t;
+
+      wrap_mode_s = test_wrap_modes[i];
+      wrap_mode_t = test_wrap_modes[i + 1];
+
+      cogl_read_pixels (i * TEX_SIZE, offset * TEX_SIZE * 2,
+                        TEX_SIZE * 2, TEX_SIZE * 2,
+                        COGL_READ_PIXELS_COLOR_BUFFER,
+                        COGL_PIXEL_FORMAT_RGBA_8888,
+                        data);
+
+      p = data;
+
+      for (y = 0; y < TEX_SIZE * 2; y++)
+        for (x = 0; x < TEX_SIZE * 2; x++)
+          {
+            guint8 green, blue;
+
+            if (x < TEX_SIZE ||
+                wrap_mode_s == COGL_MATERIAL_WRAP_MODE_REPEAT ||
+                wrap_mode_s == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
+              green = (x & 1) * 255;
+            else
+              green = ((TEX_SIZE - 1) & 1) * 255;
+
+            if (y < TEX_SIZE ||
+                wrap_mode_t == COGL_MATERIAL_WRAP_MODE_REPEAT ||
+                wrap_mode_t == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
+              blue = (y & 1) * 255;
+            else
+              blue = ((TEX_SIZE - 1) & 1) * 255;
+
+            g_assert_cmpint (p[0], ==, 0);
+            g_assert_cmpint (p[1], ==, green);
+            g_assert_cmpint (p[2], ==, blue);
+
+            p += 4;
+          }
+    }
+}
+
+static void
+validate_result (TestState *state)
+{
+  validate_set (state, 0); /* non-atlased rectangle */
+#if 0 /* this doesn't currently work */
+  validate_set (state, 1); /* atlased rectangle */
+#endif
+  validate_set (state, 2); /* cogl_polygon */
+  validate_set (state, 3); /* vertex buffer */
+
+  /* Comment this out to see what the test paints */
+  clutter_main_quit ();
+}
+
+static void
+on_paint (ClutterActor *actor, TestState *state)
+{
+  draw_frame (state);
+
+  validate_result (state);
+}
+
+static gboolean
+queue_redraw (gpointer stage)
+{
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+
+  return TRUE;
+}
+
+void
+test_cogl_wrap_modes (TestUtilsGTestFixture *fixture,
+                      void *data)
+{
+  TestState state;
+  unsigned int idle_source;
+  unsigned int paint_handler;
+
+  state.stage = clutter_stage_get_default ();
+
+  clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color);
+
+  /* We force continuous redrawing of the stage, since we need to skip
+   * the first few frames, and we wont be doing anything else that
+   * will trigger redrawing. */
+  idle_source = g_idle_add (queue_redraw, state.stage);
+
+  paint_handler = g_signal_connect_after (state.stage, "paint",
+                                          G_CALLBACK (on_paint), &state);
+
+  clutter_actor_show_all (state.stage);
+
+  clutter_main ();
+
+  g_source_remove (idle_source);
+  g_signal_handler_disconnect (state.stage, paint_handler);
+
+  if (g_test_verbose ())
+    g_print ("OK\n");
+}
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
new file mode 100644
index 0000000..3a2030a
--- /dev/null
+++ b/tests/data/Makefile.am
@@ -0,0 +1,3 @@
+NULL =
+
+EXTRA_DIST = valgrind.suppressions
diff --git a/tests/data/valgrind.suppressions b/tests/data/valgrind.suppressions
new file mode 100644
index 0000000..f47498d
--- /dev/null
+++ b/tests/data/valgrind.suppressions
@@ -0,0 +1,173 @@
+{
+   ioctl_1
+   Memcheck:Param
+   ioctl(generic)
+   fun:ioctl
+   fun:driDrawableInitVBlank
+   fun:intelMakeCurrent
+   fun:glXMakeContextCurrent
+}
+
+{
+   ioctl_2
+   Memcheck:Param
+   ioctl(generic)
+   fun:ioctl
+   fun:driDrawableGetMSC32
+   fun:clutter_backend_glx_redraw
+}
+
+{
+   ioctl_3
+   Memcheck:Param
+   ioctl(generic)
+   fun:ioctl
+   fun:driWaitForMSC32
+   fun:clutter_backend_glx_redraw
+}
+
+{
+   mesa_init_context
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:glXCreateNewContext
+}
+
+{
+   type_register
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:g_type_register_*
+}
+
+{
+   type_ref
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:g_type_class_ref
+}
+
+{
+   type_interface_prereq
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:g_type_interface_add_prerequisite
+}
+
+{
+   get_charset
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:g_get_charset
+}
+
+{
+   cogl_features
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:cogl_get_features
+}
+
+{
+   glx_query_version
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:glXQueryVersion
+}
+
+{
+   glx_create_context
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:glXCreateNewContext
+}
+
+{
+   glx_make_current
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:glXMakeContextCurrent
+}
+
+{
+   gl_draw_arrays
+   Memcheck:Leak
+   fun:*malloc
+   ...
+   fun:glDrawArrays
+}
+
+{
+   cogl_clear
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:cogl_clear
+}
+
+{
+   default_font
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:clutter_backend_get_font_name
+}
+
+{
+   id_pool
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:clutter_id_pool_new
+}
+
+{
+   x_open_display
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:XOpenDisplay
+}
+
+# ... and font descriptions from every "sans 12" type string
+{
+   pango_font_description_from_string
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:pango_font_description_from_string
+}
+
+# other lib init
+{
+   fontconfig_init
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:FcConfigParseAndLoad
+}
+
+{
+   freetype_init
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:FT_Open_Face
+}
+
+{
+   x_init_ext
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   fun:XInitExtension
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]