// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/service/error_state_mock.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h"
#include "gpu/command_buffer/service/gles2_query_manager.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
#include "gpu/command_buffer/service/gpu_tracer.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/gpu_timing.h"
#include "ui/gl/gpu_timing_fake.h"

using ::testing::_;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::SetArgPointee;

namespace gpu {
namespace gles2 {

class QueryManagerTest : public GpuServiceTest {
 public:
  static const uint32_t kSharedMemoryOffset = 132;
  static const uint32_t kSharedMemory2Offset = 232;
  static const size_t kSharedBufferSize = 2048;
  static const int32_t kInvalidSharedMemoryId = -1;
  static const uint32_t kInvalidSharedMemoryOffset = kSharedBufferSize + 1;
  static const uint32_t kInitialResult = 0xBDBDBDBDu;
  static const uint8_t kInitialMemoryValue = 0xBDu;

  QueryManagerTest() = default;
  ~QueryManagerTest() override = default;

 protected:
  void SetUp() override {
    GpuServiceTest::SetUpWithGLVersion("OpenGL ES 3.0", "");
    SetUpMockGL("");
  }

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

  void SetUpMockGL(const char* extension_expectations) {
    command_buffer_service_ = std::make_unique<FakeCommandBufferServiceBase>();
    scoped_refptr<gpu::Buffer> buffer =
        command_buffer_service_->CreateTransferBufferHelper(kSharedBufferSize,
                                                            &shared_memory_id_);
    UNSAFE_TODO(
        memset(buffer->memory(), kInitialMemoryValue, kSharedBufferSize));
    buffer = command_buffer_service_->CreateTransferBufferHelper(
        kSharedBufferSize, &shared_memory2_id_);
    UNSAFE_TODO(
        memset(buffer->memory(), kInitialMemoryValue, kSharedBufferSize));
    decoder_ = std::make_unique<MockGLES2Decoder>(
        &client_, command_buffer_service_.get(), &outputter_);
    TestHelper::SetupFeatureInfoInitExpectations(
        gl_.get(), extension_expectations);
    EXPECT_CALL(*decoder_.get(), GetGLContext())
      .WillRepeatedly(Return(GetGLContext()));
    auto feature_info = base::MakeRefCounted<FeatureInfo>();
    feature_info->InitializeForTesting();
    manager_ =
        std::make_unique<GLES2QueryManager>(decoder_.get(), feature_info.get());
  }

  QueryManager::Query* CreateQuery(GLenum target,
                                   GLuint client_id,
                                   int32_t shm_id,
                                   uint32_t shm_offset,
                                   GLuint service_id) {
    return CreateQueryOnManager(manager_.get(), target, client_id, shm_id,
                                shm_offset, service_id);
  }

  QueryManager::Query* CreateQueryOnManager(QueryManager* manager,
                                            GLenum target,
                                            GLuint client_id,
                                            int32_t shm_id,
                                            uint32_t shm_offset,
                                            GLuint service_id) {
    if (service_id) {
      EXPECT_CALL(*gl_, GenQueries(1, _))
          .WillOnce(SetArgPointee<1>(service_id))
          .RetiresOnSaturation();
    }
    scoped_refptr<gpu::Buffer> buffer = decoder_->GetSharedMemoryBuffer(shm_id);
    if (!buffer)
      return nullptr;
    QuerySync* sync = static_cast<QuerySync*>(
        buffer->GetDataAddress(shm_offset, sizeof(QuerySync)));
    if (!sync)
      return nullptr;
    return manager->CreateQuery(target, client_id, std::move(buffer), sync);
  }

  void QueueQuery(QueryManager::Query* query,
                  GLuint service_id,
                  base::subtle::Atomic32 submit_count) {
    EXPECT_CALL(*gl_, BeginQuery(query->target(), service_id))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, EndQuery(query->target()))
        .Times(1)
        .RetiresOnSaturation();
    manager_->BeginQuery(query);
    manager_->EndQuery(query, submit_count);
  }

