text:
using ::testing::AtLeast;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
using ::testing::_;

namespace gpu {
namespace gles2 {

class TextureTestHelper {
 public:
  static bool IsNPOT(const Texture* texture) {
    return texture->npot();
  }
  static bool IsTextureComplete(const Texture* texture) {
    return texture->texture_complete();
  }
  static bool IsCubeComplete(const Texture* texture) {
    return texture->cube_complete();
  }
  static GLuint owned_service_id(const Texture* texture) {
    return texture->owned_service_id();
  }
};

class TextureManagerTest : public GpuServiceTest {
 public:
  static const GLint kMaxTextureSize = 32;
  static const GLint kMaxCubeMapTextureSize = 8;
  static const GLint kMaxRectangleTextureSize = 32;
  static const GLint kMaxExternalTextureSize = 32;
  static const GLint kMax3DTextureSize = 256;
  static const GLint kMax2dLevels = 6;
  static const GLint kMaxCubeMapLevels = 4;
  static const GLint kMaxExternalLevels = 1;
  static const GLint kMax3dLevels = 9;
  static const bool kUseDefaultTextures = false;

  TextureManagerTest() {
    // Always run with this command line, but the ES3 features are not
    // enabled without FeatureInfo::EnableES3Validators().
    base::CommandLine command_line(0, nullptr);
    command_line.AppendSwitch(switches::kEnableUnsafeES3APIs);
    feature_info_ = new FeatureInfo(command_line);
  }

  ~TextureManagerTest() override {}

 protected:
  void SetUp() override {
    GpuServiceTest::SetUp();
    manager_.reset(new TextureManager(NULL,
                                      feature_info_.get(),
                                      kMaxTextureSize,
                                      kMaxCubeMapTextureSize,
                                      kMaxRectangleTextureSize,
                                      kMax3DTextureSize,
                                      kUseDefaultTextures));
    TestHelper::SetupTextureManagerInitExpectations(
        gl_.get(), false, false, "", kUseDefaultTextures);
    manager_->Initialize();
    error_state_.reset(new ::testing::StrictMock<gles2::MockErrorState>());
  }

  void TearDown() override {
    manager_->Destroy(false);
    manager_.reset();
    GpuServiceTest::TearDown();
  }

  void SetParameter(
      TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) {
    TestHelper::SetTexParameteriWithExpectations(
        gl_.get(), error_state_.get(), manager_.get(),
        texture_ref, pname, value, error);
  }

  void SetupFeatureInfo(const char* gl_extensions,
                        const char* gl_version,
                        bool enable_es3) {
    TestHelper::SetupFeatureInfoInitExpectationsWithGLVersion(
        gl_.get(), gl_extensions, "", gl_version, enable_es3);
    feature_info_->InitializeForTesting();
    if (enable_es3) {
      EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_COLOR_ATTACHMENTS, _))
          .WillOnce(SetArgPointee<1>(8))
          .RetiresOnSaturation();
      EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_DRAW_BUFFERS, _))
          .WillOnce(SetArgPointee<1>(8))
          .RetiresOnSaturation();
      feature_info_->EnableES3Validators();
    }
  }

  scoped_refptr<FeatureInfo> feature_info_;
  scoped_ptr<TextureManager> manager_;
  scoped_ptr<MockErrorState> error_state_;
};

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
const GLint TextureManagerTest::kMaxTextureSize;
const GLint TextureManagerTest::kMaxCubeMapTextureSize;
const GLint TextureManagerTest::kMaxRectangleTextureSize;
const GLint TextureManagerTest::kMaxExternalTextureSize;
const GLint TextureManagerTest::kMax3DTextureSize;
const GLint TextureManagerTest::kMax2dLevels;
const GLint TextureManagerTest::kMaxCubeMapLevels;
const GLint TextureManagerTest::kMaxExternalLevels;
const GLint TextureManagerTest::kMax3dLevels;
#endif

class GLStreamTextureImageStub : public GLStreamTextureImage {
 public:
  GLStreamTextureImageStub() {}

  // Overridden from GLImage:
  void Destroy(bool have_context) override {}
  gfx::Size GetSize() override { return gfx::Size(); }
  unsigned GetInternalFormat() override { return 0; }
  bool BindTexImage(unsigned target) override { return false; }
  void ReleaseTexImage(unsigned target) override {}
  bool CopyTexImage(unsigned target) override { return false; }
  bool CopyTexSubImage(unsigned target,
                       const gfx::Point& offset,
                       const gfx::Rect& rect) override {
    return false;
  }
  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
                            int z_order,
                            gfx::OverlayTransform transform,
                            const gfx::Rect& bounds_rect,
                            const gfx::RectF& crop_rect) override {
    return false;
  }
  void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
                    uint64_t process_tracing_id,
                    const std::string& dump_name) override {}
  void GetTextureMatrix(float matrix[16]) override {}

 protected:
  ~GLStreamTextureImageStub() override {}
};

TEST_F(TextureManagerTest, Basic) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLuint kClient2Id = 2;
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  // Check we can create texture.
  manager_->CreateTexture(kClient1Id, kService1Id);
  // Check texture got created.
  scoped_refptr<TextureRef> texture = manager_->GetTexture(kClient1Id);
  ASSERT_TRUE(texture.get() != NULL);
  EXPECT_EQ(kService1Id, texture->service_id());
  EXPECT_EQ(kClient1Id, texture->client_id());
  EXPECT_EQ(texture->texture(), manager_->GetTextureForServiceId(
      texture->service_id()));
  // Check we get nothing for a non-existent texture.
  EXPECT_TRUE(manager_->GetTexture(kClient2Id) == NULL);
  // Check trying to a remove non-existent textures does not crash.
  manager_->RemoveTexture(kClient2Id);
  // Check that it gets deleted when the last reference is released.
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  // Check we can't get the texture after we remove it.
  manager_->RemoveTexture(kClient1Id);
  EXPECT_TRUE(manager_->GetTexture(kClient1Id) == NULL);
  EXPECT_EQ(0u, texture->client_id());
}

TEST_F(TextureManagerTest, SetParameter) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  // Check we can create texture.
  manager_->CreateTexture(kClient1Id, kService1Id);
  // Check texture got created.
  TextureRef* texture_ref = manager_->GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  Texture* texture = texture_ref->texture();
  manager_->SetTarget(texture_ref, GL_TEXTURE_2D);
  SetParameter(texture_ref, GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
  EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
  SetParameter(texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
  EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->mag_filter());
  SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s());
  SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t());
  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1, GL_NO_ERROR);
  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2, GL_NO_ERROR);
  SetParameter(
      texture_ref, GL_TEXTURE_MIN_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
  EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
  SetParameter(
      texture_ref, GL_TEXTURE_MAG_FILTER, GL_CLAMP_TO_EDGE, GL_INVALID_ENUM);
  EXPECT_EQ(static_cast<GLenum>(GL_NEAREST), texture->min_filter());
  SetParameter(texture_ref, GL_TEXTURE_WRAP_S, GL_NEAREST, GL_INVALID_ENUM);
  EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_s());
  SetParameter(texture_ref, GL_TEXTURE_WRAP_T, GL_NEAREST, GL_INVALID_ENUM);
  EXPECT_EQ(static_cast<GLenum>(GL_CLAMP_TO_EDGE), texture->wrap_t());
  SetParameter(texture_ref, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0, GL_INVALID_VALUE);
}

TEST_F(TextureManagerTest, UseDefaultTexturesTrue) {
  bool use_default_textures = true;
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(),
      false, false, "GL_ANGLE_texture_usage", use_default_textures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         use_default_textures);
  manager.Initialize();

  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D) != NULL);
  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP) != NULL);

  // TODO(vmiura): Test GL_TEXTURE_EXTERNAL_OES & GL_TEXTURE_RECTANGLE_ARB.

  manager.Destroy(false);
}

TEST_F(TextureManagerTest, UseDefaultTexturesFalse) {
  bool use_default_textures = false;
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(),
      false, false, "GL_ANGLE_texture_usage", use_default_textures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         use_default_textures);
  manager.Initialize();

  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D) == NULL);
  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_CUBE_MAP) == NULL);

  // TODO(vmiura): Test GL_TEXTURE_EXTERNAL_OES & GL_TEXTURE_RECTANGLE_ARB.

  manager.Destroy(false);
}

TEST_F(TextureManagerTest, UseDefaultTexturesTrueES3) {
  bool use_default_textures = true;
  SetupFeatureInfo("", "OpenGL ES 3.0", true);
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(),
      true, false, "", use_default_textures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         use_default_textures);
  manager.Initialize();

  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_3D) != NULL);
  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D_ARRAY) != NULL);

  manager.Destroy(false);
}

TEST_F(TextureManagerTest, UseDefaultTexturesFalseES3) {
  bool use_default_textures = false;
  SetupFeatureInfo("", "OpenGL ES 3.0", true);
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(),
      true, false, "", use_default_textures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         use_default_textures);
  manager.Initialize();

  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_3D) == NULL);
  EXPECT_TRUE(manager.GetDefaultTextureInfo(GL_TEXTURE_2D_ARRAY) == NULL);

  manager.Destroy(false);
}

TEST_F(TextureManagerTest, TextureUsageExt) {
  TestHelper::SetupTextureManagerInitExpectations(
      gl_.get(), false, false, "GL_ANGLE_texture_usage", kUseDefaultTextures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.Initialize();
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  // Check we can create texture.
  manager.CreateTexture(kClient1Id, kService1Id);
  // Check texture got created.
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  TestHelper::SetTexParameteriWithExpectations(
      gl_.get(), error_state_.get(), &manager, texture_ref,
      GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, GL_NO_ERROR);
  EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_ATTACHMENT_ANGLE),
            texture_ref->texture()->usage());
  manager.Destroy(false);
}

