OSDN Git Service

mesa: improve getprocaddress test
authorRobert Ellison <papillo@tungstengraphics.com>
Fri, 7 Aug 2009 18:06:17 +0000 (12:06 -0600)
committerRobert Ellison <papillo@tungstengraphics.com>
Fri, 7 Aug 2009 18:06:17 +0000 (12:06 -0600)
- Allow the getprocaddress test to test extensions not supported by
  Mesa.  The original getprocaddress.py script only included OpenGL
  extension functions that were in Mesa dispatch tables.  Now all
  known extension functions (as detailed in gl_API.xml) are included.
  As the test does not link against any extension function symbols
  (i.e. it uses glXGetProcAddress() for all extension functions),
  it still compiles and links against Mesa; but now the same
  binary can be used to test extensions not yet supported by Mesa.

- Extend the list of tested extension functions.  The last revision
  of this test exercised 16 extension functions; this revision adds
  support for 95 more.

progs/tests/getprocaddress.c
progs/tests/getprocaddress.py

index ca66025..7de581a 100644 (file)
@@ -39,13 +39,2600 @@ typedef void (*generic_func)();
 
 #define EQUAL(X, Y)  (fabs((X) - (Y)) < 0.001)
 
-/**
+/* This macro simplifies the task of querying an extension function
+ * pointer and checking to see whether it resolved.
+ */
+#define DECLARE_GLFUNC_PTR(name,type) \
+   type name = (type) glXGetProcAddressARB((const GLubyte *) "gl" #name)
+
+/********************************************************************
+ * Generic helper functions used by the test functions.
+ */
+
+static void CheckGLError(int line, const char *file, const char *function)
+{
+    int errorCode;
+    glFinish();
+    errorCode  = glGetError();
+    if (errorCode == GL_NO_ERROR) return;
+    while (errorCode != GL_NO_ERROR) {
+       fprintf(stderr, "OpenGL error 0x%x (%s) at line %d of file %s in function %s()\n",
+           errorCode,
+           errorCode == GL_INVALID_VALUE? "GL_INVALID_VALUE":
+           errorCode == GL_INVALID_ENUM? "GL_INVALID_ENUM":
+           errorCode == GL_INVALID_OPERATION? "GL_INVALID_OPERATION":
+           errorCode == GL_STACK_OVERFLOW? "GL_STACK_OVERFLOW":
+           errorCode == GL_STACK_UNDERFLOW? "GL_STACK_UNDERFLOW":
+           errorCode == GL_OUT_OF_MEMORY? "GL_OUT_OF_MEMORY":
+           "unknown",
+           line, file, function);
+       errorCode = glGetError();
+    }
+    fflush(stderr);
+}
+
+static GLboolean 
+compare_bytes(const char *errorLabel, GLuint expectedSize, 
+   const GLubyte *expectedData, GLuint actualSize, const GLubyte *actualData)
+{
+   int i;
+
+   if (expectedSize == actualSize &&
+      memcmp(expectedData, actualData, actualSize) == 0) {
+      /* All is well */
+      return GL_TRUE;
+   }
+
+   /* Trouble; we don't match.  Print out why. */
+   fprintf(stderr, "%s: actual data is not as expected\n", errorLabel);
+   for (i = 0; i <= 1; i++) {
+      const GLubyte *ptr;
+      int size;
+      char *label;
+      int j;
+
+      switch(i) {
+         case 0:
+            label = "expected";
+            size = expectedSize;
+            ptr = expectedData;
+            break;
+         case 1:
+            label = "  actual";
+            size = actualSize;
+            ptr = actualData;
+            break;
+      }
+      
+      fprintf(stderr, "    %s: size %d: {", label, size);
+      for (j = 0; j < size; j++) {
+         fprintf(stderr, "%s0x%02x", j > 0 ? ", " : "", ptr[j]);
+      }
+      fprintf(stderr, "}\n");
+   }
+
+   /* We fail if the data is unexpected. */
+   return GL_FALSE;
+}
+
+
+static GLboolean 
+compare_ints(const char *errorLabel, GLuint expectedSize, 
+   const GLint *expectedData, GLuint actualSize, const GLint *actualData)
+{
+   int i;
+
+   if (expectedSize == actualSize &&
+      memcmp(expectedData, actualData, actualSize*sizeof(*expectedData)) == 0) {
+      /* All is well */
+      return GL_TRUE;
+   }
+
+   /* Trouble; we don't match.  Print out why. */
+   fprintf(stderr, "%s: actual data is not as expected\n", errorLabel);
+   for (i = 0; i <= 1; i++) {
+      const GLint *ptr;
+      int size;
+      char *label;
+      int j;
+
+      switch(i) {
+         case 0:
+            label = "expected";
+            size = expectedSize;
+            ptr = expectedData;
+            break;
+         case 1:
+            label = "  actual";
+            size = actualSize;
+            ptr = actualData;
+            break;
+      }
+      
+      fprintf(stderr, "    %s: size %d: {", label, size);
+      for (j = 0; j < size; j++) {
+         fprintf(stderr, "%s%d", j > 0 ? ", " : "", ptr[j]);
+      }
+      fprintf(stderr, "}\n");
+   }
+
+   /* We fail if the data is unexpected. */
+   return GL_FALSE;
+}
+
+#define MAX_CONVERTED_VALUES 4
+static GLboolean 
+compare_shorts_to_ints(const char *errorLabel, GLuint expectedSize, 
+   const GLshort *expectedData, GLuint actualSize, const GLint *actualData)
+{
+   int i;
+   GLint convertedValues[MAX_CONVERTED_VALUES];
+
+   if (expectedSize > MAX_CONVERTED_VALUES) {
+      fprintf(stderr, "%s: too much data [need %d values, have %d values]\n",
+         errorLabel, expectedSize, MAX_CONVERTED_VALUES);
+      return GL_FALSE;
+   }
+
+   for (i = 0; i < expectedSize; i++) {
+      convertedValues[i] = (GLint) expectedData[i];
+   }
+
+   return compare_ints(errorLabel, expectedSize, convertedValues, 
+      actualSize, actualData);
+}
+
+static GLboolean 
+compare_floats(const char *errorLabel, GLuint expectedSize, 
+   const GLfloat *expectedData, GLuint actualSize, const GLfloat *actualData)
+{
+   int i;
+
+   if (expectedSize == actualSize &&
+      memcmp(expectedData, actualData, actualSize*sizeof(*expectedData)) == 0) {
+      /* All is well */
+      return GL_TRUE;
+   }
+
+   /* Trouble; we don't match.  Print out why. */
+   fprintf(stderr, "%s: actual data is not as expected\n", errorLabel);
+   for (i = 0; i <= 1; i++) {
+      const GLfloat *ptr;
+      int size;
+      char *label;
+      int j;
+
+      switch(i) {
+         case 0:
+            label = "expected";
+            size = expectedSize;
+            ptr = expectedData;
+            break;
+         case 1:
+            label = "  actual";
+            size = actualSize;
+            ptr = actualData;
+            break;
+      }
+      
+      fprintf(stderr, "    %s: size %d: {", label, size);
+      for (j = 0; j < size; j++) {
+         fprintf(stderr, "%s%f", j > 0 ? ", " : "", ptr[j]);
+      }
+      fprintf(stderr, "}\n");
+   }
+
+   /* We fail if the data is unexpected. */
+   return GL_FALSE;
+}
+
+static GLboolean 
+compare_doubles(const char *errorLabel, GLuint expectedSize, 
+   const GLdouble *expectedData, GLuint actualSize, const GLdouble *actualData)
+{
+   int i;
+
+   if (expectedSize == actualSize || 
+      memcmp(expectedData, actualData, actualSize*sizeof(*expectedData)) == 0) {
+      /* All is well */
+      return GL_TRUE;
+   }
+
+   /* Trouble; we don't match.  Print out why. */
+   fprintf(stderr, "%s: actual data is not as expected\n", errorLabel);
+   for (i = 0; i <= 1; i++) {
+      const GLdouble *ptr;
+      int size;
+      char *label;
+      int j;
+
+      switch(i) {
+         case 0:
+            label = "expected";
+            size = expectedSize;
+            ptr = expectedData;
+            break;
+         case 1:
+            label = "  actual";
+            size = actualSize;
+            ptr = actualData;
+            break;
+      }
+      
+      fprintf(stderr, "    %s: size %d: {", label, size);
+      for (j = 0; j < size; j++) {
+         fprintf(stderr, "%s%f", j > 0 ? ", " : "", ptr[j]);
+      }
+      fprintf(stderr, "}\n");
+   }
+
+   /* We fail if the data is unexpected. */
+   return GL_FALSE;
+}
+
+/********************************************************************
+ * Functions to assist with GL_ARB_texture_compressiong testing
+ */
+
+static GLboolean
+check_texture_format_supported(GLenum format)
+{
+   GLint numFormats;
+   GLint *formats;
+   register int i;
+
+   glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB, &numFormats);
+   formats = malloc(numFormats * sizeof(GLint));
+   if (formats == NULL) {
+      fprintf(stderr, "check_texture_format_supported: could not allocate memory for %d GLints\n", 
+         numFormats);
+      return GL_FALSE;
+   }
+   
+   memset(formats, 0, numFormats * sizeof(GLint));
+   glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS_ARB, formats);
+
+   for (i = 0; i < numFormats; i++) {
+      if (formats[i] == format) {
+         free(formats);
+         return GL_TRUE;
+      }
+   }
+
+   /* We didn't find the format we were looking for.  Give an error. */
+#define FORMAT_NAME(x) (\
+   x == GL_COMPRESSED_RGB_FXT1_3DFX ? "GL_COMPRESSED_RGB_FXT1_3DFX" : \
+   x == GL_COMPRESSED_RGBA_FXT1_3DFX ? "GL_COMPRESSED_RGBA_FXT1_3DFX" : \
+   x == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? "GL_COMPRESSED_RGB_S3TC_DXT1_EXT" : \
+   x == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT" : \
+   x == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ? "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT" : \
+   x == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT ? "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT" : \
+   x == GL_RGB_S3TC ? "GL_RGB_S3TC" : \
+   x == GL_RGB4_S3TC ? "GL_RGB4_S3TC" : \
+   x == GL_RGBA_S3TC ? "GL_RGBA_S3TC" : \
+   x == GL_RGBA4_S3TC ? "GL_RGBA4_S3TC" : \
+   "unknown")
+   fprintf(stderr, "check_texture_format_supported: unsupported format 0x%04x [%s]\n",
+      format, FORMAT_NAME(format));
+   fprintf(stderr, "supported formats:");
+   for (i = 0; i < numFormats; i++) {
+      fprintf(stderr, " 0x%04x [%s]", formats[i], FORMAT_NAME(formats[i]));
+   }
+   fprintf(stderr, "\n");
+   return GL_FALSE;
+}
+
+/* This helper function compresses an RGBA texture and compares it
+ * against the expected compressed data.  It returns GL_TRUE if all
+ * went as expected, or GL_FALSE in the case of error.
+ */
+static GLboolean
+check_texture_compression(const char *message, GLenum dimension,
+   GLint width, GLint height, GLint depth, const GLubyte *texture, 
+   int expectedCompressedSize, const GLubyte *expectedCompressedData)
+{
+   /* These are the data we query about the texture. */
+   GLint isCompressed;
+   GLenum compressedFormat;
+   GLint compressedSize;
+   GLubyte *compressedData;
+
+   /* We need this function pointer to operate. */
+   DECLARE_GLFUNC_PTR(GetCompressedTexImageARB, PFNGLGETCOMPRESSEDTEXIMAGEARBPROC);
+   if (GetCompressedTexImageARB == NULL) {
+      fprintf(stderr, 
+         "%s: could not query GetCompressedTexImageARB function pointer\n",
+         message);
+      return GL_FALSE;
+   }
+
+   /* Verify that we actually have the GL_COMPRESSED_RGBA_S3TC_DXT3_EXT format available. */
+   if (!check_texture_format_supported(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)) {
+      return GL_FALSE;
+   }
+
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   /* Set up the base image, requesting that the GL library compress it. */
+   switch(dimension) {
+      case GL_TEXTURE_1D:
+         glTexImage1D(GL_TEXTURE_1D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            width, 0, 
+            GL_RGBA, GL_UNSIGNED_BYTE, texture);
+         break;
+      case GL_TEXTURE_2D:
+         glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            width, height, 0, 
+            GL_RGBA, GL_UNSIGNED_BYTE, texture);
+         break;
+      case GL_TEXTURE_3D:
+         glTexImage3D(GL_TEXTURE_3D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            width, height, depth, 0, 
+            GL_RGBA, GL_UNSIGNED_BYTE, texture);
+         break;
+      default:
+         fprintf(stderr, "%s: unknown dimension 0x%04x.\n", message, dimension);
+         return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Make sure the texture is compressed, and pull it out if it is. */
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_COMPRESSED_ARB, 
+      &isCompressed);
+   if (!isCompressed) {
+      fprintf(stderr, "%s: could not compress GL_COMPRESSED_RGBA_S3TC_DXT3_EXT texture\n",
+         message);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_INTERNAL_FORMAT, 
+      (GLint *)&compressedFormat);
+   if (compressedFormat != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) {
+      fprintf(stderr, "%s: got internal format 0x%04x, expected GL_COMPRESSED_RGBA_S3TC_DXT3_EXT [0x%04x]\n",
+         __FUNCTION__, compressedFormat, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressedSize);
+   compressedData = malloc(compressedSize);
+   if (compressedData == NULL) {
+      fprintf(stderr, "%s: could not malloc %d bytes for compressed texture\n",
+         message, compressedSize);
+      return GL_FALSE;
+   }
+   memset(compressedData, 0, compressedSize);
+   (*GetCompressedTexImageARB)(dimension, 0, compressedData);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Compare it to the expected compressed data. The compare_bytes()
+    * call will print out diagnostics in the case of failure.
+    */
+   if (!compare_bytes(message, 
+      expectedCompressedSize, expectedCompressedData,
+      compressedSize, compressedData)) {
+
+      free(compressedData);
+      return GL_FALSE;
+   }
+
+   /* All done.  Free our allocated data and return success. */
+   free(compressedData);
+   return GL_TRUE;
+}
+
+/* We'll use one function to exercise 1D, 2D, and 3D textures. */
+
+/* The test function for compressed 3D texture images requires several
+ * different function pointers that have to be queried.  This function
+ * gets all the function pointers it needs itself, and so is suitable for 
+ * use to test any and all of the incorporated functions.
+ */
+
+static GLboolean
+exercise_CompressedTextures(GLenum dimension)
+{
+   /* Set up a basic (uncompressed) texture.  We're doing a blue/yellow
+    * checkerboard.  The 8x4/32-pixel board is well-suited to S3TC
+    * compression, which works on 4x4 blocks of pixels.
+    */
+#define B 0,0,255,255
+#define Y 255,255,0,255
+#define TEXTURE_WIDTH 16 
+#define TEXTURE_HEIGHT 4
+#define TEXTURE_DEPTH 1
+   static GLubyte texture[TEXTURE_WIDTH*TEXTURE_HEIGHT*TEXTURE_DEPTH*4] = {
+      B, B, Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, Y, Y,
+      B, B, Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, Y, Y,
+      Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, 
+      Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, Y, Y, B, B, 
+   };
+#undef B
+#undef Y
+   GLubyte uncompressedTexture[TEXTURE_WIDTH*TEXTURE_HEIGHT*TEXTURE_DEPTH*4];
+
+   /* We'll use this as a texture subimage. */
+#define R 255,0,0,255
+#define G 0,255,0,255
+#define SUBTEXTURE_WIDTH 4
+#define SUBTEXTURE_HEIGHT 4
+#define SUBTEXTURE_DEPTH 1
+   static GLubyte subtexture[SUBTEXTURE_WIDTH*SUBTEXTURE_HEIGHT*SUBTEXTURE_DEPTH*4] = {
+      G, G, R, R,
+      G, G, R, R,
+      R, R, G, G,
+      R, R, G, G,
+   };
+#undef R
+#undef G
+
+   /* These are the expected compressed textures.  (In the case of
+    * a failed comparison, the test program will print out the
+    * actual compressed data in a format that can be directly used
+    * here, if desired.)  The brave of heart can calculate the compression 
+    * themselves based on the formulae described at:
+    *   http://en.wikipedia.org/wiki/S3_Texture_Compression
+    * In a nutshell, each group of 16 bytes encodes a 4x4 texture block.
+    * The first eight bytes of each group are 4-bit alpha values
+    * for each of the 16 pixels in the texture block.
+    * The next four bytes in each group are LSB-first RGB565 colors; the
+    * first two bytes are identified as the color C0, and the next two
+    * are the color C1.  (Two more colors C2 and C3 will be calculated 
+    * from these, but do not appear in the compression data.)  The
+    * last 4 bytes of the group are sixteen 2-bit indices that, for
+    * each of the 16 pixels in the texture block, select one of the
+    * colors C0, C1, C2, or C3.
+    *
+    * For example, our blue/yellow checkerboard is made up of
+    * four identical 4x4 blocks.  Each of those blocks will
+    * be encoded as: eight bytes of 0xff (16 alpha values, each 0xf),
+    * C0 as the RGB565 color yellow (0xffe0), encoded LSB-first;
+    * C1 as the RGB565 color blue (0x001f), encoded LSB-first;
+    * and 4 bytes of 16 2-bit color indices reflecting the
+    * choice of color for each of the 16 pixels:
+    *     00, 00, 01, 01, = 0x05
+    *     00, 00, 01, 01, = 0x05
+    *     01, 01, 00, 00, = 0x50
+    *     01, 01, 00, 00, = 0x50
+    */
+   static GLubyte compressedTexture[] = {
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50
+   };
+
+   /* The similar calculations for the 4x4 subtexture are left
+    * as an exercise for the reader.
+    */
+   static GLubyte compressedSubTexture[] = {
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0x00, 0xf8, 0xe0, 0x07, 0x05, 0x05, 0x50, 0x50,
+   };
+
+   /* The combined texture replaces the initial blue/yellow
+    * block with the green/red block.  (I'd wanted to do
+    * the more interesting exercise of putting the
+    * green/red block in the middle of the blue/yellow
+    * texture, which is a non-trivial replacement, but
+    * the attempt produces GL_INVALID_OPERATION, showing
+    * that you can only replace whole blocks of 
+    * subimages with S3TC.)  The combined texture looks
+    * like:
+    *      G G R R  B B Y Y  B B Y Y  B B Y Y
+    *      G G R R  B B Y Y  B B Y Y  B B Y Y
+    *      R R G G  Y Y B B  Y Y B B  Y Y B B 
+    *      R R G G  Y Y B B  Y Y B B  Y Y B B 
+    * which encodes just like the green/red block followed
+    * by 3 copies of the yellow/blue block.
+    */
+   static GLubyte compressedCombinedTexture[] = {
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0x00, 0xf8, 0xe0, 0x07, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50,
+      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+      0xe0, 0xff, 0x1f, 0x00, 0x05, 0x05, 0x50, 0x50
+   };
+
+   /* These are the data we query about the texture. */
+   GLint queryIsCompressed;
+   GLenum queryCompressedFormat;
+   GLint queryCompressedSize;
+   GLubyte queryCompressedData[sizeof(compressedTexture)];
+
+   /* Query the function pointers we need.  We actually won't need most
+    * of these (the "dimension" parameter dictates whether we're testing
+    * 1D, 2D, or 3D textures), but we'll have them all ready just in case.
+    */
+   DECLARE_GLFUNC_PTR(GetCompressedTexImageARB, PFNGLGETCOMPRESSEDTEXIMAGEARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexImage3DARB, PFNGLCOMPRESSEDTEXIMAGE3DARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexSubImage3DARB, PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexImage2DARB, PFNGLCOMPRESSEDTEXIMAGE2DARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexSubImage2DARB, PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexImage1DARB, PFNGLCOMPRESSEDTEXIMAGE1DARBPROC);
+   DECLARE_GLFUNC_PTR(CompressedTexSubImage1DARB, PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC);
+
+   /* If the necessary functions are missing, we can't continue */
+   if (GetCompressedTexImageARB == NULL) {
+      fprintf(stderr, "%s: GetCompressedTexImageARB function is missing\n",
+         __FUNCTION__);
+      return GL_FALSE;
+   }
+   switch (dimension) {
+      case GL_TEXTURE_1D:
+         if (CompressedTexImage1DARB == NULL || CompressedTexSubImage1DARB == NULL) {
+            fprintf(stderr, "%s: 1D compressed texture functions are missing\n",
+               __FUNCTION__);
+            return GL_FALSE;
+         };
+         break;
+      case GL_TEXTURE_2D:
+         if (CompressedTexImage2DARB == NULL || CompressedTexSubImage2DARB == NULL) {
+            fprintf(stderr, "%s: 2D compressed texture functions are missing\n",
+               __FUNCTION__);
+            return GL_FALSE;
+         };
+         break;
+      case GL_TEXTURE_3D:
+         if (CompressedTexImage3DARB == NULL || CompressedTexSubImage3DARB == NULL) {
+            fprintf(stderr, "%s: 3D compressed texture functions are missing\n",
+               __FUNCTION__);
+            return GL_FALSE;
+         };
+         break;
+      default:
+         fprintf(stderr, "%s: unknown texture dimension 0x%04x passed.\n",
+            __FUNCTION__, dimension);
+         return GL_FALSE;
+   }
+   
+   /* Check the compression of our base texture image. */
+   if (!check_texture_compression("texture compression", dimension,
+         TEXTURE_WIDTH, TEXTURE_HEIGHT, TEXTURE_DEPTH, texture,
+         sizeof(compressedTexture), compressedTexture)) {
+
+      /* Something's wrong with texture compression.  The function
+       * above will have printed an appropriate error.
+       */
+      return GL_FALSE;
+   }
+
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Do the same for our texture subimage */
+   if (!check_texture_compression("subtexture compression", dimension,
+         SUBTEXTURE_WIDTH, SUBTEXTURE_HEIGHT, SUBTEXTURE_DEPTH, subtexture,
+         sizeof(compressedSubTexture), compressedSubTexture)) {
+
+      /* Something's wrong with texture compression.  The function
+       * above will have printed an appropriate error.
+       */
+      return GL_FALSE;
+   }
+
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Send the base compressed texture down to the hardware. */
+   switch(dimension) {
+      case GL_TEXTURE_3D:
+         (*CompressedTexImage3DARB)(GL_TEXTURE_3D, 0, 
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            TEXTURE_WIDTH, TEXTURE_HEIGHT, TEXTURE_DEPTH, 0, 
+            sizeof(compressedTexture), compressedTexture);
+         break;
+
+      case GL_TEXTURE_2D:
+         (*CompressedTexImage2DARB)(GL_TEXTURE_2D, 0, 
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, 
+            sizeof(compressedTexture), compressedTexture);
+         break;
+
+      case GL_TEXTURE_1D:
+         (*CompressedTexImage1DARB)(GL_TEXTURE_1D, 0, 
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            TEXTURE_WIDTH, 0, 
+            sizeof(compressedTexture), compressedTexture);
+         break;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* For grins, query it to make sure it is as expected. */
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_COMPRESSED_ARB, 
+      &queryIsCompressed);
+   if (!queryIsCompressed) {
+      fprintf(stderr, "%s: compressed texture did not come back as compressed\n",
+         __FUNCTION__);
+      return GL_FALSE;
+   }
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_INTERNAL_FORMAT, 
+      (GLint *)&queryCompressedFormat);
+   if (queryCompressedFormat != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) {
+      fprintf(stderr, "%s: got internal format 0x%04x, expected GL_COMPRESSED_RGBA_S3TC_DXT3_EXT [0x%04x]\n",
+         __FUNCTION__, queryCompressedFormat, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
+      return GL_FALSE;
+   }
+   glGetTexLevelParameteriv(dimension, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, 
+      &queryCompressedSize);
+   if (queryCompressedSize != sizeof(compressedTexture)) {
+      fprintf(stderr, "%s: compressed 3D texture changed size: expected %d, actual %d\n",
+         __FUNCTION__, sizeof(compressedTexture), queryCompressedSize);
+      return GL_FALSE;
+   }
+   (*GetCompressedTexImageARB)(dimension, 0, queryCompressedData);
+   if (!compare_bytes(
+      "exercise_CompressedTextures:doublechecking compressed texture",
+      sizeof(compressedTexture), compressedTexture,
+      queryCompressedSize, queryCompressedData)) {
+      return GL_FALSE;
+   }
+
+   /* Now apply the texture subimage.  The current implementation of
+    * S3TC requires that subimages be only applied to whole blocks.
+    */
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   switch(dimension) {
+      case GL_TEXTURE_3D:
+         (*CompressedTexSubImage3DARB)(GL_TEXTURE_3D, 0, 
+            0, 0, 0, /* offsets */
+            SUBTEXTURE_WIDTH, SUBTEXTURE_HEIGHT, SUBTEXTURE_DEPTH,
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            sizeof(compressedSubTexture), compressedSubTexture);
+         break;
+      case GL_TEXTURE_2D:
+         (*CompressedTexSubImage2DARB)(GL_TEXTURE_2D, 0, 
+            0, 0, /* offsets */
+            SUBTEXTURE_WIDTH, SUBTEXTURE_HEIGHT,
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            sizeof(compressedSubTexture), compressedSubTexture);
+         break;
+      case GL_TEXTURE_1D:
+         (*CompressedTexSubImage2DARB)(GL_TEXTURE_2D, 0, 
+            0, 0, /* offsets */
+            SUBTEXTURE_WIDTH, SUBTEXTURE_HEIGHT,
+            GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 
+            sizeof(compressedSubTexture), compressedSubTexture);
+         break;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query the compressed texture back now, and see that it
+    * is as expected.
+    */
+   (*GetCompressedTexImageARB)(dimension, 0, queryCompressedData);
+   if (!compare_bytes("exercise_CompressedTextures:combined texture",
+      sizeof(compressedCombinedTexture), compressedCombinedTexture,
+      queryCompressedSize, queryCompressedData)) {
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Just for the exercise, uncompress the texture and pull it out. 
+    * We don't check it because the compression is lossy, so it won't
+    * compare exactly to the source texture; we just 
+    * want to exercise the code paths that convert it.
+    */
+   glGetTexImage(dimension, 0, GL_RGBA, GL_UNSIGNED_BYTE, uncompressedTexture);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* If we survived this far, we pass. */
+   return GL_TRUE;
+}
+
+/**************************************************************************
+ * Functions to assist with GL_EXT_framebuffer_object and
+ * GL_EXT_framebuffer_blit testing.
+ */
+
+#define FB_STATUS_NAME(x) (\
+   x == GL_FRAMEBUFFER_COMPLETE_EXT ? "GL_FRAMEBUFFER_COMPLETE_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT" : \
+   x == GL_FRAMEBUFFER_UNSUPPORTED_EXT ? "GL_FRAMEBUFFER_UNSUPPORTED_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT" : \
+   x == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT ? "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT" : \
+   "unknown")
+
+static GLboolean
+exercise_framebuffer(void)
+{
+   GLuint framebufferID = 0;
+   GLuint renderbufferID = 0;
+   
+   /* Dimensions of the framebuffer and renderbuffers are arbitrary.
+    * Since they won't be shown on-screen, we can use whatever we want.
+    */
+   const GLint Width = 100;
+   const GLint Height = 100;
+
+   /* Every function we use will be referenced through function pointers.
+    * This will allow this test program to run on OpenGL implementations
+    * that *don't* implement these extensions (though the implementation
+    * used to compile them must have up-to-date header files).
+    */
+   DECLARE_GLFUNC_PTR(GenFramebuffersEXT, PFNGLGENFRAMEBUFFERSEXTPROC);
+   DECLARE_GLFUNC_PTR(IsFramebufferEXT, PFNGLISFRAMEBUFFEREXTPROC);
+   DECLARE_GLFUNC_PTR(DeleteFramebuffersEXT, PFNGLDELETEFRAMEBUFFERSEXTPROC);
+   DECLARE_GLFUNC_PTR(BindFramebufferEXT, PFNGLBINDFRAMEBUFFEREXTPROC);
+   DECLARE_GLFUNC_PTR(GenRenderbuffersEXT, PFNGLGENRENDERBUFFERSEXTPROC);
+   DECLARE_GLFUNC_PTR(IsRenderbufferEXT, PFNGLISRENDERBUFFEREXTPROC);
+   DECLARE_GLFUNC_PTR(DeleteRenderbuffersEXT, PFNGLDELETERENDERBUFFERSEXTPROC);
+   DECLARE_GLFUNC_PTR(BindRenderbufferEXT, PFNGLBINDRENDERBUFFEREXTPROC);
+   DECLARE_GLFUNC_PTR(FramebufferRenderbufferEXT, PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC);
+   DECLARE_GLFUNC_PTR(RenderbufferStorageEXT, PFNGLRENDERBUFFERSTORAGEEXTPROC);
+   DECLARE_GLFUNC_PTR(CheckFramebufferStatusEXT, PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC);
+
+   /* The BlitFramebuffer function comes from a different extension.
+    * It's possible for an implementation to implement all the above,
+    * but not BlitFramebuffer; so it's okay if this one comes back
+    * NULL, as we can still test the rest.
+    */
+   DECLARE_GLFUNC_PTR(BlitFramebufferEXT, PFNGLBLITFRAMEBUFFEREXTPROC);
+
+   /* We cannot test unless we have all the function pointers. */
+   if (
+      GenFramebuffersEXT == NULL ||
+      IsFramebufferEXT == NULL || 
+      DeleteFramebuffersEXT == NULL ||
+      BindFramebufferEXT == NULL ||
+      GenRenderbuffersEXT == NULL ||
+      IsRenderbufferEXT == NULL ||
+      DeleteRenderbuffersEXT == NULL ||
+      BindRenderbufferEXT == NULL ||
+      FramebufferRenderbufferEXT == NULL ||
+      RenderbufferStorageEXT == NULL ||
+      CheckFramebufferStatusEXT == NULL
+   ) {
+      fprintf(stderr, "%s: could not locate all framebuffer functions\n",
+         __FUNCTION__);
+      return GL_FALSE;
+   }
+
+   /* Generate a framebuffer for us to play with. */
+   (*GenFramebuffersEXT)(1, &framebufferID);
+   if (framebufferID == 0) {
+      fprintf(stderr, "%s: failed to generate a frame buffer ID.\n",
+         __FUNCTION__);
+      return GL_FALSE;
+   }
+   /* The generated name is not a framebuffer object until bound. */
+   (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, framebufferID);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   if (!(*IsFramebufferEXT)(framebufferID)) {
+      fprintf(stderr, "%s: generated a frame buffer ID 0x%x that wasn't a framebuffer\n",
+         __FUNCTION__, framebufferID);
+      (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+      (*DeleteFramebuffersEXT)(1, &framebufferID);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   {
+      GLint queriedFramebufferID;
+      glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &queriedFramebufferID);
+      if (queriedFramebufferID != framebufferID) {
+         fprintf(stderr, "%s: bound frame buffer 0x%x, but queried 0x%x\n",
+            __FUNCTION__, framebufferID, queriedFramebufferID);
+         (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+         (*DeleteFramebuffersEXT)(1, &framebufferID);
+         return GL_FALSE;
+      }
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Create a color buffer to attach to the frame buffer object, so
+    * we can actually operate on it.  We go through the same basic checks
+    * with the renderbuffer that we do with the framebuffer.
+    */
+   (*GenRenderbuffersEXT)(1, &renderbufferID);
+   if (renderbufferID == 0) {
+      fprintf(stderr, "%s: could not generate a renderbuffer ID\n",
+         __FUNCTION__);
+      (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+      (*DeleteFramebuffersEXT)(1, &framebufferID);
+      return GL_FALSE;
+   }
+   (*BindRenderbufferEXT)(GL_RENDERBUFFER_EXT, renderbufferID);
+   if (!(*IsRenderbufferEXT)(renderbufferID)) {
+      fprintf(stderr, "%s: generated renderbuffer 0x%x is not a renderbuffer\n",
+         __FUNCTION__, renderbufferID);
+      (*BindRenderbufferEXT)(GL_RENDERBUFFER_EXT, 0);
+      (*DeleteRenderbuffersEXT)(1, &renderbufferID);
+      (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+      (*DeleteFramebuffersEXT)(1, &framebufferID);
+      return GL_FALSE;
+   }
+   {
+      GLint queriedRenderbufferID = 0;
+      glGetIntegerv(GL_RENDERBUFFER_BINDING_EXT, &queriedRenderbufferID);
+      if (renderbufferID != queriedRenderbufferID) {
+         fprintf(stderr, "%s: bound renderbuffer 0x%x, but got 0x%x\n",
+            __FUNCTION__, renderbufferID, queriedRenderbufferID);
+         (*BindRenderbufferEXT)(GL_RENDERBUFFER_EXT, 0);
+         (*DeleteRenderbuffersEXT)(1, &renderbufferID);
+         (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+         (*DeleteFramebuffersEXT)(1, &framebufferID);
+         return GL_FALSE;
+      }
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Add the renderbuffer as a color attachment to the current
+    * framebuffer (which is our generated framebuffer).
+    */
+   (*FramebufferRenderbufferEXT)(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT,
+      GL_RENDERBUFFER_EXT, renderbufferID);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* The renderbuffer will need some dimensions and storage space. */
+   (*RenderbufferStorageEXT)(GL_RENDERBUFFER_EXT, GL_RGB, Width, Height);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* That should be everything we need.  If we set up to draw and to
+    * read from our color attachment, we should be "framebuffer complete",
+    * meaning the framebuffer is ready to go.
+    */
+   glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
+   glReadBuffer(GL_COLOR_ATTACHMENT1_EXT);
+   {
+      GLenum status = (*CheckFramebufferStatusEXT)(GL_FRAMEBUFFER_EXT);
+      if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+         fprintf(stderr, "%s: framebuffer not complete; status = %s [0x%x]\n",
+            __FUNCTION__, FB_STATUS_NAME(status), status);
+         glReadBuffer(0);
+         glDrawBuffer(0);
+         (*BindRenderbufferEXT)(GL_RENDERBUFFER_EXT, 0);
+         (*DeleteRenderbuffersEXT)(1, &renderbufferID);
+         (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+         (*DeleteFramebuffersEXT)(1, &framebufferID);
+         return GL_FALSE;
+      }
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Define the contents of the frame buffer */
+   glClearColor(0.5, 0.5, 0.5, 0.0);
+   glClear(GL_COLOR_BUFFER_BIT);
+
+   /* If the GL_EXT_framebuffer_blit is supported, attempt a framebuffer
+    * blit from (5,5)-(10,10) to (90,90)-(95,95).  This is *not* an
+    * error if framebuffer_blit is *not* supported (as we can still
+    * effectively test the other functions).
+    */
+   if (BlitFramebufferEXT != NULL) {
+      (*BlitFramebufferEXT)(5, 5, 10, 10, 90, 90, 95, 95,
+         GL_COLOR_BUFFER_BIT, GL_NEAREST);
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* We could now test to see whether the framebuffer had the desired
+    * contents.  As this is just a touch test, we'll leave that for now.
+    * Clean up and go home.
+    */
+   glReadBuffer(0);
+   glDrawBuffer(0);
+   (*BindRenderbufferEXT)(GL_RENDERBUFFER_EXT, 0);
+   (*DeleteRenderbuffersEXT)(1, &renderbufferID);
+   (*BindFramebufferEXT)(GL_FRAMEBUFFER_EXT, 0);
+   (*DeleteFramebuffersEXT)(1, &framebufferID);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   return GL_TRUE;
+}
+
+/**************************************************************************
+ * Functions to assist with GL_ARB_shader_objects testing.
+ */
+
+static void
+print_info_log(const char *message, GLhandleARB object)
+{
+   DECLARE_GLFUNC_PTR(GetObjectParameterivARB, PFNGLGETOBJECTPARAMETERIVARBPROC);
+   DECLARE_GLFUNC_PTR(GetInfoLogARB, PFNGLGETINFOLOGARBPROC);
+   int logLength, queryLength;
+   char *log;
+
+   if (GetObjectParameterivARB == NULL) {
+      fprintf(stderr, "%s: could not get GetObjectParameterivARB address\n",
+         message);
+      return;
+   }
+   if (GetInfoLogARB == NULL) {
+      fprintf(stderr, "%s: could not get GetInfoLogARB address\n",
+         message);
+      return;
+   }
+
+   (*GetObjectParameterivARB)(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, 
+      &logLength);
+   if (logLength == 0) {
+      fprintf(stderr, "%s: info log length is 0\n", message);
+      return;
+   }
+   log = malloc(logLength);
+   if (log == NULL) {
+      fprintf(stderr, "%s: could not malloc %d bytes for info log\n",
+         message, logLength);
+   }
+   else {
+      (*GetInfoLogARB)(object, logLength, &queryLength, log);
+      fprintf(stderr, "%s: info log says '%s'\n",
+         message, log);
+   }
+   free(log);
+}
+
+static GLboolean
+exercise_uniform_start(const char *fragmentShaderText, const char *uniformName,
+   GLhandleARB *returnProgram, GLint *returnUniformLocation)
+{
+   DECLARE_GLFUNC_PTR(CreateShaderObjectARB, PFNGLCREATESHADEROBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(ShaderSourceARB, PFNGLSHADERSOURCEARBPROC);
+   DECLARE_GLFUNC_PTR(CompileShaderARB, PFNGLCOMPILESHADERARBPROC);
+   DECLARE_GLFUNC_PTR(CreateProgramObjectARB, PFNGLCREATEPROGRAMOBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(AttachObjectARB, PFNGLATTACHOBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(LinkProgramARB, PFNGLLINKPROGRAMARBPROC);
+   DECLARE_GLFUNC_PTR(UseProgramObjectARB, PFNGLUSEPROGRAMOBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(ValidateProgramARB, PFNGLVALIDATEPROGRAMARBPROC);
+   DECLARE_GLFUNC_PTR(GetUniformLocationARB, PFNGLGETUNIFORMLOCATIONARBPROC);
+   DECLARE_GLFUNC_PTR(DeleteObjectARB, PFNGLDELETEOBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(GetObjectParameterivARB, PFNGLGETOBJECTPARAMETERIVARBPROC);
+   GLhandleARB fs, program;
+   GLint uniformLocation;
+   GLint shaderCompiled, programValidated;
+
+   if (CreateShaderObjectARB == NULL ||
+       ShaderSourceARB == NULL ||
+       CompileShaderARB == NULL ||
+       CreateProgramObjectARB == NULL ||
+       AttachObjectARB == NULL ||
+       LinkProgramARB == NULL ||
+       UseProgramObjectARB == NULL ||
+       ValidateProgramARB == NULL ||
+       GetUniformLocationARB == NULL ||
+       DeleteObjectARB == NULL ||
+       GetObjectParameterivARB == NULL ||
+       0) {
+      return GL_FALSE;
+   }
+
+   /* Create the trivial fragment shader and program.  For safety
+    * we'll check to make sure they compile and link correctly.
+    */
+   fs = (*CreateShaderObjectARB)(GL_FRAGMENT_SHADER_ARB);
+   (*ShaderSourceARB)(fs, 1, &fragmentShaderText, NULL);
+   (*CompileShaderARB)(fs);
+   (*GetObjectParameterivARB)(fs, GL_OBJECT_COMPILE_STATUS_ARB,
+      &shaderCompiled);
+   if (!shaderCompiled) {
+      print_info_log("shader did not compile", fs);
+      (*DeleteObjectARB)(fs);
+      CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   program = (*CreateProgramObjectARB)();
+   (*AttachObjectARB)(program, fs);
+   (*LinkProgramARB)(program);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Make sure we're going to run successfully */
+   (*ValidateProgramARB)(program);
+   (*GetObjectParameterivARB)(program, GL_OBJECT_VALIDATE_STATUS_ARB, 
+      &programValidated);
+   if (!programValidated) {; 
+      print_info_log("program did not validate", program);
+      (*DeleteObjectARB)(program);
+      (*DeleteObjectARB)(fs);
+      CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+      return GL_FALSE;
+   }
+
+   /* Put the program in place.  We're not allowed to assign to uniform
+    * variables used by the program until the program is put into use.
+    */
+   (*UseProgramObjectARB)(program);
+
+   /* Once the shader is in place, we're free to delete it; this
+    * won't affect the copy that's part of the program.
+    */
+   (*DeleteObjectARB)(fs);
+
+   /* Find the location index of the uniform variable we declared;
+    * the caller will ned that to set the value.
+    */
+   uniformLocation = (*GetUniformLocationARB)(program, uniformName);
+   if (uniformLocation == -1) {
+      fprintf(stderr, "%s: could not determine uniform location\n",
+         __FUNCTION__);
+      (*DeleteObjectARB)(program);
+      CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+      return GL_FALSE;
+   }
+
+   /* All done with what we're supposed to do - return the program
+    * handle and the uniform location to the caller.
+    */
+   *returnProgram = program;
+   *returnUniformLocation = uniformLocation;
+   return GL_TRUE;
+}
+
+static void
+exercise_uniform_end(GLhandleARB program)
+{
+   DECLARE_GLFUNC_PTR(UseProgramObjectARB, PFNGLUSEPROGRAMOBJECTARBPROC);
+   DECLARE_GLFUNC_PTR(DeleteObjectARB, PFNGLDELETEOBJECTARBPROC);
+   if (UseProgramObjectARB == NULL || DeleteObjectARB == NULL) {
+      return;
+   }
+
+   /* Turn off our program by setting the special value 0, and
+    * then delete the program object.
+    */
+   (*UseProgramObjectARB)(0);
+   (*DeleteObjectARB)(program);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+}
+
+/**************************************************************************
+ * Exercises for fences
+ */
+static GLboolean
+exercise_fences(void)
+{
+   DECLARE_GLFUNC_PTR(DeleteFencesNV, PFNGLDELETEFENCESNVPROC);
+   DECLARE_GLFUNC_PTR(FinishFenceNV, PFNGLFINISHFENCENVPROC);
+   DECLARE_GLFUNC_PTR(GenFencesNV, PFNGLGENFENCESNVPROC);
+   DECLARE_GLFUNC_PTR(GetFenceivNV, PFNGLGETFENCEIVNVPROC);
+   DECLARE_GLFUNC_PTR(IsFenceNV, PFNGLISFENCENVPROC);
+   DECLARE_GLFUNC_PTR(SetFenceNV, PFNGLSETFENCENVPROC);
+   DECLARE_GLFUNC_PTR(TestFenceNV, PFNGLTESTFENCENVPROC);
+   GLuint fence;
+   GLint fenceStatus, fenceCondition;
+   int count;
+
+   /* Make sure we have all the function pointers we need. */
+   if (GenFencesNV == NULL ||
+      SetFenceNV == NULL ||
+      IsFenceNV == NULL ||
+      GetFenceivNV == NULL ||
+      TestFenceNV == NULL ||
+      FinishFenceNV == NULL ||
+      DeleteFencesNV == NULL) {
+      fprintf(stderr, "%s: don't have all the fence functions\n",
+         __FUNCTION__);
+      return GL_FALSE;
+   }
+
+   /* Create and set a simple fence. */
+   (*GenFencesNV)(1, &fence);
+   (*SetFenceNV)(fence, GL_ALL_COMPLETED_NV);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Make sure it reads as a fence. */
+   if (!(*IsFenceNV)(fence)) {
+      fprintf(stderr, "%s: set fence is not a fence\n", __FUNCTION__);
+      (*DeleteFencesNV)(1, &fence);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Try to read back its current status and condition. */
+   (*GetFenceivNV)(fence, GL_FENCE_CONDITION_NV, &fenceCondition);
+   if (fenceCondition != GL_ALL_COMPLETED_NV) {
+      fprintf(stderr, "%s: expected fence condition 0x%x, got 0x%x\n",
+         __FUNCTION__, GL_ALL_COMPLETED_NV, fenceCondition);
+      (*DeleteFencesNV)(1, &fence);
+      return GL_FALSE;
+   }
+   (*GetFenceivNV)(fence, GL_FENCE_STATUS_NV, &fenceStatus);
+   if (fenceStatus != GL_TRUE && fenceStatus != GL_FALSE) {
+      fprintf(stderr,"%s: fence status should be GL_TRUE or GL_FALSE, got 0x%x\n",
+         __FUNCTION__, fenceStatus);
+      (*DeleteFencesNV)(1, &fence);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Set the fence again, query its status, and wait for it to finish
+    * two different ways: once by looping on TestFence(), and a 
+    * second time by a simple call to FinishFence();
+    */
+   (*SetFenceNV)(fence, GL_ALL_COMPLETED_NV);
+   glFlush();
+   count = 1;
+   while (!(*TestFenceNV)(fence)) {
+      count++;
+      if (count == 0) {
+         break;
+      }
+   }
+   if (count == 0) {
+      fprintf(stderr, "%s: fence never returned true\n", __FUNCTION__);
+      (*DeleteFencesNV)(1, &fence);
+      return GL_FALSE;
+   }
+   (*SetFenceNV)(fence, GL_ALL_COMPLETED_NV);
+   (*FinishFenceNV)(fence);
+   if ((*TestFenceNV)(fence) != GL_TRUE) {
+      fprintf(stderr, "%s: finished fence does not have status GL_TRUE\n",
+         __FUNCTION__);
+      (*DeleteFencesNV)(1, &fence);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* All done.  Delete the fence and return. */
+   (*DeleteFencesNV)(1, &fence);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   return GL_TRUE;
+}
+
+/**************************************************************************
+ * Exercises for buffer objects
+ */
+enum Map_Buffer_Usage{ Use_Map_Buffer, Use_Map_Buffer_Range};
+static GLboolean
+exercise_buffer_objects(enum Map_Buffer_Usage usage)
+{
+#define BUFFER_DATA_SIZE 1024
+   GLuint bufferID;
+   GLint bufferMapped;
+   static GLubyte data[BUFFER_DATA_SIZE] = {0};
+   float *dataPtr;
+
+   /* Get the function pointers we need.  These are from
+    * GL_ARB_vertex_buffer_object and are required in all
+    * cases.
+    */
+   DECLARE_GLFUNC_PTR(GenBuffersARB, PFNGLGENBUFFERSARBPROC);
+   DECLARE_GLFUNC_PTR(BindBufferARB, PFNGLBINDBUFFERARBPROC);
+   DECLARE_GLFUNC_PTR(BufferDataARB, PFNGLBUFFERDATAARBPROC);
+   DECLARE_GLFUNC_PTR(MapBufferARB, PFNGLMAPBUFFERARBPROC);
+   DECLARE_GLFUNC_PTR(UnmapBufferARB, PFNGLUNMAPBUFFERARBPROC);
+   DECLARE_GLFUNC_PTR(DeleteBuffersARB, PFNGLDELETEBUFFERSARBPROC);
+   DECLARE_GLFUNC_PTR(GetBufferParameterivARB, PFNGLGETBUFFERPARAMETERIVARBPROC);
+
+   /* These are from GL_ARB_map_buffer_range, and are optional
+    * unless we're given Use_Map_Buffer_Range.  Note that they do *not*
+    * have the standard "ARB" suffixes; this is because the extension
+    * was introduced *after* a superset was standardized in OpenGL 3.0.
+    * (The extension really only exists to allow the functionality on
+    * devices that cannot implement a full OpenGL 3.0 driver.)
+    */
+   DECLARE_GLFUNC_PTR(FlushMappedBufferRange, PFNGLFLUSHMAPPEDBUFFERRANGEPROC);
+   DECLARE_GLFUNC_PTR(MapBufferRange, PFNGLMAPBUFFERRANGEPROC);
+   
+   /* This is from APPLE_flush_buffer_range, and is optional even if
+    * we're given Use_Map_Buffer_Range.  Test it before using it.
+    */
+   DECLARE_GLFUNC_PTR(BufferParameteriAPPLE, PFNGLBUFFERPARAMETERIAPPLEPROC);
+
+   /* Make sure we have all the function pointers we need. */
+   if (GenBuffersARB == NULL ||
+      BindBufferARB == NULL ||
+      BufferDataARB == NULL ||
+      MapBufferARB == NULL ||
+      UnmapBufferARB == NULL ||
+      DeleteBuffersARB == NULL ||
+      GetBufferParameterivARB == NULL) {
+      fprintf(stderr, "%s: missing basic MapBuffer functions\n", __FUNCTION__);
+      return GL_FALSE;
+   }
+   if (usage == Use_Map_Buffer_Range) {
+      if (FlushMappedBufferRange == NULL || MapBufferRange == NULL) {
+         fprintf(stderr, "%s: missing MapBufferRange functions\n", __FUNCTION__);
+         return GL_FALSE;
+      }
+   }
+
+   /* Create and define a buffer */
+   (*GenBuffersARB)(1, &bufferID);
+   (*BindBufferARB)(GL_ARRAY_BUFFER_ARB, bufferID);
+   (*BufferDataARB)(GL_ARRAY_BUFFER_ARB, BUFFER_DATA_SIZE, data, 
+      GL_DYNAMIC_DRAW_ARB);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* If we're using MapBufferRange, and if the BufferParameteriAPPLE
+    * function is present, use it before mapping.  This particular
+    * use is a no-op, intended just to exercise the entry point.
+    */
+   if (usage == Use_Map_Buffer_Range && BufferParameteriAPPLE != NULL) {
+      (*BufferParameteriAPPLE)(GL_ARRAY_BUFFER_ARB, 
+         GL_BUFFER_SERIALIZED_MODIFY_APPLE, GL_TRUE);
+   }
+
+   /* Map it, and make sure it's mapped. */
+   switch(usage) {
+      case Use_Map_Buffer:
+         dataPtr = (float *) (*MapBufferARB)(
+            GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+         break;
+      case Use_Map_Buffer_Range:
+         dataPtr = (float *)(*MapBufferRange)(GL_ARRAY_BUFFER_ARB,
+            4, 16, GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
+         break;
+   }
+   if (dataPtr == NULL) {
+      fprintf(stderr, "%s: %s returned NULL\n", __FUNCTION__,
+         usage == Use_Map_Buffer ? "MapBuffer" : "MapBufferRange");
+      (*BindBufferARB)(GL_ARRAY_BUFFER_ARB, 0);
+      (*DeleteBuffersARB)(1, &bufferID);
+      return GL_FALSE;
+   }
+   (*GetBufferParameterivARB)(GL_ARRAY_BUFFER_ARB, GL_BUFFER_MAPPED_ARB, 
+      &bufferMapped);
+   if (!bufferMapped) {
+      fprintf(stderr, "%s: buffer should be mapped but isn't\n", __FUNCTION__);
+      (*BindBufferARB)(GL_ARRAY_BUFFER_ARB, 0);
+      (*DeleteBuffersARB)(1, &bufferID);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Write something to it, just to make sure we don't segfault. */
+   *dataPtr = 1.5;
+
+   /* Unmap to show we're finished with the buffer.  Note that if we're
+    * using MapBufferRange, we first have to flush the range we modified.
+    */
+   if (usage == Use_Map_Buffer_Range) {
+      (*FlushMappedBufferRange)(GL_ARRAY_BUFFER_ARB, 4, 16);
+   }
+   if (!(*UnmapBufferARB)(GL_ARRAY_BUFFER_ARB)) {
+      fprintf(stderr, "%s: UnmapBuffer failed\n", __FUNCTION__);
+      (*BindBufferARB)(GL_ARRAY_BUFFER_ARB, 0);
+      (*DeleteBuffersARB)(1, &bufferID);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* All done. */
+   (*BindBufferARB)(GL_ARRAY_BUFFER_ARB, 0);
+   (*DeleteBuffersARB)(1, &bufferID);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+   return GL_TRUE;
+
+#undef BUFFER_DATA_SIZE
+}
+
+/**************************************************************************
+ * Exercises for occlusion query
+ */
+static GLboolean
+exercise_occlusion_query(void)
+{
+   GLuint queryObject;
+   GLint queryReady;
+   GLuint querySampleCount;
+   GLint queryCurrent;
+   GLint queryCounterBits;
+
+   /* Get the function pointers we need.  These are from
+    * GL_ARB_vertex_buffer_object and are required in all
+    * cases.
+    */
+   DECLARE_GLFUNC_PTR(GenQueriesARB, PFNGLGENQUERIESARBPROC);
+   DECLARE_GLFUNC_PTR(BeginQueryARB, PFNGLBEGINQUERYARBPROC);
+   DECLARE_GLFUNC_PTR(GetQueryivARB, PFNGLGETQUERYIVARBPROC);
+   DECLARE_GLFUNC_PTR(EndQueryARB, PFNGLENDQUERYARBPROC);
+   DECLARE_GLFUNC_PTR(IsQueryARB, PFNGLISQUERYARBPROC);
+   DECLARE_GLFUNC_PTR(GetQueryObjectivARB, PFNGLGETQUERYOBJECTIVARBPROC);
+   DECLARE_GLFUNC_PTR(GetQueryObjectuivARB, PFNGLGETQUERYOBJECTUIVARBPROC);
+   DECLARE_GLFUNC_PTR(DeleteQueriesARB, PFNGLDELETEQUERIESARBPROC);
+
+   /* Make sure we have all the function pointers we need. */
+   if (GenQueriesARB == NULL ||
+      BeginQueryARB == NULL ||
+      GetQueryivARB == NULL ||
+      EndQueryARB == NULL ||
+      IsQueryARB == NULL ||
+      GetQueryObjectivARB == NULL ||
+      GetQueryObjectuivARB == NULL ||
+      DeleteQueriesARB == NULL) {
+      fprintf(stderr, "%s: don't have all the Query functions\n", __FUNCTION__);
+      return GL_FALSE;
+   }
+
+   /* Create a query object, and start a query. */
+   (*GenQueriesARB)(1, &queryObject);
+   (*BeginQueryARB)(GL_SAMPLES_PASSED_ARB, queryObject);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* While we're in the query, check the functions that are supposed
+    * to return which query we're in and how many bits of resolution
+    * we get.
+    */
+   (*GetQueryivARB)(GL_SAMPLES_PASSED_ARB, GL_CURRENT_QUERY_ARB, &queryCurrent);
+   if (queryCurrent != queryObject) {
+      fprintf(stderr, "%s: current query 0x%x != set query 0x%x\n",
+         __FUNCTION__, queryCurrent, queryObject);
+      (*EndQueryARB)(GL_SAMPLES_PASSED_ARB);
+      (*DeleteQueriesARB)(1, &queryObject);
+      return GL_FALSE;
+   }
+   (*GetQueryivARB)(GL_SAMPLES_PASSED_ARB, GL_QUERY_COUNTER_BITS_ARB, 
+      &queryCounterBits);
+   if (queryCounterBits < 1) {
+      fprintf(stderr, "%s: query counter bits is too small (%d)\n",
+         __FUNCTION__, queryCounterBits);
+      (*EndQueryARB)(GL_SAMPLES_PASSED_ARB);
+      (*DeleteQueriesARB)(1, &queryObject);
+      return GL_FALSE;
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Finish up the query.  Since we didn't draw anything, the result
+    * should be 0 passed samples.
+    */
+   (*EndQueryARB)(GL_SAMPLES_PASSED_ARB);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Routine existence test */
+   if (!(*IsQueryARB)(queryObject)) {
+      fprintf(stderr, "%s: query object 0x%x fails existence test\n",
+         __FUNCTION__, queryObject);
+      (*DeleteQueriesARB)(1, &queryObject);
+      return GL_FALSE;
+   }
+
+   /* Loop until the query is ready, then get back the result.  We use
+    * the signed query for the boolean value of whether the result is
+    * available, but the unsigned query to actually pull the result;
+    * this is just to test both entrypoints, but in a real query you may
+    * need the extra bit of resolution.
+    */
+   queryReady = GL_FALSE;
+   do {
+      (*GetQueryObjectivARB)(queryObject, GL_QUERY_RESULT_AVAILABLE_ARB, 
+         &queryReady);
+   } while (!queryReady);
+   (*GetQueryObjectuivARB)(queryObject, GL_QUERY_RESULT_ARB, &querySampleCount);
+   (*DeleteQueriesARB)(1, &queryObject);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* If sample count isn't 0, something's funny. */
+   if (querySampleCount > 0) {
+      fprintf(stderr, "%s: expected query result of 0, got %ud\n",
+         __FUNCTION__, querySampleCount);
+      return GL_FALSE;
+   }
+
+   /* Here, all is well. */
+   return GL_TRUE;
+}
+
+/**************************************************************************
  * The following functions are used to check that the named OpenGL function
  * actually does what it's supposed to do.
- * The naming of these functions is signficant.  The getprocaddress.py script
+ * The naming of these functions is significant.  The getprocaddress.py script
  * scans this file and extracts these function names.
  */
 
+static GLboolean
+test_WeightPointerARB(generic_func func)
+{
+   /* Assume we have at least 2 vertex units (or this extension makes
+    * no sense), and establish a set of 2-element vector weights.
+    * We use floats that can be represented exactly in binary
+    * floating point formats so we can compare correctly later.
+    * We also make sure the 0th entry matches the default weights,
+    * so we can restore the default easily.
+    */
+#define USE_VERTEX_UNITS 2
+#define USE_WEIGHT_INDEX 3
+   static GLfloat weights[] = {
+      1.0,   0.0,
+      0.875, 0.125,
+      0.75,  0.25,
+      0.625, 0.375,
+      0.5,   0.5,
+      0.375, 0.625,
+      0.25,  0.75,
+      0.125, 0.875, 
+      0.0,   1.0,
+   };
+   GLint numVertexUnits;
+   GLfloat *currentWeights;
+   int i;
+   int errorCount = 0;
+
+   PFNGLWEIGHTPOINTERARBPROC WeightPointerARB = (PFNGLWEIGHTPOINTERARBPROC) func;
+
+   /* Make sure we have at least two vertex units */
+   glGetIntegerv(GL_MAX_VERTEX_UNITS_ARB, &numVertexUnits);
+   if (numVertexUnits < USE_VERTEX_UNITS) {
+      fprintf(stderr, "%s: need %d vertex units, got %d\n", 
+         __FUNCTION__, USE_VERTEX_UNITS, numVertexUnits);
+      return GL_FALSE;
+   }
+   
+   /* Make sure we allocate enough room to query all the current weights */
+   currentWeights = (GLfloat *)malloc(numVertexUnits * sizeof(GLfloat));
+   if (currentWeights == NULL) {
+      fprintf(stderr, "%s: couldn't allocate room for %d floats\n",
+         __FUNCTION__, numVertexUnits);
+      return GL_FALSE;
+   }
+
+   /* Set up the pointer, enable the state, and try to send down a
+    * weight vector (we'll arbitrarily send index 2).
+    */
+   (*WeightPointerARB)(USE_VERTEX_UNITS, GL_FLOAT, 0, weights);
+   glEnableClientState(GL_WEIGHT_ARRAY_ARB);
+   glArrayElement(USE_WEIGHT_INDEX);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Verify that it changed the current state. */
+   glGetFloatv(GL_CURRENT_WEIGHT_ARB, currentWeights);
+   for (i = 0; i < numVertexUnits; i++) {
+      if (i < USE_VERTEX_UNITS) {
+         /* This is one of the units we explicitly set. */
+         if (currentWeights[i] != weights[USE_VERTEX_UNITS*USE_WEIGHT_INDEX + i]) {
+            fprintf(stderr, "%s: current weight at index %d is %f, should be %f\n",
+               __FUNCTION__, i, currentWeights[i], 
+               weights[USE_VERTEX_UNITS*USE_WEIGHT_INDEX + i]);
+            errorCount++;
+         }
+      }
+      else {
+         /* All other weights should be 0. */
+         if (currentWeights[i] != 0.0) {
+            fprintf(stderr, "%s: current weight at index %d is %f, should be %f\n",
+               __FUNCTION__, i, 0.0,
+               weights[USE_VERTEX_UNITS*USE_WEIGHT_INDEX + i]);
+            errorCount++;
+         }
+      }
+   }
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Restore the old state.  We know the default set of weights is in
+    * index 0.
+    */
+   glArrayElement(0);
+   glDisableClientState(GL_WEIGHT_ARRAY_ARB);
+   (*WeightPointerARB)(0, GL_FLOAT, 0, NULL);
+   free(currentWeights);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* We're fine if we didn't get any mismatches. */
+   if (errorCount == 0) {
+      return GL_TRUE;
+   }
+   else {
+      return GL_FALSE;
+   }
+}
+
+/* Wrappers on the exercise_occlusion_query function */
+static GLboolean
+test_GenQueriesARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_BeginQueryARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_GetQueryivARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_EndQueryARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_IsQueryARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_GetQueryObjectivARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_GetQueryObjectuivARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+static GLboolean
+test_DeleteQueriesARB(generic_func func)
+{
+   (void) func;
+   return exercise_occlusion_query();
+}
+
+/* Wrappers on the exercise_buffer_objects() function */
+static GLboolean
+test_GenBuffersARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_BindBufferARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_BufferDataARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_MapBufferARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_UnmapBufferARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_DeleteBuffersARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_GetBufferParameterivARB(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer);
+}
+static GLboolean
+test_FlushMappedBufferRange(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer_Range);
+}
+static GLboolean
+test_MapBufferRange(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer_Range);
+}
+static GLboolean
+test_BufferParameteriAPPLE(generic_func func)
+{
+   (void) func;
+   return exercise_buffer_objects(Use_Map_Buffer_Range);
+}
+
+/* Wrappers on the exercise_framebuffer() function */
+static GLboolean
+test_BindFramebufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_BindRenderbufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_CheckFramebufferStatusEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_DeleteFramebuffersEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_DeleteRenderbuffersEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_FramebufferRenderbufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_GenFramebuffersEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_GenRenderbuffersEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_IsFramebufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_IsRenderbufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_RenderbufferStorageEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+static GLboolean
+test_BlitFramebufferEXT(generic_func func)
+{
+   (void) func;
+   return exercise_framebuffer();
+}
+
+/* These are wrappers on the exercise_CompressedTextures function. 
+ * Unfortunately, we cannot test the 1D counterparts, because the
+ * texture compressions available all support 2D and higher only.
+ */
+static GLboolean
+test_CompressedTexImage2DARB(generic_func func)
+{
+   (void) func;
+   return exercise_CompressedTextures(GL_TEXTURE_2D);
+}
+static GLboolean
+test_CompressedTexSubImage2DARB(generic_func func)
+{
+   (void) func;
+   return exercise_CompressedTextures(GL_TEXTURE_2D);
+}
+static GLboolean
+test_CompressedTexImage3DARB(generic_func func)
+{
+   (void) func;
+   return exercise_CompressedTextures(GL_TEXTURE_3D);
+}
+static GLboolean
+test_CompressedTexSubImage3DARB(generic_func func)
+{
+   (void) func;
+   return exercise_CompressedTextures(GL_TEXTURE_3D);
+}
+static GLboolean
+test_GetCompressedTexImageARB(generic_func func)
+{
+   (void) func;
+   return exercise_CompressedTextures(GL_TEXTURE_3D);
+}
+
+/* Wrappers on exercise_fences(). */
+static GLboolean
+test_DeleteFencesNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_GenFencesNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_SetFenceNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_TestFenceNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_FinishFenceNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_GetFenceivNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+static GLboolean
+test_IsFenceNV(generic_func func)
+{
+   (void) func;
+   return exercise_fences();
+}
+
+/* A bunch of glUniform*() tests */
+static GLboolean
+test_Uniform1iv(generic_func func)
+{
+   PFNGLUNIFORM1IVARBPROC Uniform1ivARB = (PFNGLUNIFORM1IVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform int uniformColor;" 
+      "void main() {gl_FragColor.r = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[1] = {1};
+   GLint queriedUniform[1];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * so we must set it using integer versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform1ivARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 1, uniform, 1, queriedUniform);
+}
+
+static GLboolean
+test_Uniform1i(generic_func func)
+{
+   PFNGLUNIFORM1IARBPROC Uniform1iARB = (PFNGLUNIFORM1IARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform int uniformColor;"
+      "void main() {gl_FragColor.r = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[1] = {1};
+   GLint queriedUniform[4];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * so we must set it using integer versions
+    * of the Uniform* functions.
+    */
+   (*Uniform1iARB)(uniformLocation, uniform[0]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 1, uniform, 1, queriedUniform);
+}
+
+static GLboolean
+test_Uniform1fv(generic_func func)
+{
+   PFNGLUNIFORM1FVARBPROC Uniform1fvARB = (PFNGLUNIFORM1FVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform float uniformColor;"
+      "void main() {gl_FragColor.r = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[1] = {1.1};
+   GLfloat queriedUniform[1];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * so we must set it using float versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform1fvARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 1, uniform, 1, queriedUniform);
+}
+
+static GLboolean
+test_Uniform1f(generic_func func)
+{
+   PFNGLUNIFORM1FARBPROC Uniform1fARB = (PFNGLUNIFORM1FARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform float uniformColor;"
+      "void main() {gl_FragColor.r = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[1] = {1.1};
+   GLfloat queriedUniform[1];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * so we must set it using float versions
+    * of the Uniform* functions.
+    */
+   (*Uniform1fARB)(uniformLocation, uniform[0]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 1, uniform, 1, queriedUniform);
+}
+
+static GLboolean
+test_Uniform2iv(generic_func func)
+{
+   PFNGLUNIFORM2IVARBPROC Uniform2ivARB = (PFNGLUNIFORM2IVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec2 uniformColor;" 
+      "void main() {gl_FragColor.rg = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[2] = {1,2};
+   GLint queriedUniform[2];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector 2 (ivec2), so we must set it using integer versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform2ivARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 2, uniform, 2, queriedUniform);
+}
+
+static GLboolean
+test_Uniform2i(generic_func func)
+{
+   PFNGLUNIFORM2IARBPROC Uniform2iARB = (PFNGLUNIFORM2IARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec2 uniformColor;"
+      "void main() {gl_FragColor.rg = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[2] = {1,2};
+   GLint queriedUniform[4];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector 2 (ivec2), so we must set it using integer versions
+    * of the Uniform* functions.
+    */
+   (*Uniform2iARB)(uniformLocation, uniform[0], uniform[1]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 2, uniform, 2, queriedUniform);
+}
+
+static GLboolean
+test_Uniform2fv(generic_func func)
+{
+   PFNGLUNIFORM2FVARBPROC Uniform2fvARB = (PFNGLUNIFORM2FVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec2 uniformColor;"
+      "void main() {gl_FragColor.rg = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[2] = {1.1,2.2};
+   GLfloat queriedUniform[2];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * vector 2 (vec2), so we must set it using float versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform2fvARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 2, uniform, 2, queriedUniform);
+}
+
+static GLboolean
+test_Uniform2f(generic_func func)
+{
+   PFNGLUNIFORM2FARBPROC Uniform2fARB = (PFNGLUNIFORM2FARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec2 uniformColor;"
+      "void main() {gl_FragColor.rg = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[2] = {1.1,2.2};
+   GLfloat queriedUniform[2];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * vector 2 (vec2), so we must set it using float versions
+    * of the Uniform* functions.
+    */
+   (*Uniform2fARB)(uniformLocation, uniform[0], uniform[1]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 2, uniform, 2, queriedUniform);
+}
+
+static GLboolean
+test_Uniform3iv(generic_func func)
+{
+   PFNGLUNIFORM3IVARBPROC Uniform3ivARB = (PFNGLUNIFORM3IVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec3 uniformColor;" 
+      "void main() {gl_FragColor.rgb = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[3] = {1,2,3};
+   GLint queriedUniform[3];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector 3 (ivec3), so we must set it using integer versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform3ivARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 3, uniform, 3, queriedUniform);
+}
+
+static GLboolean
+test_Uniform3i(generic_func func)
+{
+   PFNGLUNIFORM3IARBPROC Uniform3iARB = (PFNGLUNIFORM3IARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec3 uniformColor;"
+      "void main() {gl_FragColor.rgb = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[3] = {1,2,3};
+   GLint queriedUniform[4];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector 3 (ivec3), so we must set it using integer versions
+    * of the Uniform* functions.
+    */
+   (*Uniform3iARB)(uniformLocation, uniform[0], uniform[1], uniform[2]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 3, uniform, 3, queriedUniform);
+}
+
+static GLboolean
+test_Uniform3fv(generic_func func)
+{
+   PFNGLUNIFORM3FVARBPROC Uniform3fvARB = (PFNGLUNIFORM3FVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec3 uniformColor;"
+      "void main() {gl_FragColor.rgb = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[3] = {1.1,2.2,3.3};
+   GLfloat queriedUniform[3];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * vector 3 (vec3), so we must set it using float versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform3fvARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 3, uniform, 3, queriedUniform);
+}
+
+static GLboolean
+test_Uniform3f(generic_func func)
+{
+   PFNGLUNIFORM3FARBPROC Uniform3fARB = (PFNGLUNIFORM3FARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec3 uniformColor;"
+      "void main() {gl_FragColor.rgb = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[3] = {1.1,2.2,3.3};
+   GLfloat queriedUniform[3];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * vector 3 (vec3), so we must set it using float versions
+    * of the Uniform* functions.
+    */
+   (*Uniform3fARB)(uniformLocation, uniform[0], uniform[1], uniform[2]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 3, uniform, 3, queriedUniform);
+}
+
+static GLboolean
+test_Uniform4iv(generic_func func)
+{
+   PFNGLUNIFORM4IVARBPROC Uniform4ivARB = (PFNGLUNIFORM4IVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec4 uniformColor; void main() {gl_FragColor = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[4] = {1,2,3,4};
+   GLint queriedUniform[4];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector (ivec4), so we must set it using integer versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform4ivARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 4, uniform, 4, queriedUniform);
+}
+
+static GLboolean
+test_Uniform4i(generic_func func)
+{
+   PFNGLUNIFORM4IARBPROC Uniform4iARB = (PFNGLUNIFORM4IARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformivARB, PFNGLGETUNIFORMIVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform ivec4 uniformColor; void main() {gl_FragColor = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLint uniform[4] = {1,2,3,4};
+   GLint queriedUniform[4];
+
+   if (GetUniformivARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector (ivec4), so we must set it using integer versions
+    * of the Uniform* functions.
+    */
+   (*Uniform4iARB)(uniformLocation, uniform[0], uniform[1], uniform[2],
+      uniform[3]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformivARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_ints(__FUNCTION__, 4, uniform, 4, queriedUniform);
+}
+
+static GLboolean
+test_Uniform4fv(generic_func func)
+{
+   PFNGLUNIFORM4FVARBPROC Uniform4fvARB = (PFNGLUNIFORM4FVARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec4 uniformColor; void main() {gl_FragColor = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[4] = {1.1,2.2,3.3,4.4};
+   GLfloat queriedUniform[4];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is a float
+    * vector (vec4), so we must set it using float versions
+    * of the Uniform* functions.  The "1" means we're setting
+    * one vector's worth of information.
+    */
+   (*Uniform4fvARB)(uniformLocation, 1, uniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 4, uniform, 4, queriedUniform);
+}
+
+static GLboolean
+test_Uniform4f(generic_func func)
+{
+   PFNGLUNIFORM4FARBPROC Uniform4fARB = (PFNGLUNIFORM4FARBPROC) func;
+   DECLARE_GLFUNC_PTR(GetUniformfvARB, PFNGLGETUNIFORMFVARBPROC);
+
+   /* This is a trivial fragment shader that sets the color of the
+    * fragment to the uniform value passed in.
+    */
+   static const char *fragmentShaderText = 
+      "uniform vec4 uniformColor; void main() {gl_FragColor = uniformColor;}";
+   static const char *uniformName = "uniformColor";
+
+   GLhandleARB program;
+   GLint uniformLocation;
+   const GLfloat uniform[4] = {1.1,2.2,3.3,4.4};
+   GLfloat queriedUniform[4];
+
+   if (GetUniformfvARB == NULL) {
+      return GL_FALSE;
+   }
+
+   /* Call a helper function to compile up the shader and give
+    * us back the validated program and uniform location.
+    * If it fails, something's wrong and we can't continue.
+    */
+   if (!exercise_uniform_start(fragmentShaderText, uniformName, 
+      &program, &uniformLocation)) {
+      return GL_FALSE;
+   }
+
+   /* Set the value of the program uniform.  Note that you must
+    * use a compatible type.  Our uniform above is an integer
+    * vector (ivec4), so we must set it using integer versions
+    * of the Uniform* functions.
+    */
+   (*Uniform4fARB)(uniformLocation, uniform[0], uniform[1], uniform[2],
+      uniform[3]);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Query it back */
+   (*GetUniformfvARB)(program, uniformLocation, queriedUniform);
+   CheckGLError(__LINE__, __FILE__, __FUNCTION__);
+
+   /* Clean up before we check to see whether it came back unscathed */
+   exercise_uniform_end(program);
+
+   /* Now check to see whether the uniform came back as expected.  This
+    * will return GL_TRUE if all is well, or GL_FALSE if the comparison failed.
+    */
+   return compare_floats(__FUNCTION__, 4, uniform, 4, queriedUniform);
+}
 
 static GLboolean
 test_ActiveTextureARB(generic_func func)
@@ -107,6 +2694,40 @@ test_VertexAttrib1fvARB(generic_func func)
 }
 
 static GLboolean
+test_VertexAttrib1dvARB(generic_func func)
+{
+   PFNGLVERTEXATTRIB1DVARBPROC vertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) func;
+   PFNGLGETVERTEXATTRIBDVARBPROC getVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvARB");
+
+   const GLdouble v[1] = {25.0};
+   const GLdouble def[1] = {0};
+   GLdouble res[4];
+   GLboolean pass;
+   (*vertexAttrib1dvARB)(6, v);
+   (*getVertexAttribdvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
+   pass = (res[0] == 25.0 && res[1] == 0.0 && res[2] == 0.0 && res[3] == 1.0);
+   (*vertexAttrib1dvARB)(6, def);
+   return pass;
+}
+
+static GLboolean
+test_VertexAttrib1svARB(generic_func func)
+{
+   PFNGLVERTEXATTRIB1SVARBPROC vertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) func;
+   PFNGLGETVERTEXATTRIBIVARBPROC getVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivARB");
+
+   const GLshort v[1] = {25.0};
+   const GLshort def[1] = {0};
+   GLint res[4];
+   GLboolean pass;
+   (*vertexAttrib1svARB)(6, v);
+   (*getVertexAttribivARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
+   pass = (res[0] == 25 && res[1] == 0 && res[2] == 0 && res[3] == 1);
+   (*vertexAttrib1svARB)(6, def);
+   return pass;
+}
+
+static GLboolean
 test_VertexAttrib4NubvARB(generic_func func)
 {
    PFNGLVERTEXATTRIB4NUBVARBPROC vertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) func;
@@ -177,7 +2798,6 @@ test_VertexAttrib4NsvARB(generic_func func)
    return pass;
 }
 
-
 static GLboolean
 test_VertexAttrib4NusvARB(generic_func func)
 {
@@ -195,42 +2815,110 @@ test_VertexAttrib4NusvARB(generic_func func)
    return pass;
 }
 
+static GLboolean
+test_VertexAttrib1sNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB1SNVPROC vertexAttrib1sNV = (PFNGLVERTEXATTRIB1SNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 0, 0, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib1sNV)(6, v[0]);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib1sNV)(6, def[0]);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
 
 static GLboolean
-test_VertexAttrib4ubNV(generic_func func)
+test_VertexAttrib1fNV(generic_func func)
 {
-   PFNGLVERTEXATTRIB4UBNVPROC vertexAttrib4ubNV = (PFNGLVERTEXATTRIB4UBNVPROC) func;
+   PFNGLVERTEXATTRIB1FNVPROC vertexAttrib1fNV = (PFNGLVERTEXATTRIB1FNVPROC) func;
    PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
 
-   const GLubyte v[4] = {255, 0, 255, 0};
-   const GLubyte def[4] = {0, 0, 0, 255};
+   const GLfloat v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
    GLfloat res[4];
-   GLboolean pass;
-   (*vertexAttrib4ubNV)(6, v[0], v[1], v[2], v[3]);
+   (*vertexAttrib1fNV)(6, v[0]);
    (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
-   pass = (res[0] == 1.0 && res[1] == 0.0 && res[2] == 1.0 && res[3] == 0.0);
-   (*vertexAttrib4ubNV)(6, def[0], def[1], def[2], def[3]);
-   return pass;
+   (*vertexAttrib1fNV)(6, def[0]);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
 }
 
+static GLboolean
+test_VertexAttrib1dNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB1DNVPROC vertexAttrib1dNV = (PFNGLVERTEXATTRIB1DNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib1dNV)(6, v[0]);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib1dNV)(6, def[0]);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
 
 static GLboolean
 test_VertexAttrib2sNV(generic_func func)
 {
    PFNGLVERTEXATTRIB2SNVPROC vertexAttrib2sNV = (PFNGLVERTEXATTRIB2SNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 0, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib2sNV)(6, v[0], v[1]);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib2sNV)(6, def[0], def[1]);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib2fNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB2FNVPROC vertexAttrib2fNV = (PFNGLVERTEXATTRIB2FNVPROC) func;
    PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
 
-   const GLshort v[2] = {2, -4,};
-   const GLshort def[2] = {0, 0};
+   const GLfloat v[4] = {2.5, 4.25, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
    GLfloat res[4];
-   GLboolean pass;
-   (*vertexAttrib2sNV)(6, v[0], v[1]);
+   (*vertexAttrib2fNV)(6, v[0], v[1]);
    (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
-   pass = (EQUAL(res[0], 2) && EQUAL(res[1], -4) && EQUAL(res[2], 0) && res[3] == 1.0);
-   (*vertexAttrib2sNV)(6, def[0], def[1]);
-   return pass;
+   (*vertexAttrib2fNV)(6, def[0], def[1]);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib2dNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB2DNVPROC vertexAttrib2dNV = (PFNGLVERTEXATTRIB2DNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 0.0, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib2dNV)(6, v[0], v[1]);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib2dNV)(6, def[0], def[1]);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
 }
 
+static GLboolean
+test_VertexAttrib3sNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB3SNVPROC vertexAttrib3sNV = (PFNGLVERTEXATTRIB3SNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib3sNV)(6, v[0], v[1], v[2]);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib3sNV)(6, def[0], def[1], def[2]);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
 
 static GLboolean
 test_VertexAttrib3fNV(generic_func func)
@@ -238,35 +2926,467 @@ test_VertexAttrib3fNV(generic_func func)
    PFNGLVERTEXATTRIB3FNVPROC vertexAttrib3fNV = (PFNGLVERTEXATTRIB3FNVPROC) func;
    PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
 
-   const GLfloat v[3] = {0.2, 0.4, 0.8};
-   const GLfloat def[3] = {0, 0, 0};
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
    GLfloat res[4];
-   GLboolean pass;
    (*vertexAttrib3fNV)(6, v[0], v[1], v[2]);
    (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
-   pass = (EQUAL(res[0], 0.2) && EQUAL(res[1], 0.4) && EQUAL(res[2], 0.8) && res[3] == 1.0);
    (*vertexAttrib3fNV)(6, def[0], def[1], def[2]);
-   return pass;
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib3dNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB3DNVPROC vertexAttrib3dNV = (PFNGLVERTEXATTRIB3DNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib3dNV)(6, v[0], v[1], v[2]);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib3dNV)(6, def[0], def[1], def[2]);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4sNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4SNVPROC vertexAttrib4sNV = (PFNGLVERTEXATTRIB4SNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 5};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib4sNV)(6, v[0], v[1], v[2], v[3]);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4sNV)(6, def[0], def[1], def[2], def[3]);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4fNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4FNVPROC vertexAttrib4fNV = (PFNGLVERTEXATTRIB4FNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttrib4fNV)(6, v[0], v[1], v[2], v[3]);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4fNV)(6, def[0], def[1], def[2], def[3]);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4dNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4DNVPROC vertexAttrib4dNV = (PFNGLVERTEXATTRIB4DNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib4dNV)(6, v[0], v[1], v[2], v[3]);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4dNV)(6, def[0], def[1], def[2], def[3]);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4ubNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4UBNVPROC vertexAttrib4ubNV = (PFNGLVERTEXATTRIB4UBNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLubyte v[4] = {255, 0, 255, 0};
+   const GLubyte def[4] = {0, 0, 0, 255};
+   GLfloat res[4];
+   /* There's no byte-value query; so we use the float-value query.
+    * Bytes are interpreted as steps between 0 and 1, so the
+    * expected float values will be 0.0 for byte value 0 and 1.0 for
+    * byte value 255.
+    */
+   GLfloat expectedResults[4] = {1.0, 0.0, 1.0, 0.0};
+   (*vertexAttrib4ubNV)(6, v[0], v[1], v[2], v[3]);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4ubNV)(6, def[0], def[1], def[2], def[3]);
+   return compare_floats(__FUNCTION__, 4, expectedResults, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib1fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB1FVNVPROC vertexAttrib1fvNV = (PFNGLVERTEXATTRIB1FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttrib1fvNV)(6, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib1fvNV)(6, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib1dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB1DVNVPROC vertexAttrib1dvNV = (PFNGLVERTEXATTRIB1DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib1dvNV)(6, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib1dvNV)(6, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib2svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB2SVNVPROC vertexAttrib2svNV = (PFNGLVERTEXATTRIB2SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 0, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib2svNV)(6, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib2svNV)(6, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib2fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB2FVNVPROC vertexAttrib2fvNV = (PFNGLVERTEXATTRIB2FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttrib2fvNV)(6, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib2fvNV)(6, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib2dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB2DVNVPROC vertexAttrib2dvNV = (PFNGLVERTEXATTRIB2DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 0.0, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib2dvNV)(6, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib2dvNV)(6, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib3svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB3SVNVPROC vertexAttrib3svNV = (PFNGLVERTEXATTRIB3SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib3svNV)(6, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib3svNV)(6, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib3fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB3FVNVPROC vertexAttrib3fvNV = (PFNGLVERTEXATTRIB3FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttrib3fvNV)(6, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib3fvNV)(6, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib3dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB3DVNVPROC vertexAttrib3dvNV = (PFNGLVERTEXATTRIB3DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib3dvNV)(6, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib3dvNV)(6, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4SVNVPROC vertexAttrib4svNV = (PFNGLVERTEXATTRIB4SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 5};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttrib4svNV)(6, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4svNV)(6, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
 }
 
+static GLboolean
+test_VertexAttrib4fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4FVNVPROC vertexAttrib4fvNV = (PFNGLVERTEXATTRIB4FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttrib4fvNV)(6, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4fvNV)(6, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
 
 static GLboolean
 test_VertexAttrib4dvNV(generic_func func)
 {
    PFNGLVERTEXATTRIB4DVNVPROC vertexAttrib4dvNV = (PFNGLVERTEXATTRIB4DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttrib4dvNV)(6, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4dvNV)(6, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttrib4ubvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIB4UBVNVPROC vertexAttrib4ubvNV = (PFNGLVERTEXATTRIB4UBVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLubyte v[4] = {255, 0, 255, 0};
+   const GLubyte def[4] = {0, 0, 0, 255};
+   GLfloat res[4];
+   /* There's no byte-value query; so we use the float-value query.
+    * Bytes are interpreted as steps between 0 and 1, so the
+    * expected float values will be 0.0 for byte value 0 and 1.0 for
+    * byte value 255.
+    */
+   GLfloat expectedResults[4] = {1.0, 0.0, 1.0, 0.0};
+   (*vertexAttrib4ubvNV)(6, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttrib4ubvNV)(6, def);
+   return compare_floats(__FUNCTION__, 4, expectedResults, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs1fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS1FVNVPROC vertexAttribs1fvNV = (PFNGLVERTEXATTRIBS1FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttribs1fvNV)(6, 1, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs1fvNV)(6, 1, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs1dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS1DVNVPROC vertexAttribs1dvNV = (PFNGLVERTEXATTRIBS1DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 0.0, 0.0, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttribs1dvNV)(6, 1, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs1dvNV)(6, 1, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs2svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS2SVNVPROC vertexAttribs2svNV = (PFNGLVERTEXATTRIBS2SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 0, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttribs2svNV)(6, 1, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs2svNV)(6, 1, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs2fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS2FVNVPROC vertexAttribs2fvNV = (PFNGLVERTEXATTRIBS2FVNVPROC) func;
    PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
 
-   const GLdouble v[4] = {0.2, 0.4, 0.8, 1.2};
+   const GLfloat v[4] = {2.5, 4.25, 0.0, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttribs2fvNV)(6, 1, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs2fvNV)(6, 1, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs2dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS2DVNVPROC vertexAttribs2dvNV = (PFNGLVERTEXATTRIBS2DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 0.0, 1.0};
    const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttribs2dvNV)(6, 1, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs2dvNV)(6, 1, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs3svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS3SVNVPROC vertexAttribs3svNV = (PFNGLVERTEXATTRIBS3SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 1};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttribs3svNV)(6, 1, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs3svNV)(6, 1, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs3fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS3FVNVPROC vertexAttribs3fvNV = (PFNGLVERTEXATTRIBS3FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLfloat def[4] = {0, 0, 0, 1};
    GLfloat res[4];
-   GLboolean pass;
-   (*vertexAttrib4dvNV)(6, v);
+   (*vertexAttribs3fvNV)(6, 1, v);
    (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
-   pass = (EQUAL(res[0], 0.2) && EQUAL(res[1], 0.4) && EQUAL(res[2], 0.8) && EQUAL(res[3], 1.2));
-   (*vertexAttrib4dvNV)(6, def);
-   return pass;
+   (*vertexAttribs3fvNV)(6, 1, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs3dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS3DVNVPROC vertexAttribs3dvNV = (PFNGLVERTEXATTRIBS3DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 1.0};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttribs3dvNV)(6, 1, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs3dvNV)(6, 1, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs4svNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS4SVNVPROC vertexAttribs4svNV = (PFNGLVERTEXATTRIBS4SVNVPROC) func;
+   PFNGLGETVERTEXATTRIBIVNVPROC getVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribivNV");
+
+   const GLshort v[4] = {2, 4, 7, 5};
+   const GLshort def[4] = {0, 0, 0, 1};
+   GLint res[4];
+   (*vertexAttribs4svNV)(6, 1, v);
+   (*getVertexAttribivNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs4svNV)(6, 1, def);
+   return compare_shorts_to_ints(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs4fvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS4FVNVPROC vertexAttribs4fvNV = (PFNGLVERTEXATTRIBS4FVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLfloat v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLfloat def[4] = {0, 0, 0, 1};
+   GLfloat res[4];
+   (*vertexAttribs4fvNV)(6, 1, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs4fvNV)(6, 1, def);
+   return compare_floats(__FUNCTION__, 4, v, 4, res);
+}
+
+static GLboolean
+test_VertexAttribs4dvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS4DVNVPROC vertexAttribs4dvNV = (PFNGLVERTEXATTRIBS4DVNVPROC) func;
+   PFNGLGETVERTEXATTRIBDVNVPROC getVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribdvNV");
+
+   const GLdouble v[4] = {2.5, 4.25, 7.125, 5.0625};
+   const GLdouble def[4] = {0, 0, 0, 1};
+   GLdouble res[4];
+   (*vertexAttribs4dvNV)(6, 1, v);
+   (*getVertexAttribdvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs4dvNV)(6, 1, def);
+   return compare_doubles(__FUNCTION__, 4, v, 4, res);
 }
 
+static GLboolean
+test_VertexAttribs4ubvNV(generic_func func)
+{
+   PFNGLVERTEXATTRIBS4UBVNVPROC vertexAttribs4ubvNV = (PFNGLVERTEXATTRIBS4UBVNVPROC) func;
+   PFNGLGETVERTEXATTRIBFVNVPROC getVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvNV");
+
+   const GLubyte v[4] = {255, 0, 255, 0};
+   const GLubyte def[4] = {0, 0, 0, 255};
+   GLfloat res[4];
+   /* There's no byte-value query; so we use the float-value query.
+    * Bytes are interpreted as steps between 0 and 1, so the
+    * expected float values will be 0.0 for byte value 0 and 1.0 for
+    * byte value 255.
+    */
+   GLfloat expectedResults[4] = {1.0, 0.0, 1.0, 0.0};
+   (*vertexAttribs4ubvNV)(6, 1, v);
+   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
+   (*vertexAttribs4ubvNV)(6, 1, def);
+   return compare_floats(__FUNCTION__, 4, expectedResults, 4, res);
+}
 
 static GLboolean
 test_StencilFuncSeparateATI(generic_func func)
@@ -391,13 +3511,25 @@ static void
 check_functions( const char *extensions )
 {
    struct name_test_pair *entry;
-   int failures = 0, passes = 0;
-   int totalFail = 0, totalPass = 0;
+   int failures = 0, passes = 0, untested = 0;
+   int totalFail = 0, totalPass = 0, totalUntested = 0, totalUnsupported = 0;
    int doTests;
-
+   const char *version = (const char *) glGetString(GL_VERSION);
+
+   /* The functions list will have "real" entries (consisting of
+    * a GL function name and a pointer to an exercise function for
+    * that GL function), and "group" entries (indicated as
+    * such by having a "-" as the first character of the name).
+    * "Group" names always start with the "-" character, and can
+    * be numeric (e.g. "-1.0", "-2.1"), indicating that a particular
+    * OpenGL version is required for the following functions; or can be
+    * an extension name (e.g. "-GL_ARB_multitexture") that means
+    * that the named extension is required for the following functions.
+    */
    for (entry = functions; entry->name; entry++) {
+      /* Check if this is a group indicator */
       if (entry->name[0] == '-') {
-         const char *version = (const char *) glGetString(GL_VERSION);
+         /* A group indicator; check if it's an OpenGL version group */
          if (entry->name[1] == '1') {
             /* check GL version 1.x */
             if (version[0] == '1' &&
@@ -419,14 +3551,27 @@ check_functions( const char *extensions )
             /* check if the named extension is available */
             doTests = extension_supported(extensions, entry->name+1);
          }
+
+         /* doTests is now set if we're starting an OpenGL version
+          * group, and the running OpenGL version is at least the
+          * version required; or if we're starting an OpenGL extension
+          * group, and the extension is supported.
+          */
          if (doTests)
             printf("Testing %s functions\n", entry->name + 1);
-         totalFail += failures;
-         totalPass += passes;
+
+         /* Each time we hit a title function, reset the function
+          * counts.
+          */
          failures = 0;
          passes = 0;
+         untested = 0;
       }
       else if (doTests) {
+         /* Here, we know we're trying to exercise a function for
+          * a supported extension.  See whether we have a test for
+          * it, and try to run it.
+          */
          generic_func funcPtr = (generic_func) glXGetProcAddressARB((const GLubyte *) entry->name);
          if (funcPtr) {
             if (entry->test) {
@@ -436,21 +3581,36 @@ check_functions( const char *extensions )
                if (b) {
                   printf(" Pass\n");
                   passes++;
+                  totalPass++;
                }
                else {
                   printf(" FAIL!!!\n");
                   failures++;
+                  totalFail++;
                }
             }
             else {
-               passes++;
+               untested++;
+               totalUntested++;
             }
          }
          else {
             printf("   glXGetProcAddress(%s) failed!\n", entry->name);
             failures++;
+            totalFail++;
          }
       }
+      else {
+         /* Here, we have a function that belongs to a group that
+          * is known to be unsupported.
+          */
+         totalUnsupported++;
+      }
+
+      /* Make sure a poor test case doesn't leave any lingering
+       * OpenGL errors.
+       */
+      CheckGLError(__LINE__, __FILE__, __FUNCTION__);
 
       if (doTests && (!(entry+1)->name || (entry+1)->name[0] == '-')) {
          if (failures > 0) {
@@ -459,13 +3619,16 @@ check_functions( const char *extensions )
          if (passes > 0) {
             printf("   %d passed.\n", passes);
          }
+         if (untested > 0) {
+            printf("   %d untested.\n", untested);
+         }
       }
    }
-   totalFail += failures;
-   totalPass += passes;
 
    printf("-----------------------------\n");
-   printf("Total: %d pass  %d fail\n", totalPass, totalFail);
+   printf("Total: %d pass  %d fail  %d untested  %d unsupported  %d total\n", 
+      totalPass, totalFail, totalUntested, totalUnsupported,
+      totalPass + totalFail + totalUntested + totalUnsupported);
 }
 
 
index 8adfc51..699195b 100644 (file)
@@ -52,7 +52,7 @@ static struct name_test_pair functions[] = {"""
                prev_category = None
                
 
-               for f in api.functionIterateByOffset():
+               for f in api.functionIterateByCategory():
                        [category, num] = api.get_category_for_name( f.name )
                        if category != prev_category:
                                print '   { "-%s", NULL},' % category