  TraceOutputter outputter_;
  std::unique_ptr<FakeCommandBufferServiceBase> command_buffer_service_;
  FakeDecoderClient client_;
  std::unique_ptr<MockGLES2Decoder> decoder_;
  std::unique_ptr<GLES2QueryManager> manager_;

  int32_t shared_memory_id_ = 0;
  int32_t shared_memory2_id_ = 0;
};

class QueryManagerTimerQueryTest : public QueryManagerTest {
 protected:
  void SetUp() override {
    GpuServiceTest::SetUpWithGLVersion("OpenGL ES 3.0",
                                       "GL_EXT_disjoint_timer_query");
    fake_timing_queries_.ExpectDisjointCalls(*gl_);
    SetUpMockGL("GL_EXT_disjoint_timer_query");
  }

  gl::GPUTimingFake fake_timing_queries_;
};

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
const uint32_t QueryManagerTest::kSharedMemoryOffset;
const uint32_t QueryManagerTest::kSharedMemory2Offset;
const size_t QueryManagerTest::kSharedBufferSize;
const int32_t QueryManagerTest::kInvalidSharedMemoryId;
const uint32_t QueryManagerTest::kInvalidSharedMemoryOffset;
const uint32_t QueryManagerTest::kInitialResult;
const uint8_t QueryManagerTest::kInitialMemoryValue;
#endif

TEST_F(QueryManagerTest, Basic) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLuint kClient2Id = 2;

  EXPECT_FALSE(manager_->HavePendingQueries());
  // Check we can create a Query.
  scoped_refptr<QueryManager::Query> query(
      CreateQuery(GL_ANY_SAMPLES_PASSED_EXT, kClient1Id, shared_memory_id_,
                  kSharedMemoryOffset, kService1Id));
  ASSERT_TRUE(query.get() != nullptr);
  // Check we can get the same Query.
  EXPECT_EQ(query.get(), manager_->GetQuery(kClient1Id));
  // Check we get nothing for a non-existent query.
  EXPECT_TRUE(manager_->GetQuery(kClient2Id) == nullptr);
  // Check we can delete the query.
  EXPECT_CALL(*gl_, DeleteQueries(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  manager_->RemoveQuery(kClient1Id);
  // Check we get nothing for a non-existent query.
  EXPECT_TRUE(manager_->GetQuery(kClient1Id) == nullptr);
  // Check query is deleted
  EXPECT_TRUE(query->IsDeleted());
  EXPECT_FALSE(manager_->HavePendingQueries());
}

TEST_F(QueryManagerTest, Destroy) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;

  // Create Query.
  scoped_refptr<QueryManager::Query> query(
      CreateQuery(GL_ANY_SAMPLES_PASSED_EXT, kClient1Id, shared_memory_id_,
                  kSharedMemoryOffset, kService1Id));
  ASSERT_TRUE(query.get() != nullptr);
  EXPECT_CALL(*gl_, DeleteQueries(1, ::testing::Pointee(kService1Id)))
      .Times(1)
      .RetiresOnSaturation();
  manager_->Destroy(true);
  // Check we get nothing for a non-existent query.
  EXPECT_TRUE(manager_->GetQuery(kClient1Id) == nullptr);
  // Check query is deleted
  EXPECT_TRUE(query->IsDeleted());
}

TEST_F(QueryManagerTest, QueryBasic) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT;

  // Create Query.
  scoped_refptr<QueryManager::Query> query(
      CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset,
                  kService1Id));
  ASSERT_TRUE(query.get() != nullptr);

  EXPECT_TRUE(query->IsValid());
  EXPECT_FALSE(query->IsDeleted());
  EXPECT_FALSE(query->IsPending());
  EXPECT_EQ(kTarget, query->target());
}