TEST_F(TextureManagerTest, Destroy) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  TestHelper::SetupTextureManagerInitExpectations(
      gl_.get(), false, false, "", kUseDefaultTextures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.Initialize();
  // Check we can create texture.
  manager.CreateTexture(kClient1Id, kService1Id);
  // Check texture got created.
  TextureRef* texture = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture != NULL);
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  TestHelper::SetupTextureManagerDestructionExpectations(
      gl_.get(), false, false, "", kUseDefaultTextures);
  manager.Destroy(true);
  // Check that resources got freed.
  texture = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture == NULL);
}

TEST_F(TextureManagerTest, MaxValues) {
  // Check we get the right values for the max sizes.
  EXPECT_EQ(kMax2dLevels, manager_->MaxLevelsForTarget(GL_TEXTURE_2D));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_X));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_X));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Y));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Z));
  EXPECT_EQ(kMaxCubeMapLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z));
  EXPECT_EQ(kMaxExternalLevels,
            manager_->MaxLevelsForTarget(GL_TEXTURE_EXTERNAL_OES));
  EXPECT_EQ(kMax2dLevels, manager_->MaxLevelsForTarget(GL_TEXTURE_2D_ARRAY));
  EXPECT_EQ(kMax3dLevels, manager_->MaxLevelsForTarget(GL_TEXTURE_3D));
  EXPECT_EQ(kMaxTextureSize, manager_->MaxSizeForTarget(GL_TEXTURE_2D));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_X));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_X));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Y));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_POSITIVE_Z));
  EXPECT_EQ(kMaxCubeMapTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z));
  EXPECT_EQ(kMaxRectangleTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_RECTANGLE_ARB));
  EXPECT_EQ(kMaxExternalTextureSize,
            manager_->MaxSizeForTarget(GL_TEXTURE_EXTERNAL_OES));
  EXPECT_EQ(kMaxTextureSize, manager_->MaxSizeForTarget(GL_TEXTURE_2D_ARRAY));
  EXPECT_EQ(kMax3DTextureSize, manager_->MaxSizeForTarget(GL_TEXTURE_3D));
}

TEST_F(TextureManagerTest, ValidForTarget) {
  // check 2d
  EXPECT_TRUE(manager_->ValidForTarget(
      GL_TEXTURE_2D, 0, kMaxTextureSize, kMaxTextureSize, 1));
  EXPECT_TRUE(manager_->ValidForTarget(
      GL_TEXTURE_2D, kMax2dLevels - 1, 1, 1, 1));
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_2D, kMax2dLevels - 1, 1, 2, 1));
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_2D, kMax2dLevels - 1, 2, 1, 1));
  // check level out of range.
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_2D, kMax2dLevels, kMaxTextureSize, 1, 1));
  // check has depth.
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_2D, kMax2dLevels, kMaxTextureSize, 1, 2));
  // Check NPOT width on level 0
  EXPECT_TRUE(manager_->ValidForTarget(GL_TEXTURE_2D, 0, 5, 2, 1));
  // Check NPOT height on level 0
  EXPECT_TRUE(manager_->ValidForTarget(GL_TEXTURE_2D, 0, 2, 5, 1));
  // Check NPOT width on level 1
  EXPECT_FALSE(manager_->ValidForTarget(GL_TEXTURE_2D, 1, 5, 2, 1));
  // Check NPOT height on level 1
  EXPECT_FALSE(manager_->ValidForTarget(GL_TEXTURE_2D, 1, 2, 5, 1));

  // check cube
  EXPECT_TRUE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, 0,
      kMaxCubeMapTextureSize, kMaxCubeMapTextureSize, 1));
  EXPECT_TRUE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels - 1, 1, 1, 1));
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels - 1, 2, 2, 1));
  // check level out of range.
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels,
      kMaxCubeMapTextureSize, 1, 1));
  // check not square.
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels,
      kMaxCubeMapTextureSize, 1, 1));
  // check has depth.
  EXPECT_FALSE(manager_->ValidForTarget(
      GL_TEXTURE_CUBE_MAP, kMaxCubeMapLevels,
      kMaxCubeMapTextureSize, 1, 2));

  for (GLint level = 0; level < kMax2dLevels; ++level) {
    EXPECT_TRUE(manager_->ValidForTarget(
        GL_TEXTURE_2D, level, kMaxTextureSize >> level, 1, 1));
    EXPECT_TRUE(manager_->ValidForTarget(
        GL_TEXTURE_2D, level, 1, kMaxTextureSize >> level, 1));
    EXPECT_FALSE(manager_->ValidForTarget(
        GL_TEXTURE_2D, level, (kMaxTextureSize >> level) + 1, 1, 1));
    EXPECT_FALSE(manager_->ValidForTarget(
        GL_TEXTURE_2D, level, 1, (kMaxTextureSize >> level) + 1, 1));
  }

  for (GLint level = 0; level < kMaxCubeMapLevels; ++level) {
    EXPECT_TRUE(manager_->ValidForTarget(
        GL_TEXTURE_CUBE_MAP, level,
        kMaxCubeMapTextureSize >> level,
        kMaxCubeMapTextureSize >> level,
        1));
    EXPECT_FALSE(manager_->ValidForTarget(
        GL_TEXTURE_CUBE_MAP, level,
        (kMaxCubeMapTextureSize >> level) * 2,
        (kMaxCubeMapTextureSize >> level) * 2,
        1));
  }
}

TEST_F(TextureManagerTest, ValidForTargetNPOT) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_npot");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  // Check NPOT width on level 0
  EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 0, 5, 2, 1));
  // Check NPOT height on level 0
  EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 0, 2, 5, 1));
  // Check NPOT width on level 1
  EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 1, 5, 2, 1));
  // Check NPOT height on level 1
  EXPECT_TRUE(manager.ValidForTarget(GL_TEXTURE_2D, 1, 2, 5, 1));
  manager.Destroy(false);
}

TEST_F(TextureManagerTest, OverrideServiceID) {
  // Create a texture.
  const GLuint kClientId = 1;
  const GLuint kServiceId = 11;
  manager_->CreateTexture(kClientId, kServiceId);
  scoped_refptr<TextureRef> texture_ref(manager_->GetTexture(kClientId));
  manager_->SetTarget(texture_ref.get(), GL_TEXTURE_EXTERNAL_OES);

  Texture* texture = texture_ref->texture();
  GLuint owned_service_id = TextureTestHelper::owned_service_id(texture);
  GLuint service_id = texture->service_id();
  // Initially, the texture should use the same service id that it owns.
  EXPECT_EQ(owned_service_id, service_id);

  // Override the service_id.
  GLuint unowned_service_id = service_id + 1;
  texture->SetUnownedServiceId(unowned_service_id);

  // Make sure that service_id() changed but owned_service_id() didn't.
  EXPECT_EQ(unowned_service_id, texture->service_id());
  EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));

  // Undo the override.
  texture->SetUnownedServiceId(0);

  // The service IDs should be back as they were.
  EXPECT_EQ(service_id, texture->service_id());
  EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));

  // Override again, so that we can check delete behavior.
  texture->SetUnownedServiceId(unowned_service_id);
  EXPECT_EQ(unowned_service_id, texture->service_id());
  EXPECT_EQ(owned_service_id, TextureTestHelper::owned_service_id(texture));

  // Remove the texture.  It should delete the texture id that it owns, even
  // though it is overridden.
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(owned_service_id)))
      .Times(1)
      .RetiresOnSaturation();
  manager_->RemoveTexture(kClientId);
}

TEST_F(TextureManagerTest, AlphaLuminanceCompatibilityProfile) {
  const GLuint kClientId = 1;
  const GLuint kServiceId = 11;

  SetupFeatureInfo("", "2.1", false);
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(), false, false, "",
      kUseDefaultTextures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.Initialize();

  // Create a texture.
  manager.CreateTexture(kClientId, kServiceId);
  scoped_refptr<TextureRef> texture_ref(manager.GetTexture(kClientId));
  manager.SetTarget(texture_ref.get(), GL_TEXTURE_2D);

  Texture* texture = texture_ref->texture();

  // GL_ALPHA emulation
  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 1,
      0, GL_ALPHA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  // GL_LUMINANCE emulation
  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1,
      1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  // GL_LUMINANCE_ALPHA emulation
  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
      1, 1, 1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kServiceId)))
      .Times(1)
      .RetiresOnSaturation();
  manager.RemoveTexture(kClientId);
}

TEST_F(TextureManagerTest, AlphaLuminanceCoreProfileEmulation) {
  const GLuint kClientId = 1;
  const GLuint kServiceId = 11;

  SetupFeatureInfo("", "4.2", true);
  TestHelper::SetupTextureManagerInitExpectations(gl_.get(), true, true, "",
      kUseDefaultTextures);
  TextureManager manager(NULL,
                         feature_info_.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.Initialize();

  // Create a texture.
  manager.CreateTexture(kClientId, kServiceId);
  scoped_refptr<TextureRef> texture_ref(manager.GetTexture(kClientId));
  manager.SetTarget(texture_ref.get(), GL_TEXTURE_2D);

  Texture* texture = texture_ref->texture();

  // GL_ALPHA emulation
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_NONE))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_NONE))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_NONE))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED))
      .Times(1)
      .RetiresOnSaturation();

  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_ALPHA, 1, 1, 1,
      0, GL_ALPHA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  // GL_LUMINANCE emulation
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE))
      .Times(1)
      .RetiresOnSaturation();

  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_LUMINANCE, 1, 1,
      1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  // GL_LUMINANCE_ALPHA emulation
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A,
              GL_GREEN))
      .Times(1)
      .RetiresOnSaturation();

  manager.SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
      1, 1, 1, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  texture->ApplyFormatWorkarounds(feature_info_.get());

  // Ensure explicitly setting swizzles while using emulated settings properly
  // swizzles the swizzle.
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R,
              GL_GREEN))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A,
              GL_RED))
      .Times(1)
      .RetiresOnSaturation();

  manager.SetParameteri("TexParameteri", error_state_.get(), texture_ref.get(),
      GL_TEXTURE_SWIZZLE_R, GL_ALPHA);
  manager.SetParameteri("TexParameteri", error_state_.get(), texture_ref.get(),
      GL_TEXTURE_SWIZZLE_A, GL_GREEN);

  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kServiceId)))
      .Times(1)
      .RetiresOnSaturation();
  manager.RemoveTexture(kClientId);
}

