/*
 * Copyright (C) 1999-2002  Brian Paul   All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * Test that glXGetProcAddress works.
 */

#define GLX_GLXEXT_PROTOTYPES

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


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 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)
{
   PFNGLACTIVETEXTUREARBPROC activeTexture = (PFNGLACTIVETEXTUREARBPROC) func;
   GLint t;
   GLboolean pass;
   (*activeTexture)(GL_TEXTURE1_ARB);
   glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &t);
   pass = (t == GL_TEXTURE1_ARB);
   (*activeTexture)(GL_TEXTURE0_ARB);  /* restore default */
   return pass;
}


static GLboolean
test_SecondaryColor3fEXT(generic_func func)
{
   PFNGLSECONDARYCOLOR3FEXTPROC secColor3f = (PFNGLSECONDARYCOLOR3FEXTPROC) func;
   GLfloat color[4];
   GLboolean pass;
   (*secColor3f)(1.0, 1.0, 0.0);
   glGetFloatv(GL_CURRENT_SECONDARY_COLOR_EXT, color);
   pass = (color[0] == 1.0 && color[1] == 1.0 && color[2] == 0.0);
   (*secColor3f)(0.0, 0.0, 0.0);  /* restore default */
   return pass;
}


static GLboolean
test_ActiveStencilFaceEXT(generic_func func)
{
   PFNGLACTIVESTENCILFACEEXTPROC activeFace = (PFNGLACTIVESTENCILFACEEXTPROC) func;
   GLint face;
   GLboolean pass;
   (*activeFace)(GL_BACK);
   glGetIntegerv(GL_ACTIVE_STENCIL_FACE_EXT, &face);
   pass = (face == GL_BACK);
   (*activeFace)(GL_FRONT);  /* restore default */
   return pass;
}


static GLboolean
test_VertexAttrib1fvARB(generic_func func)
{
   PFNGLVERTEXATTRIB1FVARBPROC vertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) func;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLfloat v[1] = {25.0};
   const GLfloat def[1] = {0};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib1fvARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (res[0] == 25.0 && res[1] == 0.0 && res[2] == 0.0 && res[3] == 1.0);
   (*vertexAttrib1fvARB)(6, def);
   return pass;
}

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;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLubyte v[4] = {255, 0, 255, 0};
   const GLubyte def[4] = {0, 0, 0, 255};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib4NubvARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (res[0] == 1.0 && res[1] == 0.0 && res[2] == 1.0 && res[3] == 0.0);
   (*vertexAttrib4NubvARB)(6, def);
   return pass;
}


static GLboolean
test_VertexAttrib4NuivARB(generic_func func)
{
   PFNGLVERTEXATTRIB4NUIVARBPROC vertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) func;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLuint v[4] = {0xffffffff, 0, 0xffffffff, 0};
   const GLuint def[4] = {0, 0, 0, 0xffffffff};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib4NuivARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (EQUAL(res[0], 1.0) && EQUAL(res[1], 0.0) && EQUAL(res[2], 1.0) && EQUAL(res[3], 0.0));
   (*vertexAttrib4NuivARB)(6, def);
   return pass;
}


static GLboolean
test_VertexAttrib4ivARB(generic_func func)
{
   PFNGLVERTEXATTRIB4IVARBPROC vertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) func;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLint v[4] = {1, 2, -3, 4};
   const GLint def[4] = {0, 0, 0, 1};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib4ivARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (EQUAL(res[0], 1.0) && EQUAL(res[1], 2.0) && EQUAL(res[2], -3.0) && EQUAL(res[3], 4.0));
   (*vertexAttrib4ivARB)(6, def);
   return pass;
}


static GLboolean
test_VertexAttrib4NsvARB(generic_func func)
{
   PFNGLVERTEXATTRIB4NSVARBPROC vertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) func;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLshort v[4] = {0, 32767, 32767, 0};
   const GLshort def[4] = {0, 0, 0, 32767};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib4NsvARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (EQUAL(res[0], 0.0) && EQUAL(res[1], 1.0) && EQUAL(res[2], 1.0) && EQUAL(res[3], 0.0));
   (*vertexAttrib4NsvARB)(6, def);
   return pass;
}

