[cogl] Starts porting Cogl conformance tests from Clutter



commit b5a765707690b65feb670e86a227109090f78b02
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 currently
    still 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 is the basic testing
    infrastructure we need, so now we can port more and more tests
    incrementally.
    
    Since the test suite wants a way to synchronize X requests/replies and
    we can't simply call XSynchronize in the test-utils code before we know
    if we are really running on X this adds a check for an environment
    variable named "COGL_X11_SYNC" in cogl-xlib-renderer.c and if it's set
    it forces XSynchronize (dpy, TRUE) to be called.
    
    By default the conformance tests are run off screen. This makes the
    tests run much faster and they also don't interfere with other work you
    may want to do by constantly stealing focus. CoglOnscreen framebuffers
    obviously don't get tested this way so it's important that the tests
    also get run on screen every once in a while, especially if changes are
    being made to CoglFramebuffer related code.  On screen testing can be
    enabled by setting COGL_TEST_ONSCREEN=1 in your environment.

 Makefile.am                                   |    2 +-
 cogl/cogl-xlib-renderer.c                     |    5 +
 configure.ac                                  |    4 +
 tests/Makefile.am                             |   16 +
 tests/README                                  |   63 ++++
 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 ++++++++++
 42 files changed, 7430 insertions(+), 1 deletions(-)
---
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 6d5ae89..8909203 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..cc6dbb9
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,63 @@
+Outline of test categories:
+
+The conform/ tests:
+-------------------
+These tests should be non-interactive unit-tests that verify a single
+feature is behaving as documented. See conform/ADDING_NEW_TESTS for more
+details.
+
+Although it may seem a bit awkward; all the tests are built into a
+single binary because it makes building the tests *much* faster by avoiding
+lots of linking.
+
+Each test has a wrapper script generated though so running the individual tests
+should be convenient enough. Running the wrapper script will also print out for
+convenience how you could run the test under gdb or valgrind like this for
+example:
+
+  NOTE: For debugging purposes, you can run this single test as follows:
+  $ libtool --mode=execute \
+            gdb --eval-command="b test_cogl_depth_test" \
+            --args ./test-conformance -p /conform/cogl/test_cogl_depth_test
+  or:
+  $ env G_SLICE=always-malloc \
+    libtool --mode=execute \
+            valgrind ./test-conformance -p /conform/cogl/test_cogl_depth_test
+
+By default the conformance tests are run offscreen. This makes the tests run
+much faster and they also don't interfere with other work you may want to do by
+constantly stealing focus. CoglOnscreen framebuffers obviously don't get tested
+this way so it's important that the tests also get run onscreen every once in a
+while, especially if changes are being made to CoglFramebuffer related code.
+Onscreen testing can be enabled by setting COGL_TEST_ONSCREEN=1 in your
+environment.
+
+The micro-bench/ tests:
+-----------------------
+These should be focused performance tests, 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:
+--------------------
+This contains optional data (like images) that can be referenced by a test.
+
+
+Misc 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..e6ed5bb
--- /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 = 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]