class TextureTestBase : public GpuServiceTest {
 public:
  static const GLint kMaxTextureSize = 32;
  static const GLint kMaxCubeMapTextureSize = 8;
  static const GLint kMaxRectangleTextureSize = 32;
  static const GLint kMax3DTextureSize = 256;
  static const GLint kMax2dLevels = 6;
  static const GLint kMaxCubeMapLevels = 4;
  static const GLuint kClient1Id = 1;
  static const GLuint kService1Id = 11;
  static const bool kUseDefaultTextures = false;

  TextureTestBase()
      : feature_info_(new FeatureInfo()) {
  }
  ~TextureTestBase() override { texture_ref_ = NULL; }

 protected:
  void SetUpBase(MemoryTracker* memory_tracker, const std::string& extensions) {
    GpuServiceTest::SetUp();
    if (!extensions.empty()) {
      TestHelper::SetupFeatureInfoInitExpectations(gl_.get(),
                                                   extensions.c_str());
      feature_info_->InitializeForTesting();
    }

    manager_.reset(new TextureManager(memory_tracker,
                                      feature_info_.get(),
                                      kMaxTextureSize,
                                      kMaxCubeMapTextureSize,
                                      kMaxRectangleTextureSize,
                                      kMax3DTextureSize,
                                      kUseDefaultTextures));
    decoder_.reset(new ::testing::StrictMock<gles2::MockGLES2Decoder>());
    error_state_.reset(new ::testing::StrictMock<gles2::MockErrorState>());
    manager_->CreateTexture(kClient1Id, kService1Id);
    texture_ref_ = manager_->GetTexture(kClient1Id);
    ASSERT_TRUE(texture_ref_.get() != NULL);
  }

  void TearDown() override {
    if (texture_ref_.get()) {
      // If it's not in the manager then setting texture_ref_ to NULL will
      // delete the texture.
      if (!texture_ref_->client_id()) {
        // Check that it gets deleted when the last reference is released.
        EXPECT_CALL(*gl_,
            DeleteTextures(1, ::testing::Pointee(texture_ref_->service_id())))
            .Times(1)
            .RetiresOnSaturation();
      }
      texture_ref_ = NULL;
    }
    manager_->Destroy(false);
    manager_.reset();
    GpuServiceTest::TearDown();
  }

  void SetParameter(
      TextureRef* texture_ref, GLenum pname, GLint value, GLenum error) {
    TestHelper::SetTexParameteriWithExpectations(
        gl_.get(), error_state_.get(), manager_.get(),
        texture_ref, pname, value, error);
  }

  scoped_ptr<MockGLES2Decoder> decoder_;
  scoped_ptr<MockErrorState> error_state_;
  scoped_refptr<FeatureInfo> feature_info_;
  scoped_ptr<TextureManager> manager_;
  scoped_refptr<TextureRef> texture_ref_;
};

class TextureTest : public TextureTestBase {
 protected:
  void SetUp() override { SetUpBase(NULL, std::string()); }
};

class TextureMemoryTrackerTest : public TextureTestBase {
 protected:
  void SetUp() override {
    mock_memory_tracker_ = new StrictMock<MockMemoryTracker>();
    SetUpBase(mock_memory_tracker_.get(), std::string());
  }

  scoped_refptr<MockMemoryTracker> mock_memory_tracker_;
};

#define EXPECT_MEMORY_ALLOCATION_CHANGE(old_size, new_size)   \
  EXPECT_CALL(*mock_memory_tracker_.get(),                    \
              TrackMemoryAllocatedChange(old_size, new_size)) \
      .Times(1).RetiresOnSaturation()

TEST_F(TextureTest, Basic) {
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(0u, texture->target());
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_EQ(0, texture->num_uncleared_mips());
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(texture->IsImmutable());
  EXPECT_EQ(static_cast<GLenum>(GL_NEAREST_MIPMAP_LINEAR),
            texture->min_filter());
  EXPECT_EQ(static_cast<GLenum>(GL_LINEAR), texture->mag_filter());
  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_s());
  EXPECT_EQ(static_cast<GLenum>(GL_REPEAT), texture->wrap_t());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_EQ(0u, texture->estimated_size());
}

TEST_F(TextureTest, SetTargetTexture2D) {
  Texture* texture = texture_ref_->texture();
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(texture->IsImmutable());
}

TEST_F(TextureTest, SetTargetTextureExternalOES) {
  Texture* texture = texture_ref_->texture();
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES);
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_TRUE(texture->IsImmutable());
}

TEST_F(TextureTest, ZeroSizeCanNotRender) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
}

TEST_F(TextureTest, EstimatedSize) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 4));
  EXPECT_EQ(8u * 4u * 4u, texture_ref_->texture()->estimated_size());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2, GL_RGBA, 8, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 4));
  EXPECT_EQ(8u * 4u * 4u * 2u, texture_ref_->texture()->estimated_size());
}

TEST_F(TextureMemoryTrackerTest, EstimatedSize) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 128);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 4));
  EXPECT_MEMORY_ALLOCATION_CHANGE(128, 0);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 256);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2, GL_RGBA, 8, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 4));
  // Add expectation for texture deletion.
  EXPECT_MEMORY_ALLOCATION_CHANGE(256, 0);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0);
}

TEST_F(TextureTest, POT2D) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Check Setting level 0 to POT
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  EXPECT_TRUE(texture->SafeToRenderFrom());
  // Set filters to something that will work with a single mip.
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  // Set them back.
  SetParameter(texture_ref_.get(),
               GL_TEXTURE_MIN_FILTER,
               GL_LINEAR_MIPMAP_LINEAR,
               GL_NO_ERROR);

  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  // Make mips.
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  // Change a mip.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  // Set a level past the number of mips that would get generated.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  // Make mips.
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
}

TEST_F(TextureTest, BaseLevel) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Check Setting level 1 to POT
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_BASE_LEVEL, 1, GL_NO_ERROR);
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
}

TEST_F(TextureTest, BaseLevelMaxLevel) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Set up level 2, 3, 4.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 8));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 4, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR,
      GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_BASE_LEVEL, 2, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MAX_LEVEL, 4, GL_NO_ERROR);
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_BASE_LEVEL, 0, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
}

TEST_F(TextureMemoryTrackerTest, MarkMipmapsGenerated) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 64);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_MEMORY_ALLOCATION_CHANGE(64, 0);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 84);
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_MEMORY_ALLOCATION_CHANGE(84, 0);
  EXPECT_MEMORY_ALLOCATION_CHANGE(0, 0);
}

TEST_F(TextureTest, UnusedMips) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Set level zero to large size.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  // Set level zero to large smaller (levels unused mips)
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  // Set an unused level to some size
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 4, GL_RGBA, 16, 16,
                         1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(16, 16));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
}

TEST_F(TextureTest, NPOT2D) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Check Setting level 0 to NPOT
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 5));
  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_LINEAR, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
  // Change it to POT.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
}

TEST_F(TextureTest, NPOT2DNPOTOK) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_npot");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  Texture* texture = texture_ref->texture();

  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  // Check Setting level 0 to NPOT
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 4, 5, 1, 0,
                       GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 5));
  EXPECT_TRUE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager.CanGenerateMipmaps(texture_ref));
  EXPECT_FALSE(manager.CanRender(texture_ref));
  manager.MarkMipmapsGenerated(texture_ref);
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(manager.CanRender(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, POTCubeMap) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target());
  // Check Setting level 0 each face to POT
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));

  // Make mips.
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));

  // Change a mip.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 1,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  // Set a level past the number of mips that would get generated.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 3,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  // Make mips.
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
}

TEST_F(TextureTest, POTCubeMapWithoutMipmap) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);

  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target());
  // Check Setting level 0 each face to POT
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_FALSE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_FALSE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_FALSE(manager_->CanRender(texture_ref_.get()));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0,
                         GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(4, 4));
  EXPECT_FALSE(TextureTestHelper::IsNPOT(texture));
  EXPECT_FALSE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_TRUE(TextureTestHelper::IsCubeComplete(texture));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  EXPECT_TRUE(manager_->CanRender(texture_ref_.get()));
}

TEST_F(TextureTest, GetLevelSize) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_3D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_3D, 1, GL_RGBA, 4, 5, 6,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 5));
  GLsizei width = -1;
  GLsizei height = -1;
  GLsizei depth = -1;
  Texture* texture = texture_ref_->texture();
  EXPECT_FALSE(
      texture->GetLevelSize(GL_TEXTURE_3D, -1, &width, &height, &depth));
  EXPECT_FALSE(
      texture->GetLevelSize(GL_TEXTURE_3D, 1000, &width, &height, &depth));
  EXPECT_FALSE(
      texture->GetLevelSize(GL_TEXTURE_3D, 0, &width, &height, &depth));
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_3D, 1, &width, &height, &depth));
  EXPECT_EQ(4, width);
  EXPECT_EQ(5, height);
  EXPECT_EQ(6, depth);
  manager_->RemoveTexture(kClient1Id);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_3D, 1, &width, &height, &depth));
  EXPECT_EQ(4, width);
  EXPECT_EQ(5, height);
  EXPECT_EQ(6, depth);
}