TEST_F(QueryManagerTest, ProcessPendingQuery) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;
  const GLuint kResult = 1;

  // Check nothing happens if there are no pending queries.
  manager_->ProcessPendingQueries(false);

  // Create Query.
  scoped_refptr<QueryManager::Query> query(
      CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset,
                  kService1Id));
  ASSERT_TRUE(query.get() != nullptr);

  // Setup shared memory like client would.
  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  ASSERT_TRUE(sync != nullptr);
  sync->Reset();

  // Queue it
  QueueQuery(query.get(), kService1Id, kSubmitCount);
  EXPECT_TRUE(query->IsPending());
  EXPECT_TRUE(manager_->HavePendingQueries());

  // Process with return not available.
  // Expect 1 GL command.
  EXPECT_CALL(*gl_,
              GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
      .WillOnce(SetArgPointee<2>(0))
      .RetiresOnSaturation();
  manager_->ProcessPendingQueries(false);
  EXPECT_TRUE(query->IsPending());
  EXPECT_EQ(0, base::subtle::Atomic32{sync->process_count});
  EXPECT_EQ(0u, uint64_t{sync->result});

  // Process with return available.
  // Expect 2 GL commands.
  EXPECT_CALL(*gl_,
              GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
      .WillOnce(SetArgPointee<2>(1))
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_EXT, _))
      .WillOnce(SetArgPointee<2>(kResult))
      .RetiresOnSaturation();
  manager_->ProcessPendingQueries(false);
  EXPECT_FALSE(query->IsPending());
  EXPECT_EQ(kSubmitCount, base::subtle::Atomic32{sync->process_count});
  EXPECT_EQ(kResult, uint64_t{sync->result});
  EXPECT_FALSE(manager_->HavePendingQueries());

  // Process with no queries.
  // Expect no GL commands/
  manager_->ProcessPendingQueries(false);
}