static GLboolean
test_VertexAttrib4NusvARB(generic_func func)
{
   PFNGLVERTEXATTRIB4NUSVARBPROC vertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) func;
   PFNGLGETVERTEXATTRIBFVARBPROC getVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) glXGetProcAddressARB((const GLubyte *) "glGetVertexAttribfvARB");

   const GLushort v[4] = {0xffff, 0, 0xffff, 0};
   const GLushort def[4] = {0, 0, 0, 0xffff};
   GLfloat res[4];
   GLboolean pass;
   (*vertexAttrib4NusvARB)(6, v);
   (*getVertexAttribfvARB)(6, GL_CURRENT_VERTEX_ATTRIB_ARB, res);
   pass = (EQUAL(res[0], 1.0) && EQUAL(res[1], 0.0) && EQUAL(res[2], 1.0) && EQUAL(res[3], 0.0));
   (*vertexAttrib4NusvARB)(6, def);
   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_VertexAttrib1fNV(generic_func func)
{
   PFNGLVERTEXATTRIB1FNVPROC vertexAttrib1fNV = (PFNGLVERTEXATTRIB1FNVPROC) 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];
   (*vertexAttrib1fNV)(6, v[0]);
   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
   (*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 GLfloat v[4] = {2.5, 4.25, 0.0, 1.0};
   const GLfloat def[4] = {0, 0, 0, 1};
   GLfloat res[4];
   (*vertexAttrib2fNV)(6, v[0], v[1]);
   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
   (*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)
{
   PFNGLVERTEXATTRIB3FNVPROC vertexAttrib3fNV = (PFNGLVERTEXATTRIB3FNVPROC) 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];
   (*vertexAttrib3fNV)(6, v[0], v[1], v[2]);
   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
   (*vertexAttrib3fNV)(6, def[0], def[1], def[2]);
   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 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];
   (*vertexAttribs3fvNV)(6, 1, v);
   (*getVertexAttribfvNV)(6, GL_CURRENT_ATTRIB_NV, res);
   (*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)
{
#ifdef GL_ATI_separate_stencil
   PFNGLSTENCILFUNCSEPARATEATIPROC stencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC) func;
   GLint frontFunc, backFunc;
   GLint frontRef, backRef;
   GLint frontMask, backMask;
   (*stencilFuncSeparateATI)(GL_LESS, GL_GREATER, 2, 0xa);
   glGetIntegerv(GL_STENCIL_FUNC, &frontFunc);
   glGetIntegerv(GL_STENCIL_BACK_FUNC, &backFunc);
   glGetIntegerv(GL_STENCIL_REF, &frontRef);
   glGetIntegerv(GL_STENCIL_BACK_REF, &backRef);
   glGetIntegerv(GL_STENCIL_VALUE_MASK, &frontMask);
   glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &backMask);
   if (frontFunc != GL_LESS ||
       backFunc != GL_GREATER ||
       frontRef != 2 ||
       backRef != 2 ||
       frontMask != 0xa ||
       backMask != 0xa)
      return GL_FALSE;
#endif
   return GL_TRUE;
}

static GLboolean
test_StencilFuncSeparate(generic_func func)
{
#ifdef GL_VERSION_2_0
   PFNGLSTENCILFUNCSEPARATEPROC stencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) func;
   GLint frontFunc, backFunc;
   GLint frontRef, backRef;
   GLint frontMask, backMask;
   (*stencilFuncSeparate)(GL_BACK, GL_GREATER, 2, 0xa);
   glGetIntegerv(GL_STENCIL_FUNC, &frontFunc);
   glGetIntegerv(GL_STENCIL_BACK_FUNC, &backFunc);
   glGetIntegerv(GL_STENCIL_REF, &frontRef);
   glGetIntegerv(GL_STENCIL_BACK_REF, &backRef);
   glGetIntegerv(GL_STENCIL_VALUE_MASK, &frontMask);
   glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &backMask);
   if (frontFunc != GL_ALWAYS ||
       backFunc != GL_GREATER ||
       frontRef != 0 ||
       backRef != 2 ||
       frontMask == 0xa || /* might be 0xff or ~0 */
       backMask != 0xa)
      return GL_FALSE;
#endif
   return GL_TRUE;
}

static GLboolean
test_StencilOpSeparate(generic_func func)
{
#ifdef GL_VERSION_2_0
   PFNGLSTENCILOPSEPARATEPROC stencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) func;
   GLint frontFail, backFail;
   GLint frontZFail, backZFail;
   GLint frontZPass, backZPass;
   (*stencilOpSeparate)(GL_BACK, GL_INCR, GL_DECR, GL_INVERT);
   glGetIntegerv(GL_STENCIL_FAIL, &frontFail);
   glGetIntegerv(GL_STENCIL_BACK_FAIL, &backFail);
   glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &frontZFail);
   glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &backZFail);
   glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &frontZPass);
   glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &backZPass);
   if (frontFail != GL_KEEP ||
       backFail != GL_INCR ||
       frontZFail != GL_KEEP ||
       backZFail != GL_DECR ||
       frontZPass != GL_KEEP ||
       backZPass != GL_INVERT)
      return GL_FALSE;
#endif
   return GL_TRUE;
}

static GLboolean
test_StencilMaskSeparate(generic_func func)
{
#ifdef GL_VERSION_2_0
   PFNGLSTENCILMASKSEPARATEPROC stencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) func;
   GLint frontMask, backMask;
   (*stencilMaskSeparate)(GL_BACK, 0x1b);
   glGetIntegerv(GL_STENCIL_WRITEMASK, &frontMask);
   glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &backMask);
   if (frontMask == 0x1b ||
       backMask != 0x1b)
      return GL_FALSE;