TEST_F(TextureTest, GetLevelSizeTexture2DArray) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D_ARRAY);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 4,
                         4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4));
  GLsizei width = -1;
  GLsizei height = -1;
  GLsizei depth = -1;
  Texture* texture = texture_ref_->texture();
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, -1,
                                     &width, &height, &depth));
  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 1000,
                                     &width, &height, &depth));
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 0,
                                    &width, &height, &depth));
  EXPECT_EQ(4, width);
  EXPECT_EQ(4, height);
  EXPECT_EQ(2, depth);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 1,
                                    &width, &height, &depth));
  EXPECT_EQ(2, width);
  EXPECT_EQ(2, height);
  EXPECT_EQ(2, depth);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 2,
                                    &width, &height, &depth));
  EXPECT_EQ(1, width);
  EXPECT_EQ(1, height);
  EXPECT_EQ(2, depth);
  manager_->RemoveTexture(kClient1Id);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 0,
                                    &width, &height, &depth));
  EXPECT_EQ(4, width);
  EXPECT_EQ(4, height);
  EXPECT_EQ(2, depth);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 1,
                                    &width, &height, &depth));
  EXPECT_EQ(2, width);
  EXPECT_EQ(2, height);
  EXPECT_EQ(2, depth);
  EXPECT_TRUE(texture->GetLevelSize(GL_TEXTURE_2D_ARRAY, 2,
                                    &width, &height, &depth));
  EXPECT_EQ(1, width);
  EXPECT_EQ(1, height);
  EXPECT_EQ(2, depth);
}

TEST_F(TextureTest, GetLevelType) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 5));
  GLenum type = 0;
  GLenum format = 0;
  Texture* texture = texture_ref_->texture();
  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, -1, &type, &format));
  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 1000, &type, &format));
  EXPECT_FALSE(texture->GetLevelType(GL_TEXTURE_2D, 0, &type, &format));
  EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
  EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type);
  EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format);
  manager_->RemoveTexture(kClient1Id);
  EXPECT_TRUE(texture->GetLevelType(GL_TEXTURE_2D, 1, &type, &format));
  EXPECT_EQ(static_cast<GLenum>(GL_UNSIGNED_BYTE), type);
  EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format);
}

TEST_F(TextureTest, ValidForTexture) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 5, 6,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 5));
  // Check bad face.
  Texture* texture = texture_ref_->texture();
  EXPECT_FALSE(texture->ValidForTexture(
      GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 1, 0, 0, 0, 4, 5, 6));
  // Check bad level.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 0, 0, 0, 0, 4, 5, 6));
  // Check bad xoffset.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, -1, 0, 0, 4, 5, 6));
  // Check bad xoffset + width > width.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 1, 0, 0, 4, 5, 6));
  // Check bad yoffset.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, -1, 0, 4, 5, 6));
  // Check bad yoffset + height > height.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 1, 0, 4, 5, 6));
  // Check bad zoffset.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, -1, 4, 5, 6));
  // Check bad zoffset + depth > depth.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 1, 4, 5, 6));
  // Check bad width.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 0, 5, 5, 6));
  // Check bad height.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 0, 4, 6, 6));
  // Check bad depth.
  EXPECT_FALSE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 0, 4, 5, 7));
  // Check valid full size
  EXPECT_TRUE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 0, 4, 5, 6));
  // Check valid particial size.
  EXPECT_TRUE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 1, 1, 1, 2, 3, 4));
  manager_->RemoveTexture(kClient1Id);
  EXPECT_TRUE(texture->ValidForTexture(GL_TEXTURE_2D, 1, 0, 0, 0, 4, 5, 6));
}

TEST_F(TextureTest, FloatNotLinear) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_float");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  Texture* texture = texture_ref->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
                       GL_RGBA, GL_FLOAT, gfx::Rect(1, 1));
  EXPECT_FALSE(manager.CanRender(texture_ref));
  TestHelper::SetTexParameteriWithExpectations(
      gl_.get(), error_state_.get(), &manager,
      texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
  EXPECT_FALSE(manager.CanRender(texture_ref));
  TestHelper::SetTexParameteriWithExpectations(
      gl_.get(), error_state_.get(), &manager, texture_ref,
      GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
  EXPECT_TRUE(manager.CanRender(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, FloatLinear) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_float GL_OES_texture_float_linear");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  Texture* texture = texture_ref->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
                       GL_RGBA, GL_FLOAT, gfx::Rect(1, 1));
  EXPECT_TRUE(manager.CanRender(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, HalfFloatNotLinear) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_half_float");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  Texture* texture = texture_ref->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
                       GL_RGBA, GL_HALF_FLOAT_OES, gfx::Rect(1, 1));
  EXPECT_FALSE(manager.CanRender(texture_ref));
  TestHelper::SetTexParameteriWithExpectations(
      gl_.get(), error_state_.get(), &manager,
      texture_ref, GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
  EXPECT_FALSE(manager.CanRender(texture_ref));
  TestHelper::SetTexParameteriWithExpectations(
      gl_.get(), error_state_.get(), &manager, texture_ref,
      GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST, GL_NO_ERROR);
  EXPECT_TRUE(manager.CanRender(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, HalfFloatLinear) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_texture_half_float GL_OES_texture_half_float_linear");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  Texture* texture = texture_ref->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1, 0,
                       GL_RGBA, GL_HALF_FLOAT_OES, gfx::Rect(1, 1));
  EXPECT_TRUE(manager.CanRender(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, EGLImageExternal) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_OES_EGL_image_external");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_EXTERNAL_OES);
  Texture* texture = texture_ref->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target());
  EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, DepthTexture) {
  TestHelper::SetupFeatureInfoInitExpectations(
      gl_.get(), "GL_ANGLE_depth_texture");
  scoped_refptr<FeatureInfo> feature_info(new FeatureInfo());
  feature_info->InitializeForTesting();
  TextureManager manager(NULL,
                         feature_info.get(),
                         kMaxTextureSize,
                         kMaxCubeMapTextureSize,
                         kMaxRectangleTextureSize,
                         kMax3DTextureSize,
                         kUseDefaultTextures);
  manager.CreateTexture(kClient1Id, kService1Id);
  TextureRef* texture_ref = manager.GetTexture(kClient1Id);
  ASSERT_TRUE(texture_ref != NULL);
  manager.SetTarget(texture_ref, GL_TEXTURE_2D);
  manager.SetLevelInfo(texture_ref, GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 4, 4,
                       1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, gfx::Rect());
  EXPECT_FALSE(manager.CanGenerateMipmaps(texture_ref));
  manager.Destroy(false);
}

TEST_F(TextureTest, SafeUnsafe) {
  static const GLuint kClient2Id = 2;
  static const GLuint kService2Id = 12;
  static const GLuint kClient3Id = 3;
  static const GLuint kService3Id = 13;
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(0, texture->num_uncleared_mips());
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 0, true);
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 1, true);
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(2, texture->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 0, true);
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref_.get(), GL_TEXTURE_2D, 1, true);
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture->num_uncleared_mips());
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());

  manager_->CreateTexture(kClient2Id, kService2Id);
  scoped_refptr<TextureRef> texture_ref2(
      manager_->GetTexture(kClient2Id));
  ASSERT_TRUE(texture_ref2.get() != NULL);
  manager_->SetTarget(texture_ref2.get(), GL_TEXTURE_2D);
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  Texture* texture2 = texture_ref2->texture();
  EXPECT_EQ(0, texture2->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref2.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(8, 8));
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture2->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref2.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture2->num_uncleared_mips());

  manager_->CreateTexture(kClient3Id, kService3Id);
  scoped_refptr<TextureRef> texture_ref3(
      manager_->GetTexture(kClient3Id));
  ASSERT_TRUE(texture_ref3.get() != NULL);
  manager_->SetTarget(texture_ref3.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref3.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  Texture* texture3 = texture_ref3->texture();
  EXPECT_EQ(1, texture3->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref2.get(), GL_TEXTURE_2D, 0, true);
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture2->num_uncleared_mips());
  manager_->SetLevelCleared(texture_ref3.get(), GL_TEXTURE_2D, 0, true);
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture3->num_uncleared_mips());

  manager_->SetLevelInfo(texture_ref2.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(1, 1, 1, 1));
  manager_->SetLevelInfo(texture_ref3.get(), GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 4, 4, 4));
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture2->num_uncleared_mips());
  EXPECT_EQ(1, texture3->num_uncleared_mips());
  manager_->RemoveTexture(kClient3Id);
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  manager_->RemoveTexture(kClient2Id);
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id)))
      .Times(1)
      .RetiresOnSaturation();
  texture_ref2 = NULL;
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService3Id)))
      .Times(1)
      .RetiresOnSaturation();
  texture_ref3 = NULL;
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
}

TEST_F(TextureTest, ClearTexture) {
  EXPECT_CALL(*decoder_, ClearLevel(_, _, _, _, _, _, _, _, _))
      .WillRepeatedly(Return(true));
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  Texture* texture = texture_ref_->texture();
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(2, texture->num_uncleared_mips());
  manager_->ClearRenderableLevels(decoder_.get(), texture_ref_.get());
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 3));
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(2, texture->num_uncleared_mips());
  manager_->ClearTextureLevel(
      decoder_.get(), texture_ref_.get(), GL_TEXTURE_2D, 0);
  EXPECT_FALSE(texture->SafeToRenderFrom());
  EXPECT_TRUE(manager_->HaveUnsafeTextures());
  EXPECT_TRUE(manager_->HaveUnclearedMips());
  EXPECT_EQ(1, texture->num_uncleared_mips());
  manager_->ClearTextureLevel(
      decoder_.get(), texture_ref_.get(), GL_TEXTURE_2D, 1);
  EXPECT_TRUE(texture->SafeToRenderFrom());
  EXPECT_FALSE(manager_->HaveUnsafeTextures());
  EXPECT_FALSE(manager_->HaveUnclearedMips());
  EXPECT_EQ(0, texture->num_uncleared_mips());
}