TEST_F(QueryManagerTest, ProcessPendingQueries) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLuint kClient2Id = 2;
  const GLuint kService2Id = 12;
  const GLuint kClient3Id = 3;
  const GLuint kService3Id = 13;
  const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT;
  const base::subtle::Atomic32 kSubmitCount1 = 123;
  const base::subtle::Atomic32 kSubmitCount2 = 123;
  const base::subtle::Atomic32 kSubmitCount3 = 123;
  const GLuint kResult1 = 1;
  const GLuint kResult2 = 1;
  const GLuint kResult3 = 1;

  // Setup shared memory like client would.
  QuerySync* sync1 = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync1) * 3);
  ASSERT_TRUE(sync1 != nullptr);
  QuerySync* sync2 = UNSAFE_TODO(sync1 + 1);
  QuerySync* sync3 = UNSAFE_TODO(sync2 + 1);

  // Create Queries.
  scoped_refptr<QueryManager::Query> query1(
      CreateQuery(kTarget, kClient1Id, shared_memory_id_,
                  kSharedMemoryOffset + sizeof(*sync1) * 0, kService1Id));
  scoped_refptr<QueryManager::Query> query2(
      CreateQuery(kTarget, kClient2Id, shared_memory_id_,
                  kSharedMemoryOffset + sizeof(*sync1) * 1, kService2Id));
  scoped_refptr<QueryManager::Query> query3(
      CreateQuery(kTarget, kClient3Id, shared_memory_id_,
                  kSharedMemoryOffset + sizeof(*sync1) * 2, kService3Id));
  ASSERT_TRUE(query1.get() != nullptr);
  ASSERT_TRUE(query2.get() != nullptr);
  ASSERT_TRUE(query3.get() != nullptr);
  EXPECT_FALSE(manager_->HavePendingQueries());

  sync1->Reset();
  sync2->Reset();
  sync3->Reset();

  // Queue them
  QueueQuery(query1.get(), kService1Id, kSubmitCount1);
  QueueQuery(query2.get(), kService2Id, kSubmitCount2);
  QueueQuery(query3.get(), kService3Id, kSubmitCount3);
  EXPECT_TRUE(query1->IsPending());
  EXPECT_TRUE(query2->IsPending());
  EXPECT_TRUE(query3->IsPending());
  EXPECT_TRUE(manager_->HavePendingQueries());

  // Process with return available for first 2 queries.
  // Expect 4 GL commands.
  {
    InSequence s;
    EXPECT_CALL(
        *gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
        .WillOnce(SetArgPointee<2>(1))
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, GetQueryObjectuiv(kService1Id, GL_QUERY_RESULT_EXT, _))
        .WillOnce(SetArgPointee<2>(kResult1))
        .RetiresOnSaturation();
    EXPECT_CALL(
        *gl_, GetQueryObjectuiv(kService2Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
        .WillOnce(SetArgPointee<2>(1))
        .RetiresOnSaturation();
    EXPECT_CALL(*gl_, GetQueryObjectuiv(kService2Id, GL_QUERY_RESULT_EXT, _))
        .WillOnce(SetArgPointee<2>(kResult2))
        .RetiresOnSaturation();
    EXPECT_CALL(
        *gl_, GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
        .WillOnce(SetArgPointee<2>(0))
        .RetiresOnSaturation();
    manager_->ProcessPendingQueries(false);
  }
  EXPECT_FALSE(query1->IsPending());
  EXPECT_FALSE(query2->IsPending());
  EXPECT_TRUE(query3->IsPending());
  EXPECT_EQ(kSubmitCount1, base::subtle::Atomic32{sync1->process_count});
  EXPECT_EQ(kSubmitCount2, base::subtle::Atomic32{sync2->process_count});
  EXPECT_EQ(kResult1, uint64_t{sync1->result});
  EXPECT_EQ(kResult2, uint64_t{sync2->result});
  EXPECT_EQ(0, base::subtle::Atomic32{sync3->process_count});
  EXPECT_EQ(0u, uint64_t{sync3->result});
  EXPECT_TRUE(manager_->HavePendingQueries());

  // Process with renaming query. No result.
  // Expect 1 GL commands.
  EXPECT_CALL(*gl_,
              GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
      .WillOnce(SetArgPointee<2>(0))
      .RetiresOnSaturation();
  manager_->ProcessPendingQueries(false);
  EXPECT_TRUE(query3->IsPending());
  EXPECT_EQ(0, base::subtle::Atomic32{sync3->process_count});
  EXPECT_EQ(0u, uint64_t{sync3->result});
  EXPECT_TRUE(manager_->HavePendingQueries());

  // Process with renaming query. With result.
  // Expect 2 GL commands.
  EXPECT_CALL(*gl_,
              GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _))
      .WillOnce(SetArgPointee<2>(1))
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, GetQueryObjectuiv(kService3Id, GL_QUERY_RESULT_EXT, _))
      .WillOnce(SetArgPointee<2>(kResult3))
      .RetiresOnSaturation();
  manager_->ProcessPendingQueries(false);
  EXPECT_FALSE(query3->IsPending());
  EXPECT_EQ(kSubmitCount3, base::subtle::Atomic32{sync3->process_count});
  EXPECT_EQ(kResult3, uint64_t{sync3->result});
  EXPECT_FALSE(manager_->HavePendingQueries());
}

TEST_F(QueryManagerTest, ExitWithPendingQuery) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  // Create Query.
  scoped_refptr<QueryManager::Query> query(
      CreateQuery(kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset,
                  kService1Id));
  ASSERT_TRUE(query.get() != nullptr);

  // Queue it
  QueueQuery(query.get(), kService1Id, kSubmitCount);
}