#endif
   return GL_TRUE;
}


/*
 * The following file is auto-generated with Python.
 */
#include "getproclist.h"



static int
extension_supported(const char *haystack, const char *needle)
{
   const char *p = strstr(haystack, needle);
   if (p) {
      /* found string, make sure next char is space or zero */
      const int len = strlen(needle);
      if (p[len] == ' ' || p[len] == 0)
         return 1;
      else
         return 0;
   }
   else
      return 0;
}


/* Run all the known extension function tests, if the extension is supported.
 * Return a count of how many failed.
 */
static int
check_functions( const char *extensions )
{
   struct name_test_pair *entry;
   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] == '-') {
         /* 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' &&
                version[1] == '.' &&
                version[2] >= entry->name[3])
               doTests = 1;
            else
               doTests = 0;
         }
         else if (entry->name[1] == '2') {
            if (version[0] == '2' &&
                version[1] == '.' &&
                version[2] >= entry->name[3])
               doTests = 1;
            else
               doTests = 0;
         }
         else {
            /* 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);

         /* 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) {
               GLboolean b;
               printf("   Validating %s:", entry->name);
               b = (*entry->test)(funcPtr);
               if (b) {
                  printf(" Pass\n");
                  passes++;
                  totalPass++;
               }
               else {
                  printf(" FAIL!!!\n");
                  failures++;
                  totalFail++;
               }
            }
            else {
               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) {
            printf("   %d failed.\n", failures);
         }
         if (passes > 0) {
            printf("   %d passed.\n", passes);
         }
         if (untested > 0) {
            printf("   %d untested.\n", untested);
         }
      }
   }

   printf("-----------------------------\n");
   printf("Total: %d pass  %d fail  %d untested  %d unsupported  %d total\n", 
      totalPass, totalFail, totalUntested, totalUnsupported,
      totalPass + totalFail + totalUntested + totalUnsupported);

   return totalFail;
}


/* Return an error code */
#define ERROR_NONE 0
#define ERROR_NO_VISUAL 1
#define ERROR_NO_CONTEXT 2
#define ERROR_NO_MAKECURRENT 3
#define ERROR_FAILED 4

static int
print_screen_info(Display *dpy, int scrnum, Bool allowDirect)
{
   Window win;
   int attribSingle[] = {
      GLX_RGBA,
      GLX_RED_SIZE, 1,
      GLX_GREEN_SIZE, 1,
      GLX_BLUE_SIZE, 1,
      GLX_STENCIL_SIZE, 1,
      None };
   int attribDouble[] = {
      GLX_RGBA,
      GLX_RED_SIZE, 1,
      GLX_GREEN_SIZE, 1,
      GLX_BLUE_SIZE, 1,
      GLX_STENCIL_SIZE, 1,
      GLX_DOUBLEBUFFER,
      None };

   XSetWindowAttributes attr;
   unsigned long mask;
   Window root;
   GLXContext ctx;
   XVisualInfo *visinfo;
   int width = 100, height = 100;
   int failures;

   root = RootWindow(dpy, scrnum);

   visinfo = glXChooseVisual(dpy, scrnum, attribSingle);
   if (!visinfo) {
      visinfo = glXChooseVisual(dpy, scrnum, attribDouble);
      if (!visinfo) {
         fprintf(stderr, "Error: couldn't find RGB GLX visual\n");
         return ERROR_NO_VISUAL;
      }
   }

   attr.background_pixel = 0;
   attr.border_pixel = 0;
   attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
   attr.event_mask = StructureNotifyMask | ExposureMask;
   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
   win = XCreateWindow(dpy, root, 0, 0, width, height,
		       0, visinfo->depth, InputOutput,
		       visinfo->visual, mask, &attr);

   ctx = glXCreateContext( dpy, visinfo, NULL, allowDirect );
   if (!ctx) {
      fprintf(stderr, "Error: glXCreateContext failed\n");
      XDestroyWindow(dpy, win);
      return ERROR_NO_CONTEXT;
   }

   if (!glXMakeCurrent(dpy, win, ctx)) {
      fprintf(stderr, "Error: glXMakeCurrent failed\n");
      glXDestroyContext(dpy, ctx);
      XDestroyWindow(dpy, win);
      return ERROR_NO_MAKECURRENT;
   }

   failures = check_functions( (const char *) glGetString(GL_EXTENSIONS) );
   glXDestroyContext(dpy, ctx);
   XDestroyWindow(dpy, win);

   return (failures == 0 ? ERROR_NONE : ERROR_FAILED);
}

int
main(int argc, char *argv[])
{
   char *displayName = NULL;
   Display *dpy;
   int returnCode;

   dpy = XOpenDisplay(displayName);
   if (!dpy) {
      fprintf(stderr, "Error: unable to open display %s\n", displayName);
      return -1;
   }

   returnCode = print_screen_info(dpy, 0, GL_TRUE);

   XCloseDisplay(dpy);

   return returnCode;
}