TEST_F(TextureTest, UseDeletedTexture) {
  static const GLuint kClient2Id = 2;
  static const GLuint kService2Id = 12;
  // Make the default texture renderable
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  // Make a new texture
  manager_->CreateTexture(kClient2Id, kService2Id);
  scoped_refptr<TextureRef> texture_ref(
      manager_->GetTexture(kClient2Id));
  manager_->SetTarget(texture_ref.get(), GL_TEXTURE_2D);
  EXPECT_FALSE(manager_->CanRender(texture_ref.get()));
  // Remove it.
  manager_->RemoveTexture(kClient2Id);
  EXPECT_FALSE(manager_->CanRender(texture_ref.get()));
  // Check that we can still manipulate it and it effects the manager.
  manager_->SetLevelInfo(texture_ref.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_TRUE(manager_->CanRender(texture_ref.get()));
  EXPECT_CALL(*gl_, DeleteTextures(1, ::testing::Pointee(kService2Id)))
      .Times(1)
      .RetiresOnSaturation();
  texture_ref = NULL;
}

TEST_F(TextureTest, GetLevelImage) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  Texture* texture = texture_ref_->texture();
  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
  // Set image.
  scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
  manager_->SetLevelImage(texture_ref_.get(), GL_TEXTURE_2D, 1, image.get(),
                          Texture::BOUND);
  EXPECT_FALSE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
  EXPECT_TRUE(texture->GetLevelStreamTextureImage(GL_TEXTURE_2D, 1) == NULL);
  // Remove it.
  manager_->SetLevelImage(texture_ref_.get(), GL_TEXTURE_2D, 1, nullptr,
                          Texture::UNBOUND);
  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
  manager_->SetLevelImage(texture_ref_.get(), GL_TEXTURE_2D, 1, image.get(),
                          Texture::UNBOUND);
  // Image should be reset when SetLevelInfo is called.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_2D, 1) == NULL);
  EXPECT_TRUE(texture->GetLevelStreamTextureImage(GL_TEXTURE_2D, 1) == NULL);
}

TEST_F(TextureTest, GetLevelStreamTextureImage) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES, 0,
                         GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(2, 2));
  Texture* texture = texture_ref_->texture();

  // Set image.
  scoped_refptr<GLStreamTextureImage> image(new GLStreamTextureImageStub);
  manager_->SetLevelStreamTextureImage(texture_ref_.get(),
                                       GL_TEXTURE_EXTERNAL_OES, 0, image.get(),
                                       Texture::BOUND);
  EXPECT_FALSE(texture->GetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0) == NULL);
  EXPECT_FALSE(
      texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0) == NULL);
  // Replace it as a normal image.
  manager_->SetLevelImage(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES, 0,
                          image.get(), Texture::BOUND);
  EXPECT_FALSE(texture->GetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0) == NULL);
  EXPECT_TRUE(texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0) ==
              NULL);

  // Image should be reset when SetLevelInfo is called.
  manager_->SetLevelStreamTextureImage(texture_ref_.get(),
                                       GL_TEXTURE_EXTERNAL_OES, 0, image.get(),
                                       Texture::UNBOUND);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES, 0,
                         GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         gfx::Rect(2, 2));
  EXPECT_TRUE(texture->GetLevelImage(GL_TEXTURE_EXTERNAL_OES, 0) == NULL);
  EXPECT_TRUE(texture->GetLevelStreamTextureImage(GL_TEXTURE_EXTERNAL_OES, 0) ==
              NULL);
}

namespace {

bool InSet(std::set<std::string>* string_set, const std::string& str) {
  std::pair<std::set<std::string>::iterator, bool> result =
      string_set->insert(str);
  return !result.second;
}

}  // anonymous namespace

TEST_F(TextureTest, AddToSignature) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  std::string signature1;
  std::string signature2;
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature1);

  std::set<std::string> string_set;
  EXPECT_FALSE(InSet(&string_set, signature1));

  // check changing 1 thing makes a different signature.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 4, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(4, 2));
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  // check putting it back makes the same signature.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect(2, 2));
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_EQ(signature1, signature2);

  // Check setting cleared status does not change signature.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_EQ(signature1, signature2);

  // Check changing other settings changes signature.
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 4, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 2,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         1, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGB, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_FLOAT, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  // put it back
  manager_->SetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 1,
                         0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_EQ(signature1, signature2);

  // check changing parameters changes signature.
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MIN_FILTER, GL_NEAREST, GL_NO_ERROR);
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  SetParameter(texture_ref_.get(),
               GL_TEXTURE_MIN_FILTER,
               GL_NEAREST_MIPMAP_LINEAR,
               GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_NEAREST, GL_NO_ERROR);
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  SetParameter(
      texture_ref_.get(), GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  SetParameter(texture_ref_.get(), GL_TEXTURE_WRAP_S, GL_REPEAT, GL_NO_ERROR);
  SetParameter(
      texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE, GL_NO_ERROR);
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_FALSE(InSet(&string_set, signature2));

  // Check putting it back genenerates the same signature
  SetParameter(texture_ref_.get(), GL_TEXTURE_WRAP_T, GL_REPEAT, GL_NO_ERROR);
  signature2.clear();
  manager_->AddToSignature(texture_ref_.get(), GL_TEXTURE_2D, 1, &signature2);
  EXPECT_EQ(signature1, signature2);

  // Check the set was acutally getting different signatures.
  EXPECT_EQ(11u, string_set.size());
}

class ProduceConsumeTextureTest : public TextureTest,
                                  public ::testing::WithParamInterface<GLenum> {
 public:
  void SetUp() override {
    TextureTest::SetUpBase(NULL, "GL_OES_EGL_image_external");
    manager_->CreateTexture(kClient2Id, kService2Id);
    texture2_ = manager_->GetTexture(kClient2Id);

    EXPECT_CALL(*decoder_.get(), GetErrorState())
      .WillRepeatedly(Return(error_state_.get()));
  }

  void TearDown() override {
    if (texture2_.get()) {
      // If it's not in the manager then setting texture2_ to NULL will
      // delete the texture.
      if (!texture2_->client_id()) {
        // Check that it gets deleted when the last reference is released.
        EXPECT_CALL(
            *gl_,
            DeleteTextures(1, ::testing::Pointee(texture2_->service_id())))
            .Times(1).RetiresOnSaturation();
      }
      texture2_ = NULL;
    }
    TextureTest::TearDown();
  }

 protected:
  struct LevelInfo {
    LevelInfo(GLenum target,
              GLenum format,
              GLsizei width,
              GLsizei height,
              GLsizei depth,
              GLint border,
              GLenum type,
              const gfx::Rect& cleared_rect)
        : target(target),
          format(format),
          width(width),
          height(height),
          depth(depth),
          border(border),
          type(type),
          cleared_rect(cleared_rect) {}

    LevelInfo()
        : target(0),
          format(0),
          width(-1),
          height(-1),
          depth(1),
          border(0),
          type(0) {}

    bool operator==(const LevelInfo& other) const {
      return target == other.target && format == other.format &&
             width == other.width && height == other.height &&
             depth == other.depth && border == other.border &&
             type == other.type && cleared_rect == other.cleared_rect;
    }

    GLenum target;
    GLenum format;
    GLsizei width;
    GLsizei height;
    GLsizei depth;
    GLint border;
    GLenum type;
    gfx::Rect cleared_rect;
  };

  void SetLevelInfo(TextureRef* texture_ref,
                    GLint level,
                    const LevelInfo& info) {
    manager_->SetLevelInfo(texture_ref, info.target, level, info.format,
                           info.width, info.height, info.depth, info.border,
                           info.format, info.type, info.cleared_rect);
  }

  static LevelInfo GetLevelInfo(const TextureRef* texture_ref,
                                GLint target,
                                GLint level) {
    const Texture* texture = texture_ref->texture();
    LevelInfo info;
    info.target = target;
    EXPECT_TRUE(texture->GetLevelSize(target, level, &info.width,
                                      &info.height, &info.depth));
    EXPECT_TRUE(texture->GetLevelType(target, level, &info.type,
                                      &info.format));
    info.cleared_rect = texture->GetLevelClearedRect(target, level);
    return info;
  }

  Texture* Produce(TextureRef* texture_ref) {
    Texture* texture = manager_->Produce(texture_ref);
    EXPECT_TRUE(texture != NULL);
    return texture;
  }

  void Consume(GLuint client_id, Texture* texture) {
    EXPECT_TRUE(manager_->Consume(client_id, texture));
  }

  scoped_refptr<TextureRef> texture2_;

 private:
  static const GLuint kClient2Id;
  static const GLuint kService2Id;
};

const GLuint ProduceConsumeTextureTest::kClient2Id = 2;
const GLuint ProduceConsumeTextureTest::kService2Id = 12;