TEST_F(QueryManagerTimerQueryTest, TimeElapsedQuery) {
  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIME_ELAPSED_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;
  decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true);
  fake_timing_queries_.SetCurrentGLTime(200 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->BeginQuery(query);
  fake_timing_queries_.SetCurrentGLTime(300 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->EndQuery(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());

  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  const uint64_t expected_result =
      100u * base::Time::kNanosecondsPerMicrosecond;
  EXPECT_EQ(expected_result, uint64_t{sync->result});

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, TimeElapsedPauseResume) {
  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIME_ELAPSED_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;
  decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true);
  fake_timing_queries_.SetCurrentGLTime(200 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->BeginQuery(query);

  // Pause and Resume here.
  fake_timing_queries_.SetCurrentGLTime(300 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->PauseQueries();

  fake_timing_queries_.SetCurrentGLTime(400 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->ResumeQueries();

  fake_timing_queries_.SetCurrentGLTime(500 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->EndQuery(query, kSubmitCount);

  manager_->ProcessPendingQueries(false);
  EXPECT_TRUE(query->IsFinished());

  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  const uint64_t expected_result =
      300u * base::Time::kNanosecondsPerMicrosecond;
  EXPECT_EQ(expected_result, uint64_t{sync->result});

  // Make sure next query works properly.
  fake_timing_queries_.SetCurrentGLTime(600 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->BeginQuery(query);
  fake_timing_queries_.SetCurrentGLTime(700 *
                                        base::Time::kNanosecondsPerMicrosecond);
  manager_->EndQuery(query, kSubmitCount + 1);
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());

  const uint64_t expected_result2 =
      100u * base::Time::kNanosecondsPerMicrosecond;
  EXPECT_EQ(expected_result2, uint64_t{sync->result});

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, TimeElapsedDisjoint) {
  DisjointValueSync* disjoint_sync =
      decoder_->GetSharedMemoryAs<DisjointValueSync*>(
          shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync));
  manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset);

  const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount();
  ASSERT_EQ(0u, current_disjoint_value);

  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIME_ELAPSED_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  // Disjoint happening before the query should not trigger a disjoint event.
  fake_timing_queries_.SetDisjoint();

  fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true);
  manager_->BeginQuery(query);
  manager_->EndQuery(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());
  EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount());

  // Disjoint happening during query should trigger disjoint event.
  fake_timing_queries_.ExpectGPUTimerQuery(*gl_, true);
  manager_->BeginQuery(query);
  fake_timing_queries_.SetDisjoint();
  manager_->EndQuery(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());
  EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount());

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, TimeStampQuery) {
  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIMESTAMP_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  const uint64_t expected_result =
      100u * base::Time::kNanosecondsPerMicrosecond;
  fake_timing_queries_.SetCurrentGLTime(expected_result);
  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  EXPECT_EQ(expected_result, uint64_t{sync->result});

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, TimeStampQueryPending) {
  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIMESTAMP_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  const uint64_t expected_result =
      100u * base::Time::kNanosecondsPerMicrosecond;
  fake_timing_queries_.SetCurrentGLTime(expected_result);
  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  EXPECT_TRUE(query->IsPending());
  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  EXPECT_EQ(expected_result, uint64_t{sync->result});

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, TimeStampDisjoint) {
  DisjointValueSync* disjoint_sync =
      decoder_->GetSharedMemoryAs<DisjointValueSync*>(
          shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync));
  manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset);

  const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount();
  ASSERT_EQ(0u, current_disjoint_value);

  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIMESTAMP_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  // Disjoint happening before the query should not trigger a disjoint event.
  fake_timing_queries_.SetDisjoint();

  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());
  EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount());

  // Disjoint happening during query should trigger disjoint event.
  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  fake_timing_queries_.SetDisjoint();
  manager_->ProcessPendingQueries(false);

  EXPECT_TRUE(query->IsFinished());
  EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount());

  manager_->Destroy(false);
}

