text: //
  // Copyright 2020 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.
  //
  // ActiveTextureCacheTest.cpp: Regression tests of ANGLE's ActiveTextureCache inside gl::State.
  
  #include "test_utils/ANGLETest.h"
  #include "test_utils/gl_raii.h"
  
  namespace angle
  {
  
  class ActiveTextureCacheTest : public ANGLETest
  {
    protected:
      ActiveTextureCacheTest()
      {
          setWindowWidth(128);
          setWindowHeight(128);
          setConfigRedBits(8);
          setConfigGreenBits(8);
          setConfigBlueBits(8);
          setConfigAlphaBits(8);
      }
  
      void testSetUp() override
      {
          constexpr char kVS[] =
              "precision highp float;\n"
              "\n"
              "void main()\n"
              "{\n"
              "   gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
              "}\n";
  
          constexpr char k2DFS[] =
              "precision highp float;\n"
              "uniform sampler2D tex2D;\n"
              "uniform samplerCube texCube;\n"
              "\n"
              "void main()\n"
              "{\n"
              "   gl_FragColor = texture2D(tex2D, vec2(0.0, 0.0)) + textureCube(texCube, vec3(0.0, "
              "0.0, 0.0));\n"
              "}\n";
  
          mProgram = CompileProgram(kVS, k2DFS);
          ASSERT_NE(0u, mProgram);
  
          m2DTextureLocation = glGetUniformLocation(mProgram, "tex2D");
          ASSERT_NE(-1, m2DTextureLocation);
  
          mCubeTextureLocation = glGetUniformLocation(mProgram, "texCube");
          ASSERT_NE(-1, mCubeTextureLocation);
      }
  
      void testTearDown() override { glDeleteProgram(mProgram); }
  
      GLuint mProgram            = 0;
      GLint m2DTextureLocation   = -1;
      GLint mCubeTextureLocation = -1;
  };
  
  // Regression test for a bug that causes the ActiveTexturesCache to get out of sync with the
  // currently bound textures when changing program uniforms in such a way that the program becomes
  // invalid.
  TEST_P(ActiveTextureCacheTest, UniformChangeUpdatesActiveTextureCache)
  {
      glUseProgram(mProgram);
  
      // Generate two textures and reset the texture binding
      GLuint tex0 = 0;
      glGenTextures(1, &tex0);
      glBindTexture(GL_TEXTURE_2D, tex0);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
  
      GLuint tex1 = 0;
      glGenTextures(1, &tex1);
      glBindTexture(GL_TEXTURE_2D, tex1);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
      glBindTexture(GL_TEXTURE_2D, 0);
  
      // Set the active texture to 1 and bind tex0.
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_2D, tex0);
  
      // Point the program's 2D sampler at texture binding 1. The texture will be added to the
      // ActiveTexturesCache because it matches the program's sampler type for this texture binding.
      glUniform1i(m2DTextureLocation, 1);
  
      // Point the program's cube sampler to texture binding 1 as well. This causes the program's
      // samplers become invalid and the ActiveTexturesCache is NOT updated.
      glUniform1i(mCubeTextureLocation, 1);
  
      // Bind tex1. ActiveTexturesCache is NOT updated (still contains tex0). The current texture
      // bindings do not match ActiveTexturesCache's state.
      glBindTexture(GL_TEXTURE_2D, tex1);
  
      // Delete tex0. The ActiveTexturesCache entry that points to tex0 is not cleared because tex0 is
      // not currently bound.
      glDeleteTextures(1, &tex0);
  
      // Use-after-free occurs during context destruction when the ActiveTexturesCache is cleared.
  }
  
  // Use this to select which configurations (e.g. which renderer, which GLES major version) these
  // tests should be run against.
  ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ActiveTextureCacheTest);
  
  }  // namespace angle
  