TEST_F(ProduceConsumeTextureTest, ProduceConsume2D) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_2D);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D), texture->target());
  LevelInfo level0(GL_TEXTURE_2D, GL_RGBA, 4, 4, 1, 0, GL_UNSIGNED_BYTE,
                   gfx::Rect(4, 4));
  SetLevelInfo(texture_ref_.get(), 0, level0);
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture_ref_.get());
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  LevelInfo level1 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 1);
  LevelInfo level2 = GetLevelInfo(texture_ref_.get(), GL_TEXTURE_2D, 2);
  Texture* produced_texture = Produce(texture_ref_.get());
  EXPECT_EQ(produced_texture, texture);

  // Make this texture bigger with more levels, and make sure they get
  // clobbered correctly during Consume().
  manager_->SetTarget(texture2_.get(), GL_TEXTURE_2D);
  SetLevelInfo(texture2_.get(), 0, LevelInfo(GL_TEXTURE_2D, GL_RGBA, 16, 16, 1,
                                             0, GL_UNSIGNED_BYTE, gfx::Rect()));
  EXPECT_TRUE(manager_->CanGenerateMipmaps(texture_ref_.get()));
  manager_->MarkMipmapsGenerated(texture2_.get());
  texture = texture2_->texture();
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  EXPECT_EQ(1024U + 256U + 64U + 16U + 4U, texture->estimated_size());

  GLuint client_id = texture2_->client_id();
  manager_->RemoveTexture(client_id);
  Consume(client_id, produced_texture);
  scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id);
  EXPECT_EQ(produced_texture, restored_texture->texture());
  EXPECT_EQ(level0, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 0));
  EXPECT_EQ(level1, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 1));
  EXPECT_EQ(level2, GetLevelInfo(restored_texture.get(), GL_TEXTURE_2D, 2));
  texture = restored_texture->texture();
  EXPECT_EQ(64U + 16U + 4U, texture->estimated_size());
  GLint w, h;
  EXPECT_FALSE(texture->GetLevelSize(GL_TEXTURE_2D, 3, &w, &h, nullptr));

  // However the old texture ref still exists if it was referenced somewhere.
  EXPECT_EQ(1024U + 256U + 64U + 16U + 4U,
            texture2_->texture()->estimated_size());
}

TEST_F(ProduceConsumeTextureTest, ProduceConsumeClearRectangle) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_RECTANGLE_ARB);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB), texture->target());
  LevelInfo level0(GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, 1, 1, 1, 0,
                   GL_UNSIGNED_BYTE, gfx::Rect());
  SetLevelInfo(texture_ref_.get(), 0, level0);
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  Texture* produced_texture = Produce(texture_ref_.get());
  EXPECT_EQ(produced_texture, texture);
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_RECTANGLE_ARB),
            produced_texture->target());

  GLuint client_id = texture2_->client_id();
  manager_->RemoveTexture(client_id);
  Consume(client_id, produced_texture);
  scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id);
  EXPECT_EQ(produced_texture, restored_texture->texture());

  // See if we can clear the previously uncleared level now.
  EXPECT_EQ(level0,
            GetLevelInfo(restored_texture.get(), GL_TEXTURE_RECTANGLE_ARB, 0));
  EXPECT_CALL(*decoder_, ClearLevel(_, _, _, _, _, _, _, _, _))
      .WillRepeatedly(Return(true));
  EXPECT_TRUE(manager_->ClearTextureLevel(
      decoder_.get(), restored_texture.get(), GL_TEXTURE_RECTANGLE_ARB, 0));
}

TEST_F(ProduceConsumeTextureTest, ProduceConsumeExternal) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_EXTERNAL_OES);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES), texture->target());
  LevelInfo level0(GL_TEXTURE_EXTERNAL_OES, GL_RGBA, 1, 1, 1, 0,
                   GL_UNSIGNED_BYTE, gfx::Rect());
  SetLevelInfo(texture_ref_.get(), 0, level0);
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  Texture* produced_texture = Produce(texture_ref_.get());
  EXPECT_EQ(produced_texture, texture);

  GLuint client_id = texture2_->client_id();
  manager_->RemoveTexture(client_id);
  Consume(client_id, produced_texture);
  scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id);
  EXPECT_EQ(produced_texture, restored_texture->texture());
  EXPECT_EQ(level0,
            GetLevelInfo(restored_texture.get(), GL_TEXTURE_EXTERNAL_OES, 0));
}

TEST_P(ProduceConsumeTextureTest, ProduceConsumeTextureWithImage) {
  GLenum target = GetParam();
  manager_->SetTarget(texture_ref_.get(), target);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(target), texture->target());
  scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
  manager_->SetLevelInfo(texture_ref_.get(), target, 0, GL_RGBA, 0, 0, 1, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  manager_->SetLevelImage(texture_ref_.get(), target, 0, image.get(),
                          Texture::BOUND);
  GLuint service_id = texture->service_id();
  Texture* produced_texture = Produce(texture_ref_.get());

  GLuint client_id = texture2_->client_id();
  manager_->RemoveTexture(client_id);
  Consume(client_id, produced_texture);
  scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id);
  EXPECT_EQ(produced_texture, restored_texture->texture());
  EXPECT_EQ(service_id, restored_texture->service_id());
  EXPECT_EQ(image.get(), restored_texture->texture()->GetLevelImage(target, 0));
}

static const GLenum kTextureTargets[] = {GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES,
                                         GL_TEXTURE_RECTANGLE_ARB, };

INSTANTIATE_TEST_CASE_P(Target,
                        ProduceConsumeTextureTest,
                        ::testing::ValuesIn(kTextureTargets));

TEST_F(ProduceConsumeTextureTest, ProduceConsumeCube) {
  manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP);
  Texture* texture = texture_ref_->texture();
  EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP), texture->target());
  LevelInfo face0(GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_RGBA, 1, 1, 1, 0,
                  GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  LevelInfo face5(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, GL_RGBA, 3, 3, 1, 0,
                  GL_UNSIGNED_BYTE, gfx::Rect(1, 1));
  SetLevelInfo(texture_ref_.get(), 0, face0);
  SetLevelInfo(texture_ref_.get(), 0, face5);
  EXPECT_TRUE(TextureTestHelper::IsTextureComplete(texture));
  Texture* produced_texture = Produce(texture_ref_.get());
  EXPECT_EQ(produced_texture, texture);

  GLuint client_id = texture2_->client_id();
  manager_->RemoveTexture(client_id);
  Consume(client_id, produced_texture);
  scoped_refptr<TextureRef> restored_texture = manager_->GetTexture(client_id);
  EXPECT_EQ(produced_texture, restored_texture->texture());
  EXPECT_EQ(
      face0,
      GetLevelInfo(restored_texture.get(), GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0));
  EXPECT_EQ(
      face5,
      GetLevelInfo(restored_texture.get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0));
}

class CountingMemoryTracker : public MemoryTracker {
 public:
  CountingMemoryTracker() {
    current_size_ = 0;
  }

  void TrackMemoryAllocatedChange(size_t old_size,
                                  size_t new_size) override {
    current_size_ += new_size - old_size;
  }

  bool EnsureGPUMemoryAvailable(size_t size_needed) override { return true; }

  size_t GetSize() {
    return current_size_;
  }

  uint64_t ClientTracingId() const override { return 0; }

  int ClientId() const override { return 0; }

  uint64_t ShareGroupTracingGUID() const override { return 0; }

 private:
  ~CountingMemoryTracker() override {}

  size_t current_size_;
  DISALLOW_COPY_AND_ASSIGN(CountingMemoryTracker);
};

class SharedTextureTest : public GpuServiceTest {
 public:
  static const bool kUseDefaultTextures = false;

  SharedTextureTest() : feature_info_(new FeatureInfo()) {}

  ~SharedTextureTest() override {}

  void SetUp() override {
    GpuServiceTest::SetUp();
    memory_tracker1_ = new CountingMemoryTracker;
    texture_manager1_.reset(
        new TextureManager(memory_tracker1_.get(),
                           feature_info_.get(),
                           TextureManagerTest::kMaxTextureSize,
                           TextureManagerTest::kMaxCubeMapTextureSize,
                           TextureManagerTest::kMaxRectangleTextureSize,
                           TextureManagerTest::kMax3DTextureSize,
                           kUseDefaultTextures));
    memory_tracker2_ = new CountingMemoryTracker;
    texture_manager2_.reset(
        new TextureManager(memory_tracker2_.get(),
                           feature_info_.get(),
                           TextureManagerTest::kMaxTextureSize,
                           TextureManagerTest::kMaxCubeMapTextureSize,
                           TextureManagerTest::kMaxRectangleTextureSize,
                           TextureManagerTest::kMax3DTextureSize,
                           kUseDefaultTextures));
    TestHelper::SetupTextureManagerInitExpectations(
        gl_.get(), false, false, "", kUseDefaultTextures);
    texture_manager1_->Initialize();
    TestHelper::SetupTextureManagerInitExpectations(
        gl_.get(), false, false, "", kUseDefaultTextures);
    texture_manager2_->Initialize();
  }

  void TearDown() override {
    texture_manager2_->Destroy(false);
    texture_manager2_.reset();
    texture_manager1_->Destroy(false);
    texture_manager1_.reset();
    GpuServiceTest::TearDown();
  }

 protected:
  scoped_refptr<FeatureInfo> feature_info_;
  scoped_refptr<CountingMemoryTracker> memory_tracker1_;
  scoped_ptr<TextureManager> texture_manager1_;
  scoped_refptr<CountingMemoryTracker> memory_tracker2_;
  scoped_ptr<TextureManager> texture_manager2_;
};

TEST_F(SharedTextureTest, DeleteTextures) {
  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
  scoped_refptr<TextureRef> ref2 =
      texture_manager2_->Consume(20, ref1->texture());
  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(0);
  ref1 = NULL;
  texture_manager1_->RemoveTexture(10);
  testing::Mock::VerifyAndClearExpectations(gl_.get());

  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(1)
      .RetiresOnSaturation();
  ref2 = NULL;
  texture_manager2_->RemoveTexture(20);
  testing::Mock::VerifyAndClearExpectations(gl_.get());
}