TEST_F(QueryManagerTimerQueryTest, DisjointContinualTest) {
  DisjointValueSync* disjoint_sync =
      decoder_->GetSharedMemoryAs<DisjointValueSync*>(
          shared_memory2_id_, kSharedMemory2Offset, sizeof(*disjoint_sync));
  manager_->SetDisjointSync(shared_memory2_id_, kSharedMemory2Offset);

  const uint32_t current_disjoint_value = disjoint_sync->GetDisjointCount();
  ASSERT_EQ(0u, current_disjoint_value);

  // Disjoint value should not be updated until we have a timestamp query.
  fake_timing_queries_.SetDisjoint();
  manager_->ProcessFrameBeginUpdates();
  EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount());

  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_TIMESTAMP_EXT;
  const base::subtle::Atomic32 kSubmitCount = 123;

  QueryManager::Query* query = CreateQuery(
      kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  fake_timing_queries_.ExpectGPUTimeStampQuery(*gl_, false);
  manager_->QueryCounter(query, kSubmitCount);
  manager_->ProcessPendingQueries(false);

  EXPECT_EQ(current_disjoint_value, disjoint_sync->GetDisjointCount());
  fake_timing_queries_.SetDisjoint();
  manager_->ProcessFrameBeginUpdates();
  EXPECT_NE(current_disjoint_value, disjoint_sync->GetDisjointCount());

  manager_->Destroy(false);
}

TEST_F(QueryManagerTest, GetErrorQuery) {
  const GLuint kClient1Id = 1;
  const GLenum kTarget = GL_GET_ERROR_QUERY_CHROMIUM;
  const base::subtle::Atomic32 kSubmitCount = 123;

  TestHelper::SetupFeatureInfoInitExpectations(gl_.get(), "");
  auto feature_info = base::MakeRefCounted<FeatureInfo>();
  feature_info->InitializeForTesting();
  std::unique_ptr<GLES2QueryManager> manager(
      new GLES2QueryManager(decoder_.get(), feature_info.get()));

  QueryManager::Query* query =
      CreateQueryOnManager(manager.get(), kTarget, kClient1Id,
                           shared_memory_id_, kSharedMemoryOffset, 0);
  ASSERT_TRUE(query != nullptr);

  // Setup shared memory like client would.
  QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>(
      shared_memory_id_, kSharedMemoryOffset, sizeof(*sync));
  ASSERT_TRUE(sync != nullptr);
  sync->Reset();

  manager->BeginQuery(query);

  MockErrorState mock_error_state;
  EXPECT_CALL(*decoder_.get(), GetErrorState())
      .WillRepeatedly(Return(&mock_error_state));
  EXPECT_CALL(mock_error_state, GetGLError())
      .WillOnce(Return(GL_INVALID_ENUM))
      .RetiresOnSaturation();

  manager->EndQuery(query, kSubmitCount);
  EXPECT_FALSE(query->IsPending());

  EXPECT_EQ(static_cast<GLuint>(GL_INVALID_ENUM), uint64_t{sync->result});

  manager->Destroy(false);
}

TEST_F(QueryManagerTest, OcclusionQuery) {
  const GLuint kClient1Id = 1;
  const GLuint kService1Id = 11;
  const GLenum kTarget = GL_SAMPLES_PASSED_ARB;
  const base::subtle::Atomic32 kSubmitCount = 123;

  TestHelper::SetupFeatureInfoInitExpectations(gl_.get(),
                                               "GL_EXT_occlusion_query");
  auto feature_info = base::MakeRefCounted<FeatureInfo>();
  feature_info->InitializeForTesting();
  std::unique_ptr<GLES2QueryManager> manager(
      new GLES2QueryManager(decoder_.get(), feature_info.get()));

  QueryManager::Query* query =
      CreateQueryOnManager(manager.get(), kTarget, kClient1Id,
                           shared_memory_id_, kSharedMemoryOffset, kService1Id);
  ASSERT_TRUE(query != nullptr);

  EXPECT_CALL(*gl_, BeginQuery(GL_SAMPLES_PASSED_ARB, kService1Id))
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*gl_, EndQuery(GL_SAMPLES_PASSED_ARB))
      .Times(1)
      .RetiresOnSaturation();
  manager->BeginQuery(query);
  manager->EndQuery(query, kSubmitCount);
  manager->Destroy(false);
}

}  // namespace gles2
}  // namespace gpu
