Move end2end and standalone tests to gl_tests and egl_tests

Also introduce a test_utils directory that contains helpers used for all
types of tests.

BUG=angleproject:892

Change-Id: I9e1bff895020ffd3a109162283971a290a1098bd
Reviewed-on: https://chromium-review.googlesource.com/270198
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
new file mode 100644
index 0000000..dbd09d7
--- /dev/null
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -0,0 +1,616 @@
+//
+// Copyright 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "test_utils/ANGLETest.h"
+
+using namespace angle;
+
+namespace
+{
+
+class TextureTest : public ANGLETest
+{
+  protected:
+    TextureTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+
+    void SetUp() override
+    {
+        ANGLETest::SetUp();
+        glGenTextures(1, &mTexture2D);
+        glGenTextures(1, &mTextureCube);
+
+        glBindTexture(GL_TEXTURE_2D, mTexture2D);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+        EXPECT_GL_NO_ERROR();
+
+        glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
+        glTexStorage2DEXT(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, 1, 1);
+        EXPECT_GL_NO_ERROR();
+
+        ASSERT_GL_NO_ERROR();
+
+        const std::string vertexShaderSource = SHADER_SOURCE
+        (
+            precision highp float;
+            attribute vec4 position;
+            varying vec2 texcoord;
+
+            uniform vec2 textureScale;
+
+            void main()
+            {
+                gl_Position = vec4(position.xy * textureScale, 0.0, 1.0);
+                texcoord = (position.xy * 0.5) + 0.5;
+            }
+        );
+
+        const std::string fragmentShaderSource2D = SHADER_SOURCE
+        (
+            precision highp float;
+            uniform sampler2D tex;
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_FragColor = texture2D(tex, texcoord);
+            }
+        );
+
+        const std::string fragmentShaderSourceCube = SHADER_SOURCE
+        (
+            precision highp float;
+            uniform sampler2D tex2D;
+            uniform samplerCube texCube;
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_FragColor = texture2D(tex2D, texcoord);
+                gl_FragColor += textureCube(texCube, vec3(texcoord, 0));
+            }
+        );
+
+        m2DProgram = CompileProgram(vertexShaderSource, fragmentShaderSource2D);
+        mCubeProgram = CompileProgram(vertexShaderSource, fragmentShaderSourceCube);
+        if (m2DProgram == 0 || mCubeProgram == 0)
+        {
+            FAIL() << "shader compilation failed.";
+        }
+
+        mTexture2DUniformLocation = glGetUniformLocation(m2DProgram, "tex");
+        ASSERT_NE(-1, mTexture2DUniformLocation);
+
+        mTextureScaleUniformLocation = glGetUniformLocation(m2DProgram, "textureScale");
+        ASSERT_NE(-1, mTextureScaleUniformLocation);
+
+        glUseProgram(m2DProgram);
+        glUniform2f(mTextureScaleUniformLocation, 1.0f, 1.0f);
+        glUseProgram(0);
+        ASSERT_GL_NO_ERROR();
+    }
+
+    void TearDown() override
+    {
+        glDeleteTextures(1, &mTexture2D);
+        glDeleteTextures(1, &mTextureCube);
+        glDeleteProgram(m2DProgram);
+        glDeleteProgram(mCubeProgram);
+
+        ANGLETest::TearDown();
+    }
+
+    // Tests CopyTexSubImage with floating point textures of various formats.
+    void testFloatCopySubImage(int sourceImageChannels, int destImageChannels)
+    {
+        // TODO(jmadill): Figure out why this is broken on Intel D3D11
+        if (isIntel() && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
+        {
+            std::cout << "Test skipped on Intel D3D11." << std::endl;
+            return;
+        }
+
+        if (getClientVersion() < 3)
+        {
+            if (!extensionEnabled("GL_OES_texture_float"))
+            {
+                std::cout << "Test skipped due to missing GL_OES_texture_float." << std::endl;
+                return;
+            }
+
+            if ((sourceImageChannels < 3 || destImageChannels < 3) && !extensionEnabled("GL_EXT_texture_rg"))
+            {
+                std::cout << "Test skipped due to missing GL_EXT_texture_rg." << std::endl;
+                return;
+            }
+        }
+
+        GLfloat sourceImageData[4][16] =
+        {
+            { // R
+                1.0f,
+                0.0f,
+                0.0f,
+                1.0f
+            },
+            { // RG
+                1.0f, 0.0f,
+                0.0f, 1.0f,
+                0.0f, 0.0f,
+                1.0f, 1.0f
+            },
+            { // RGB
+                1.0f, 0.0f, 0.0f,
+                0.0f, 1.0f, 0.0f,
+                0.0f, 0.0f, 1.0f,
+                1.0f, 1.0f, 0.0f
+            },
+            { // RGBA
+                1.0f, 0.0f, 0.0f, 1.0f,
+                0.0f, 1.0f, 0.0f, 1.0f,
+                0.0f, 0.0f, 1.0f, 1.0f,
+                1.0f, 1.0f, 0.0f, 1.0f
+            },
+        };
+
+        GLenum imageFormats[] =
+        {
+            GL_R32F,
+            GL_RG32F,
+            GL_RGB32F,
+            GL_RGBA32F,
+        };
+
+        GLenum sourceUnsizedFormats[] =
+        {
+            GL_RED,
+            GL_RG,
+            GL_RGB,
+            GL_RGBA,
+        };
+
+        GLuint textures[2];
+
+        glGenTextures(2, textures);
+
+        GLfloat *imageData = sourceImageData[sourceImageChannels - 1];
+        GLenum sourceImageFormat = imageFormats[sourceImageChannels - 1];
+        GLenum sourceUnsizedFormat = sourceUnsizedFormats[sourceImageChannels - 1];
+        GLenum destImageFormat = imageFormats[destImageChannels - 1];
+
+        glBindTexture(GL_TEXTURE_2D, textures[0]);
+        glTexStorage2DEXT(GL_TEXTURE_2D, 1, sourceImageFormat, 2, 2);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, sourceUnsizedFormat, GL_FLOAT, imageData);
+
+        if (sourceImageChannels < 3 && !extensionEnabled("GL_EXT_texture_rg"))
+        {
+            // This is not supported
+            ASSERT_GL_ERROR(GL_INVALID_OPERATION);
+        }
+        else
+        {
+            ASSERT_GL_NO_ERROR();
+        }
+
+        GLuint fbo;
+        glGenFramebuffers(1, &fbo);
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
+
+        glBindTexture(GL_TEXTURE_2D, textures[1]);
+        glTexStorage2DEXT(GL_TEXTURE_2D, 1, destImageFormat, 2, 2);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 2, 2);
+        ASSERT_GL_NO_ERROR();
+
+        glBindFramebuffer(GL_FRAMEBUFFER, 0);
+        drawQuad(m2DProgram, "position", 0.5f);
+        swapBuffers();
+
+        int testImageChannels = std::min(sourceImageChannels, destImageChannels);
+
+        EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
+        if (testImageChannels > 1)
+        {
+            EXPECT_PIXEL_EQ(getWindowHeight() - 1, 0, 0, 255, 0, 255);
+            EXPECT_PIXEL_EQ(getWindowHeight() - 1, getWindowWidth() - 1, 255, 255, 0, 255);
+            if (testImageChannels > 2)
+            {
+                EXPECT_PIXEL_EQ(0, getWindowWidth() - 1, 0, 0, 255, 255);
+            }
+        }
+
+        glDeleteFramebuffers(1, &fbo);
+        glDeleteTextures(2, textures);
+
+        ASSERT_GL_NO_ERROR();
+    }
+
+    GLuint mTexture2D;
+    GLuint mTextureCube;
+
+    GLuint m2DProgram;
+    GLuint mCubeProgram;
+    GLint mTexture2DUniformLocation;
+    GLint mTextureScaleUniformLocation;
+};
+
+TEST_P(TextureTest, NegativeAPISubImage)
+{
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+    EXPECT_GL_ERROR(GL_NO_ERROR);
+
+    const GLubyte *pixels[20] = { 0 };
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+}
+
+TEST_P(TextureTest, ZeroSizedUploads)
+{
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+    EXPECT_GL_ERROR(GL_NO_ERROR);
+
+    // Use the texture first to make sure it's in video memory
+    glUseProgram(m2DProgram);
+    glUniform1i(mTexture2DUniformLocation, 0);
+    drawQuad(m2DProgram, "position", 0.5f);
+
+    const GLubyte *pixel[4] = { 0 };
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    EXPECT_GL_NO_ERROR();
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    EXPECT_GL_NO_ERROR();
+
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test drawing with two texture types, to trigger an ANGLE bug in validation
+TEST_P(TextureTest, CubeMapBug)
+{
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
+    EXPECT_GL_ERROR(GL_NO_ERROR);
+
+    glUseProgram(mCubeProgram);
+    GLint tex2DUniformLocation = glGetUniformLocation(mCubeProgram, "tex2D");
+    GLint texCubeUniformLocation = glGetUniformLocation(mCubeProgram, "texCube");
+    EXPECT_NE(-1, tex2DUniformLocation);
+    EXPECT_NE(-1, texCubeUniformLocation);
+    glUniform1i(tex2DUniformLocation, 0);
+    glUniform1i(texCubeUniformLocation, 1);
+    drawQuad(mCubeProgram, "position", 0.5f);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Copy of a test in conformance/textures/texture-mips, to test generate mipmaps
+TEST_P(TextureTest, MipmapsTwice)
+{
+    int px = getWindowWidth() / 2;
+    int py = getWindowHeight() / 2;
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+
+    // Fill with red
+    std::vector<GLubyte> pixels(4 * 16 * 16);
+    for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
+    {
+        pixels[pixelId * 4 + 0] = 255;
+        pixels[pixelId * 4 + 1] = 0;
+        pixels[pixelId * 4 + 2] = 0;
+        pixels[pixelId * 4 + 3] = 255;
+    }
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glGenerateMipmap(GL_TEXTURE_2D);
+
+    glUseProgram(m2DProgram);
+    glUniform1i(mTexture2DUniformLocation, 0);
+    glUniform2f(mTextureScaleUniformLocation, 0.0625f, 0.0625f);
+    drawQuad(m2DProgram, "position", 0.5f);
+    EXPECT_GL_NO_ERROR();
+    EXPECT_PIXEL_EQ(px, py, 255, 0, 0, 255);
+
+    // Fill with blue
+    for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
+    {
+        pixels[pixelId * 4 + 0] = 0;
+        pixels[pixelId * 4 + 1] = 0;
+        pixels[pixelId * 4 + 2] = 255;
+        pixels[pixelId * 4 + 3] = 255;
+    }
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+    glGenerateMipmap(GL_TEXTURE_2D);
+
+    // Fill with green
+    for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
+    {
+        pixels[pixelId * 4 + 0] = 0;
+        pixels[pixelId * 4 + 1] = 255;
+        pixels[pixelId * 4 + 2] = 0;
+        pixels[pixelId * 4 + 3] = 255;
+    }
+
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+    glGenerateMipmap(GL_TEXTURE_2D);
+
+    drawQuad(m2DProgram, "position", 0.5f);
+
+    EXPECT_GL_NO_ERROR();
+    EXPECT_PIXEL_EQ(px, py, 0, 255, 0, 255);
+}
+
+// Test creating a FBO with a cube map render target, to test an ANGLE bug
+// https://code.google.com/p/angleproject/issues/detail?id=849
+TEST_P(TextureTest, CubeMapFBO)
+{
+    GLuint fbo;
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+    glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, mTextureCube, 0);
+
+    EXPECT_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+    glDeleteFramebuffers(1, &fbo);
+
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test that glTexSubImage2D works properly when glTexStorage2DEXT has initialized the image with a default color.
+TEST_P(TextureTest, TexStorage)
+{
+    int width = getWindowWidth();
+    int height = getWindowHeight();
+
+    GLuint tex2D;
+    glGenTextures(1, &tex2D);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, tex2D);
+
+    // Fill with red
+    std::vector<GLubyte> pixels(3 * 16 * 16);
+    for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
+    {
+        pixels[pixelId * 3 + 0] = 255;
+        pixels[pixelId * 3 + 1] = 0;
+        pixels[pixelId * 3 + 2] = 0;
+    }
+
+    // ANGLE internally uses RGBA as the DirectX format for RGB images
+    // therefore glTexStorage2DEXT initializes the image to a default color to get a consistent alpha color.
+    // The data is kept in a CPU-side image and the image is marked as dirty.
+    glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
+
+    // Initializes the color of the upper-left 8x8 pixels, leaves the other pixels untouched.
+    // glTexSubImage2D should take into account that the image is dirty.
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    glUseProgram(m2DProgram);
+    glUniform1i(mTexture2DUniformLocation, 0);
+    glUniform2f(mTextureScaleUniformLocation, 1.f, 1.f);
+    drawQuad(m2DProgram, "position", 0.5f);
+    glDeleteTextures(1, &tex2D);
+    EXPECT_GL_NO_ERROR();
+    EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
+
+    // Validate that the region of the texture without data has an alpha of 1.0
+    GLubyte pixel[4];
+    glReadPixels(3 * width / 4, 3 * height / 4, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+    EXPECT_EQ(pixel[3], 255);
+}
+
+// Test that glTexSubImage2D combined with a PBO works properly when glTexStorage2DEXT has initialized the image with a default color.
+TEST_P(TextureTest, TexStorageWithPBO)
+{
+    if (extensionEnabled("NV_pixel_buffer_object"))
+    {
+        int width = getWindowWidth();
+        int height = getWindowHeight();
+
+        GLuint tex2D;
+        glGenTextures(1, &tex2D);
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, tex2D);
+
+        // Fill with red
+        std::vector<GLubyte> pixels(3 * 16 * 16);
+        for (size_t pixelId = 0; pixelId < 16 * 16; ++pixelId)
+        {
+            pixels[pixelId * 3 + 0] = 255;
+            pixels[pixelId * 3 + 1] = 0;
+            pixels[pixelId * 3 + 2] = 0;
+        }
+
+        // Read 16x16 region from red backbuffer to PBO
+        GLuint pbo;
+        glGenBuffers(1, &pbo);
+        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
+        glBufferData(GL_PIXEL_UNPACK_BUFFER, 3 * 16 * 16, pixels.data(), GL_STATIC_DRAW);
+
+        // ANGLE internally uses RGBA as the DirectX format for RGB images
+        // therefore glTexStorage2DEXT initializes the image to a default color to get a consistent alpha color.
+        // The data is kept in a CPU-side image and the image is marked as dirty.
+        glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGB8, 16, 16);
+
+        // Initializes the color of the upper-left 8x8 pixels, leaves the other pixels untouched.
+        // glTexSubImage2D should take into account that the image is dirty.
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glUseProgram(m2DProgram);
+        glUniform1i(mTexture2DUniformLocation, 0);
+        glUniform2f(mTextureScaleUniformLocation, 1.f, 1.f);
+        drawQuad(m2DProgram, "position", 0.5f);
+        glDeleteTextures(1, &tex2D);
+        glDeleteTextures(1, &pbo);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_PIXEL_EQ(3 * width / 4, 3 * height / 4, 0, 0, 0, 255);
+        EXPECT_PIXEL_EQ(width / 4, height / 4, 255, 0, 0, 255);
+    }
+}
+
+// See description on testFloatCopySubImage
+TEST_P(TextureTest, CopySubImageFloat_R_R)
+{
+    testFloatCopySubImage(1, 1);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RG_R)
+{
+    testFloatCopySubImage(2, 1);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RG_RG)
+{
+    testFloatCopySubImage(2, 2);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGB_R)
+{
+    testFloatCopySubImage(3, 1);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGB_RG)
+{
+    testFloatCopySubImage(3, 2);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGB_RGB)
+{
+    testFloatCopySubImage(3, 3);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGBA_R)
+{
+    testFloatCopySubImage(4, 1);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGBA_RG)
+{
+    testFloatCopySubImage(4, 2);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGBA_RGB)
+{
+    testFloatCopySubImage(4, 3);
+}
+
+TEST_P(TextureTest, CopySubImageFloat_RGBA_RGBA)
+{
+    testFloatCopySubImage(4, 4);
+}
+
+// Port of https://www.khronos.org/registry/webgl/conformance-suites/1.0.3/conformance/textures/texture-npot.html
+// Run against GL_ALPHA/UNSIGNED_BYTE format, to ensure that D3D11 Feature Level 9_3 correctly handles GL_ALPHA
+TEST_P(TextureTest, TextureNPOT_GL_ALPHA_UBYTE)
+{
+    const int npotTexSize = 5;
+    const int potTexSize = 4; // Should be less than npotTexSize
+    GLuint tex2D;
+
+    if (extensionEnabled("GL_OES_texture_npot"))
+    {
+        // This test isn't applicable if texture_npot is enabled
+        return;
+    }
+
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+    glActiveTexture(GL_TEXTURE0);
+    glGenTextures(1, &tex2D);
+    glBindTexture(GL_TEXTURE_2D, tex2D);
+
+    std::vector<GLubyte> pixels(1 * npotTexSize * npotTexSize);
+    for (size_t pixelId = 0; pixelId < npotTexSize * npotTexSize; ++pixelId)
+    {
+        pixels[pixelId] = 64;
+    }
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    // Check that an NPOT texture not on level 0 generates INVALID_VALUE
+    glTexImage2D(GL_TEXTURE_2D, 1, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    // Check that an NPOT texture on level 0 succeeds
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, npotTexSize, npotTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+    EXPECT_GL_NO_ERROR();
+
+    // Check that generateMipmap fails on NPOT
+    glGenerateMipmap(GL_TEXTURE_2D);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Check that nothing is drawn if filtering is not correct for NPOT
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glClear(GL_COLOR_BUFFER_BIT);
+    drawQuad(m2DProgram, "position", 1.0f);
+    EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
+
+    // NPOT texture with TEXTURE_MIN_FILTER not NEAREST or LINEAR should draw with 0,0,0,255
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+    glClear(GL_COLOR_BUFFER_BIT);
+    drawQuad(m2DProgram, "position", 1.0f);
+    EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 255);
+
+    // NPOT texture with TEXTURE_MIN_FILTER set to LINEAR should draw
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glClear(GL_COLOR_BUFFER_BIT);
+    drawQuad(m2DProgram, "position", 1.0f);
+    EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
+
+    // Check that glTexImage2D for POT texture succeeds
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, potTexSize, potTexSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels.data());
+    EXPECT_GL_NO_ERROR();
+
+    // Check that generateMipmap for an POT texture succeeds
+    glGenerateMipmap(GL_TEXTURE_2D);
+    EXPECT_GL_NO_ERROR();
+
+    // POT texture with TEXTURE_MIN_FILTER set to LINEAR_MIPMAP_LINEAR should draw
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glClear(GL_COLOR_BUFFER_BIT);
+    drawQuad(m2DProgram, "position", 1.0f);
+    EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 64);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
+ANGLE_INSTANTIATE_TEST(TextureTest, ES2_D3D9(), ES2_D3D11(), ES2_D3D11_FL9_3());
+
+} // namespace