TEST_F(SharedTextureTest, TextureSafetyAccounting) {
  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());

  // Newly created texture is renderable.
  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());

  // Associate new texture ref to other texture manager, should account for it
  // too.
  scoped_refptr<TextureRef> ref2 =
      texture_manager2_->Consume(20, ref1->texture());
  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());

  // Make texture renderable but uncleared on one texture manager, should affect
  // other one.
  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());

  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());
  EXPECT_TRUE(texture_manager1_->HaveUnsafeTextures());
  EXPECT_TRUE(texture_manager1_->HaveUnclearedMips());
  EXPECT_TRUE(texture_manager2_->HaveUnsafeTextures());
  EXPECT_TRUE(texture_manager2_->HaveUnclearedMips());

  // Make texture cleared on one texture manager, should affect other one.
  texture_manager1_->SetLevelCleared(ref1.get(), GL_TEXTURE_2D, 0, true);
  EXPECT_FALSE(texture_manager1_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager1_->HaveUnclearedMips());
  EXPECT_FALSE(texture_manager2_->HaveUnsafeTextures());
  EXPECT_FALSE(texture_manager2_->HaveUnclearedMips());

  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(1)
      .RetiresOnSaturation();
  texture_manager1_->RemoveTexture(10);
  texture_manager2_->RemoveTexture(20);
}

TEST_F(SharedTextureTest, FBOCompletenessCheck) {
  const GLenum kCompleteValue = GL_FRAMEBUFFER_COMPLETE;
  FramebufferManager framebuffer_manager1(1, 1, CONTEXT_TYPE_OPENGLES2,
                                          nullptr);
  texture_manager1_->set_framebuffer_manager(&framebuffer_manager1);
  FramebufferManager framebuffer_manager2(1, 1, CONTEXT_TYPE_OPENGLES2,
                                          nullptr);
  texture_manager2_->set_framebuffer_manager(&framebuffer_manager2);

  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
  framebuffer_manager1.CreateFramebuffer(10, 10);
  scoped_refptr<Framebuffer> framebuffer1 =
      framebuffer_manager1.GetFramebuffer(10);
  framebuffer1->AttachTexture(
      GL_COLOR_ATTACHMENT0, ref1.get(), GL_TEXTURE_2D, 0, 0);
  EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1.get()));
  EXPECT_NE(kCompleteValue,
            framebuffer1->IsPossiblyComplete(feature_info_.get()));

  // Make FBO complete in manager 1.
  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                  gfx::Rect(1, 1));
  EXPECT_EQ(kCompleteValue,
            framebuffer1->IsPossiblyComplete(feature_info_.get()));
  framebuffer_manager1.MarkAsComplete(framebuffer1.get());
  EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1.get()));

  // Share texture with manager 2.
  scoped_refptr<TextureRef> ref2 =
      texture_manager2_->Consume(20, ref1->texture());
  framebuffer_manager2.CreateFramebuffer(20, 20);
  scoped_refptr<Framebuffer> framebuffer2 =
      framebuffer_manager2.GetFramebuffer(20);
  framebuffer2->AttachTexture(
      GL_COLOR_ATTACHMENT0, ref2.get(), GL_TEXTURE_2D, 0, 0);
  EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2.get()));
  EXPECT_EQ(kCompleteValue,
            framebuffer2->IsPossiblyComplete(feature_info_.get()));
  framebuffer_manager2.MarkAsComplete(framebuffer2.get());
  EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2.get()));

  // Change level for texture, both FBOs should be marked incomplete
  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                  gfx::Rect(1, 1));
  EXPECT_FALSE(framebuffer_manager1.IsComplete(framebuffer1.get()));
  EXPECT_EQ(kCompleteValue,
            framebuffer1->IsPossiblyComplete(feature_info_.get()));
  framebuffer_manager1.MarkAsComplete(framebuffer1.get());
  EXPECT_TRUE(framebuffer_manager1.IsComplete(framebuffer1.get()));
  EXPECT_FALSE(framebuffer_manager2.IsComplete(framebuffer2.get()));
  EXPECT_EQ(kCompleteValue,
            framebuffer2->IsPossiblyComplete(feature_info_.get()));
  framebuffer_manager2.MarkAsComplete(framebuffer2.get());
  EXPECT_TRUE(framebuffer_manager2.IsComplete(framebuffer2.get()));

  EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, _))
      .Times(2)
      .RetiresOnSaturation();
  framebuffer_manager1.RemoveFramebuffer(10);
  framebuffer_manager2.RemoveFramebuffer(20);
  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(1)
      .RetiresOnSaturation();
  texture_manager1_->RemoveTexture(10);
  texture_manager2_->RemoveTexture(20);
}

TEST_F(SharedTextureTest, Memory) {
  size_t initial_memory1 = memory_tracker1_->GetSize();
  size_t initial_memory2 = memory_tracker2_->GetSize();

  // Newly created texture is unrenderable.
  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 0, GL_RGBA, 10, 10,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gfx::Rect());

  EXPECT_LT(0u, ref1->texture()->estimated_size());
  EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(),
            memory_tracker1_->GetSize());

  // Associate new texture ref to other texture manager, it doesn't account for
  // the texture memory, the first memory tracker still has it.
  scoped_refptr<TextureRef> ref2 =
      texture_manager2_->Consume(20, ref1->texture());
  EXPECT_EQ(initial_memory1 + ref1->texture()->estimated_size(),
            memory_tracker1_->GetSize());
  EXPECT_EQ(initial_memory2, memory_tracker2_->GetSize());

  // Delete the texture, memory should go to the remaining tracker.
  texture_manager1_->RemoveTexture(10);
  ref1 = NULL;
  EXPECT_EQ(initial_memory1,
            memory_tracker1_->GetSize());
  EXPECT_EQ(initial_memory2 + ref2->texture()->estimated_size(),
            memory_tracker2_->GetSize());

  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(1)
      .RetiresOnSaturation();
  ref2 = NULL;
  texture_manager2_->RemoveTexture(20);
  EXPECT_EQ(initial_memory2,
            memory_tracker2_->GetSize());
}

TEST_F(SharedTextureTest, Images) {
  scoped_refptr<TextureRef> ref1 = texture_manager1_->CreateTexture(10, 10);
  scoped_refptr<TextureRef> ref2 =
      texture_manager2_->Consume(20, ref1->texture());

  texture_manager1_->SetTarget(ref1.get(), GL_TEXTURE_2D);
  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                  gfx::Rect(2, 2));
  EXPECT_FALSE(ref1->texture()->HasImages());
  EXPECT_FALSE(ref2->texture()->HasImages());
  EXPECT_FALSE(texture_manager1_->HaveImages());
  EXPECT_FALSE(texture_manager2_->HaveImages());
  scoped_refptr<gl::GLImage> image1(new gl::GLImageStub);
  texture_manager1_->SetLevelImage(ref1.get(), GL_TEXTURE_2D, 1, image1.get(),
                                   Texture::BOUND);
  EXPECT_TRUE(ref1->texture()->HasImages());
  EXPECT_TRUE(ref2->texture()->HasImages());
  EXPECT_TRUE(texture_manager1_->HaveImages());
  EXPECT_TRUE(texture_manager2_->HaveImages());
  scoped_refptr<gl::GLImage> image2(new gl::GLImageStub);
  texture_manager1_->SetLevelImage(ref1.get(), GL_TEXTURE_2D, 1, image2.get(),
                                   Texture::BOUND);
  EXPECT_TRUE(ref1->texture()->HasImages());
  EXPECT_TRUE(ref2->texture()->HasImages());
  EXPECT_TRUE(texture_manager1_->HaveImages());
  EXPECT_TRUE(texture_manager2_->HaveImages());
  texture_manager1_->SetLevelInfo(ref1.get(), GL_TEXTURE_2D, 1, GL_RGBA, 2, 2,
                                  1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                                  gfx::Rect(2, 2));
  EXPECT_FALSE(ref1->texture()->HasImages());
  EXPECT_FALSE(ref2->texture()->HasImages());
  EXPECT_FALSE(texture_manager1_->HaveImages());
  EXPECT_FALSE(texture_manager1_->HaveImages());

  EXPECT_CALL(*gl_, DeleteTextures(1, _))
      .Times(1)
      .RetiresOnSaturation();
  texture_manager1_->RemoveTexture(10);
  texture_manager2_->RemoveTexture(20);
}

class TextureFormatTypeValidationTest : public TextureManagerTest {
 public:
  TextureFormatTypeValidationTest() {}
  ~TextureFormatTypeValidationTest() override {}

 protected:
  void ExpectValid(
      bool tex_image_call, GLenum format, GLenum type, GLint internal_format) {
    EXPECT_TRUE(manager_->ValidateTextureParameters(
        error_state_.get(), "", tex_image_call,
        format, type, internal_format, 0));
  }

  void ExpectInvalid(
      bool tex_image_call, GLenum format, GLenum type, GLint internal_format) {
    EXPECT_CALL(*error_state_,
                SetGLError(_, _, _, _, _))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_FALSE(manager_->ValidateTextureParameters(
        error_state_.get(), "", tex_image_call,
        format, type, internal_format, 0));
  }

  void ExpectInvalidEnum(
      bool tex_image_call, GLenum format, GLenum type, GLint internal_format) {
    EXPECT_CALL(*error_state_,
                SetGLErrorInvalidEnum(_, _, _, _, _))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_FALSE(manager_->ValidateTextureParameters(
        error_state_.get(), "", tex_image_call,
        format, type, internal_format, 0));
  }
};

