// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains unit tests for gles2 commmands

#include <limits>

#include "base/bind.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"

namespace gpu {
namespace gles2 {

class GLES2FormatTest : public testing::Test {
 protected:
  static const unsigned char kInitialValue = 0xBD;

  virtual void SetUp() {
    memset(buffer_, kInitialValue, sizeof(buffer_));
  }

  virtual void TearDown() {
  }

  template <typename T>
  T* GetBufferAs() {
    return static_cast<T*>(static_cast<void*>(&buffer_));
  }

  void CheckBytesWritten(
      const void* end, size_t expected_size, size_t written_size) {
    size_t actual_size = static_cast<const unsigned char*>(end) -
        GetBufferAs<const unsigned char>();
    EXPECT_LT(actual_size, sizeof(buffer_));
    EXPECT_GT(actual_size, 0u);
    EXPECT_EQ(expected_size, actual_size);
    EXPECT_EQ(kInitialValue, buffer_[written_size]);
    EXPECT_NE(kInitialValue, buffer_[written_size - 1]);
  }

  void CheckBytesWrittenMatchesExpectedSize(
      const void* end, size_t expected_size) {
    CheckBytesWritten(end, expected_size, expected_size);
  }

 private:
  unsigned char buffer_[1024];
};

void SignalCompletion(uint32* assigned_async_token_ptr,
                      uint32 async_token,
                      AsyncUploadSync* sync) {
  EXPECT_EQ(async_token, *assigned_async_token_ptr);
  sync->SetAsyncUploadToken(async_token);
}

TEST(GLES2FormatAsyncUploadSyncTest, AsyncUploadSync) {
  const size_t kSize = 10;
  const size_t kCount = 1000;

  base::Thread thread("GLES2FormatUploadSyncTest - Fake Upload Thread");
  thread.Start();

  // Run the same test 50 times so we retest the wrap as well.
  for (size_t test_run = 0; test_run < 50; ++test_run) {
    AsyncUploadSync sync;
    sync.Reset();

    uint32 buffer_tokens[kSize];
    memset(buffer_tokens, 0, sizeof(buffer_tokens));

    // Start with a token large enough so that we'll wrap.
    uint32 async_token = std::numeric_limits<uint32>::max() - kCount / 2;

    // Set initial async token.
    sync.SetAsyncUploadToken(async_token);

    for (size_t i = 0; i < kCount; ++i) {
      size_t buffer = i % kSize;

      // Loop until previous async token has passed if any was set.
      while (buffer_tokens[buffer] &&
             !sync.HasAsyncUploadTokenPassed(buffer_tokens[buffer]))
        base::PlatformThread::YieldCurrentThread();

      // Next token, skip 0.
      async_token++;
      if (async_token == 0)
        async_token++;

      // Set the buffer's associated token.
      buffer_tokens[buffer] = async_token;

      // Set the async upload token on the fake upload thread and assert that
      // the associated buffer still has the given token.
      thread.message_loop()->PostTask(FROM_HERE,
                                      base::Bind(&SignalCompletion,
                                                 &buffer_tokens[buffer],
                                                 async_token,
                                                 &sync));
    }

    // Flush the thread message loop before starting again.
    base::WaitableEvent waitable(false, false);
    thread.message_loop()->PostTask(FROM_HERE,
                                    base::Bind(&base::WaitableEvent::Signal,
                                               base::Unretained(&waitable)));
    waitable.Wait();
  }
}

// GCC requires these declarations, but MSVC requires they not be present
#ifndef _MSC_VER
const unsigned char GLES2FormatTest::kInitialValue;
#endif

#include "gpu/command_buffer/common/gles2_cmd_format_test_autogen.h"

}  // namespace gles2
}  // namespace gpu