TEST_F(TextureFormatTypeValidationTest, ES2Basic) {
  SetupFeatureInfo("", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_ALPHA, GL_UNSIGNED_BYTE, GL_ALPHA);
  ExpectValid(true, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GL_LUMINANCE_ALPHA);

  ExpectInvalid(true, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GL_ALPHA);

  // float / half_float.
  ExpectInvalidEnum(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectInvalidEnum(true, GL_RGBA, GL_HALF_FLOAT_OES, GL_RGBA);

  // GL_EXT_bgra
  ExpectInvalidEnum(true, GL_BGRA_EXT, GL_UNSIGNED_BYTE, GL_BGRA_EXT);

  // depth / stencil
  ExpectInvalidEnum(
      true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT);
  ExpectInvalidEnum(
      true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL);

  // SRGB
  ExpectInvalidEnum(true, GL_SRGB_EXT, GL_UNSIGNED_BYTE, GL_SRGB_EXT);
  ExpectInvalidEnum(
      true, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, GL_SRGB_ALPHA_EXT);

  // ES3
  ExpectInvalid(true, GL_RGB, GL_UNSIGNED_BYTE, GL_RGB8);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithExtTextureFormatBGRA8888) {
  SetupFeatureInfo("GL_EXT_texture_format_BGRA8888", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_BGRA_EXT, GL_UNSIGNED_BYTE, GL_BGRA_EXT);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithAppleTextureFormatBGRA8888) {
  SetupFeatureInfo("GL_APPLE_texture_format_BGRA8888", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_BGRA_EXT, GL_UNSIGNED_BYTE, GL_BGRA_EXT);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithArbDepth) {
  SetupFeatureInfo("GL_ARB_depth_texture", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_DEPTH_COMPONENT);
  ExpectInvalidEnum(
      true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithOesDepth) {
  SetupFeatureInfo("GL_OES_depth_texture", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_DEPTH_COMPONENT);
  ExpectInvalidEnum(
      true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithAngleDepth) {
  SetupFeatureInfo("GL_ANGLE_depth_texture", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_DEPTH_COMPONENT);
  ExpectInvalidEnum(
      true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithExtPackedDepthStencil) {
  SetupFeatureInfo(
      "GL_EXT_packed_depth_stencil GL_ARB_depth_texture",
      "OpenGL ES 2.0",
      false);

  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_DEPTH_COMPONENT);
  ExpectValid(true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithRGWithFloat) {
  SetupFeatureInfo(
      "GL_EXT_texture_rg GL_OES_texture_float GL_OES_texture_half_float",
      "OpenGL ES 2.0",
      false);

  ExpectValid(true, GL_RED_EXT, GL_HALF_FLOAT_OES, GL_RED_EXT);
  ExpectValid(true, GL_RG_EXT, GL_HALF_FLOAT_OES, GL_RG_EXT);
  ExpectValid(true, GL_RED_EXT, GL_UNSIGNED_BYTE, GL_RED_EXT);
  ExpectValid(true, GL_RG_EXT, GL_UNSIGNED_BYTE, GL_RG_EXT);

  ExpectInvalidEnum(true, GL_RED_EXT, GL_BYTE, GL_RED_EXT);
  ExpectInvalidEnum(true, GL_RG_EXT, GL_BYTE, GL_RG_EXT);
  ExpectInvalidEnum(true, GL_RED_EXT, GL_SHORT, GL_RED_EXT);
  ExpectInvalidEnum(true, GL_RG_EXT, GL_SHORT, GL_RG_EXT);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithRGNoFloat) {
  SetupFeatureInfo("GL_ARB_texture_rg", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_RED_EXT, GL_UNSIGNED_BYTE, GL_RED_EXT);
  ExpectValid(true, GL_RG_EXT, GL_UNSIGNED_BYTE, GL_RG_EXT);

  ExpectInvalidEnum(true, GL_RED_EXT, GL_HALF_FLOAT_OES, GL_RED_EXT);
  ExpectInvalidEnum(true, GL_RG_EXT, GL_HALF_FLOAT_OES, GL_RG_EXT);
}

TEST_F(TextureFormatTypeValidationTest, ES2OnTopOfES3) {
  SetupFeatureInfo("", "OpenGL ES 3.0", false);

  ExpectInvalidEnum(true, GL_RGB, GL_FLOAT, GL_RGB);
  ExpectInvalidEnum(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectInvalidEnum(true, GL_LUMINANCE, GL_FLOAT, GL_LUMINANCE);
  ExpectInvalidEnum(true, GL_LUMINANCE_ALPHA, GL_FLOAT, GL_LUMINANCE_ALPHA);
  ExpectInvalidEnum(true, GL_ALPHA, GL_FLOAT, GL_ALPHA);

  ExpectInvalidEnum(true, GL_SRGB_EXT, GL_UNSIGNED_BYTE, GL_SRGB_EXT);
  ExpectInvalidEnum(
      true, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, GL_SRGB_ALPHA_EXT);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithOesTextureFloat) {
  SetupFeatureInfo("GL_OES_texture_float", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_RGB, GL_FLOAT, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_FLOAT, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_FLOAT, GL_LUMINANCE_ALPHA);
  ExpectValid(true, GL_ALPHA, GL_FLOAT, GL_ALPHA);

  ExpectInvalidEnum(true, GL_RGB, GL_HALF_FLOAT_OES, GL_RGB);
  ExpectInvalidEnum(true, GL_RGBA, GL_HALF_FLOAT_OES, GL_RGBA);
  ExpectInvalidEnum(true, GL_LUMINANCE, GL_HALF_FLOAT_OES, GL_LUMINANCE);
  ExpectInvalidEnum(
      true, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES, GL_LUMINANCE_ALPHA);
  ExpectInvalidEnum(true, GL_ALPHA, GL_HALF_FLOAT_OES, GL_ALPHA);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithOesTextureFloatLinear) {
  SetupFeatureInfo(
      "GL_OES_texture_float GL_OES_texture_float_linear",
      "OpenGL ES 2.0",
      false);

  ExpectValid(true, GL_RGB, GL_FLOAT, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_FLOAT, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_FLOAT, GL_LUMINANCE_ALPHA);
  ExpectValid(true, GL_ALPHA, GL_FLOAT, GL_ALPHA);

  ExpectInvalidEnum(true, GL_RGB, GL_HALF_FLOAT_OES, GL_RGB);
  ExpectInvalidEnum(true, GL_RGBA, GL_HALF_FLOAT_OES, GL_RGBA);
  ExpectInvalidEnum(true, GL_LUMINANCE, GL_HALF_FLOAT_OES, GL_LUMINANCE);
  ExpectInvalidEnum(
      true, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES, GL_LUMINANCE_ALPHA);
  ExpectInvalidEnum(true, GL_ALPHA, GL_HALF_FLOAT_OES, GL_ALPHA);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithOesTextureHalfFloat) {
  SetupFeatureInfo("GL_OES_texture_half_float", "OpenGL ES 2.0", false);

  ExpectValid(true, GL_RGB, GL_HALF_FLOAT_OES, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_HALF_FLOAT_OES, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_HALF_FLOAT_OES, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES, GL_LUMINANCE_ALPHA);
  ExpectValid(true, GL_ALPHA, GL_HALF_FLOAT_OES, GL_ALPHA);

  ExpectInvalidEnum(true, GL_RGB, GL_FLOAT, GL_RGB);
  ExpectInvalidEnum(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectInvalidEnum(true, GL_LUMINANCE, GL_FLOAT, GL_LUMINANCE);
  ExpectInvalidEnum(true, GL_LUMINANCE_ALPHA, GL_FLOAT, GL_LUMINANCE_ALPHA);
  ExpectInvalidEnum(true, GL_ALPHA, GL_FLOAT, GL_ALPHA);
}

TEST_F(TextureFormatTypeValidationTest, ES2WithOesTextureHalfFloatLinear) {
  SetupFeatureInfo(
      "GL_OES_texture_half_float GL_OES_texture_half_float_linear",
      "OpenGL ES 2.0",
      false);

  ExpectValid(true, GL_RGB, GL_HALF_FLOAT_OES, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_HALF_FLOAT_OES, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_HALF_FLOAT_OES, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_HALF_FLOAT_OES, GL_LUMINANCE_ALPHA);
  ExpectValid(true, GL_ALPHA, GL_HALF_FLOAT_OES, GL_ALPHA);

  ExpectInvalidEnum(true, GL_RGB, GL_FLOAT, GL_RGB);
  ExpectInvalidEnum(true, GL_RGBA, GL_FLOAT, GL_RGBA);
  ExpectInvalidEnum(true, GL_LUMINANCE, GL_FLOAT, GL_LUMINANCE);
  ExpectInvalidEnum(true, GL_LUMINANCE_ALPHA, GL_FLOAT, GL_LUMINANCE_ALPHA);
  ExpectInvalidEnum(true, GL_ALPHA, GL_FLOAT, GL_ALPHA);
}

TEST_F(TextureFormatTypeValidationTest, ES3Basic) {
  SetupFeatureInfo("", "OpenGL ES 3.0", true);

  ExpectValid(true, GL_ALPHA, GL_UNSIGNED_BYTE, GL_ALPHA);
  ExpectValid(true, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_RGB);
  ExpectValid(true, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, GL_RGBA);
  ExpectValid(true, GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_LUMINANCE);
  ExpectValid(true, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, GL_LUMINANCE_ALPHA);

  ExpectValid(true, GL_RG, GL_BYTE, GL_RG8_SNORM);
  ExpectValid(true, GL_RG_INTEGER, GL_UNSIGNED_INT, GL_RG32UI);
  ExpectValid(true, GL_RG_INTEGER, GL_SHORT, GL_RG16I);
  ExpectValid(true, GL_RGB, GL_UNSIGNED_BYTE, GL_SRGB8);
  ExpectValid(true, GL_RGBA, GL_HALF_FLOAT, GL_RGBA16F);
  ExpectValid(true, GL_RGBA, GL_FLOAT, GL_RGBA16F);
  ExpectValid(true, GL_RGBA, GL_FLOAT, GL_RGBA32F);

  ExpectValid(
      true, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_COMPONENT16);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, GL_DEPTH_COMPONENT24);
  ExpectValid(true, GL_DEPTH_COMPONENT, GL_FLOAT, GL_DEPTH_COMPONENT32F);
  ExpectValid(
      true, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH24_STENCIL8);
  ExpectValid(true, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
              GL_DEPTH32F_STENCIL8);

  ExpectInvalid(true, GL_RGB_INTEGER, GL_INT, GL_RGBA8);
}

}  // namespace gles2
}  // namespace gpu