root/cc/resources/resource_update_controller_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. num_total_flushes_
  2. OnFlush
  3. OnUpload
  4. IsQueryResultAvailable
  5. SetUp
  6. AppendFullUploadsOfIndexedTextureToUpdateQueue
  7. AppendFullUploadsToUpdateQueue
  8. AppendPartialUploadsOfIndexedTextureToUpdateQueue
  9. AppendPartialUploadsToUpdateQueue
  10. SetMaxUploadCountPerUpdate
  11. UpdateTextures
  12. MakeQueryResultAvailable
  13. flush
  14. shallowFlushCHROMIUM
  15. texSubImage2D
  16. getQueryObjectuivEXT
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. Reset
  25. ReadyToFinalizeCalled
  26. ReadyToFinalizeTextureUpdates
  27. Create
  28. SetNow
  29. Now
  30. SetUpdateTextureTime
  31. UpdateMoreTexturesCompletionTime
  32. SetUpdateMoreTexturesSize
  33. UpdateMoreTexturesSize
  34. update_more_textures_size_
  35. RunPendingTask
  36. TEST_F
  37. TEST_F
  38. TEST_F

// Copyright 2012 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.

#include "cc/resources/resource_update_controller.h"

#include "base/test/test_simple_task_runner.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/scheduler_test_common.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/test/tiled_layer_test_common.h"
#include "cc/trees/single_thread_proxy.h"  // For DebugScopedSetImplThread
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2ext.h"

using testing::Test;

namespace cc {
namespace {

const int kFlushPeriodFull = 4;
const int kFlushPeriodPartial = kFlushPeriodFull;

class ResourceUpdateControllerTest;

class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D {
 public:
  explicit WebGraphicsContext3DForUploadTest(ResourceUpdateControllerTest* test)
      : test_(test) {}

  virtual void flush() OVERRIDE;
  virtual void shallowFlushCHROMIUM() OVERRIDE;
  virtual void texSubImage2D(GLenum target,
                             GLint level,
                             GLint xoffset,
                             GLint yoffset,
                             GLsizei width,
                             GLsizei height,
                             GLenum format,
                             GLenum type,
                             const void* pixels) OVERRIDE;

  virtual void getQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* value)
      OVERRIDE;

 private:
  ResourceUpdateControllerTest* test_;
};

class ResourceUpdateControllerTest : public Test {
 public:
  ResourceUpdateControllerTest()
      : proxy_(),
        queue_(make_scoped_ptr(new ResourceUpdateQueue)),
        resource_manager_(PrioritizedResourceManager::Create(&proxy_)),
        query_results_available_(0),
        full_upload_count_expected_(0),
        partial_count_expected_(0),
        total_upload_count_expected_(0),
        max_upload_count_per_update_(0),
        num_consecutive_flushes_(0),
        num_dangling_uploads_(0),
        num_total_uploads_(0),
        num_total_flushes_(0) {}

  virtual ~ResourceUpdateControllerTest() {
    DebugScopedSetImplThreadAndMainThreadBlocked
    impl_thread_and_main_thread_blocked(&proxy_);
    resource_manager_->ClearAllMemory(resource_provider_.get());
  }

 public:
  void OnFlush() {
    // Check for back-to-back flushes.
    EXPECT_EQ(0, num_consecutive_flushes_) << "Back-to-back flushes detected.";

    num_dangling_uploads_ = 0;
    num_consecutive_flushes_++;
    num_total_flushes_++;
  }

  void OnUpload() {
    // Check for too many consecutive uploads
    if (num_total_uploads_ < full_upload_count_expected_) {
      EXPECT_LT(num_dangling_uploads_, kFlushPeriodFull)
          << "Too many consecutive full uploads detected.";
    } else {
      EXPECT_LT(num_dangling_uploads_, kFlushPeriodPartial)
          << "Too many consecutive partial uploads detected.";
    }

    num_consecutive_flushes_ = 0;
    num_dangling_uploads_++;
    num_total_uploads_++;
  }

  bool IsQueryResultAvailable() {
    if (!query_results_available_)
      return false;

    query_results_available_--;
    return true;
  }

 protected:
  virtual void SetUp() {
    bitmap_.allocN32Pixels(300, 150);

    for (int i = 0; i < 4; i++) {
      textures_[i] = PrioritizedResource::Create(resource_manager_.get(),
                                                 gfx::Size(300, 150),
                                                 RGBA_8888);
      textures_[i]->
          set_request_priority(PriorityCalculator::VisiblePriority(true));
    }
    resource_manager_->PrioritizeTextures();

    output_surface_ = FakeOutputSurface::Create3d(
        scoped_ptr<TestWebGraphicsContext3D>(
            new WebGraphicsContext3DForUploadTest(this)));
    CHECK(output_surface_->BindToClient(&output_surface_client_));

    shared_bitmap_manager_.reset(new TestSharedBitmapManager());
    resource_provider_ = ResourceProvider::Create(
        output_surface_.get(), shared_bitmap_manager_.get(), 0, false, 1);
  }

  void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count,
                                                      int texture_index) {
    full_upload_count_expected_ += count;
    total_upload_count_expected_ += count;

    const gfx::Rect rect(0, 0, 300, 150);
    const ResourceUpdate upload = ResourceUpdate::Create(
        textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d());
    for (int i = 0; i < count; i++)
      queue_->AppendFullUpload(upload);
  }

  void AppendFullUploadsToUpdateQueue(int count) {
    AppendFullUploadsOfIndexedTextureToUpdateQueue(count, 0);
  }

  void AppendPartialUploadsOfIndexedTextureToUpdateQueue(int count,
                                                         int texture_index) {
    partial_count_expected_ += count;
    total_upload_count_expected_ += count;

    const gfx::Rect rect(0, 0, 100, 100);
    const ResourceUpdate upload = ResourceUpdate::Create(
        textures_[texture_index].get(), &bitmap_, rect, rect, gfx::Vector2d());
    for (int i = 0; i < count; i++)
      queue_->AppendPartialUpload(upload);
  }

  void AppendPartialUploadsToUpdateQueue(int count) {
    AppendPartialUploadsOfIndexedTextureToUpdateQueue(count, 0);
  }

  void SetMaxUploadCountPerUpdate(int count) {
    max_upload_count_per_update_ = count;
  }

  void UpdateTextures() {
    DebugScopedSetImplThreadAndMainThreadBlocked
    impl_thread_and_main_thread_blocked(&proxy_);
    scoped_ptr<ResourceUpdateController> update_controller =
        ResourceUpdateController::Create(NULL,
                                         proxy_.ImplThreadTaskRunner(),
                                         queue_.Pass(),
                                         resource_provider_.get());
    update_controller->Finalize();
  }

  void MakeQueryResultAvailable() { query_results_available_++; }

 protected:
  // Classes required to interact and test the ResourceUpdateController
  FakeProxy proxy_;
  FakeOutputSurfaceClient output_surface_client_;
  scoped_ptr<OutputSurface> output_surface_;
  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
  scoped_ptr<ResourceProvider> resource_provider_;
  scoped_ptr<ResourceUpdateQueue> queue_;
  scoped_ptr<PrioritizedResource> textures_[4];
  scoped_ptr<PrioritizedResourceManager> resource_manager_;
  SkBitmap bitmap_;
  int query_results_available_;

  // Properties / expectations of this test
  int full_upload_count_expected_;
  int partial_count_expected_;
  int total_upload_count_expected_;
  int max_upload_count_per_update_;

  // Dynamic properties of this test
  int num_consecutive_flushes_;
  int num_dangling_uploads_;
  int num_total_uploads_;
  int num_total_flushes_;
};

void WebGraphicsContext3DForUploadTest::flush() { test_->OnFlush(); }

void WebGraphicsContext3DForUploadTest::shallowFlushCHROMIUM() {
  test_->OnFlush();
}

void WebGraphicsContext3DForUploadTest::texSubImage2D(GLenum target,
                                                      GLint level,
                                                      GLint xoffset,
                                                      GLint yoffset,
                                                      GLsizei width,
                                                      GLsizei height,
                                                      GLenum format,
                                                      GLenum type,
                                                      const void* pixels) {
  test_->OnUpload();
}

void WebGraphicsContext3DForUploadTest::getQueryObjectuivEXT(GLuint id,
                                                             GLenum pname,
                                                             GLuint* params) {
  if (pname == GL_QUERY_RESULT_AVAILABLE_EXT)
    *params = test_->IsQueryResultAvailable();
}

// ZERO UPLOADS TESTS
TEST_F(ResourceUpdateControllerTest, ZeroUploads) {
  AppendFullUploadsToUpdateQueue(0);
  AppendPartialUploadsToUpdateQueue(0);
  UpdateTextures();

  EXPECT_EQ(0, num_total_flushes_);
  EXPECT_EQ(0, num_total_uploads_);
}

// ONE UPLOAD TESTS
TEST_F(ResourceUpdateControllerTest, OneFullUpload) {
  AppendFullUploadsToUpdateQueue(1);
  AppendPartialUploadsToUpdateQueue(0);
  UpdateTextures();

  EXPECT_EQ(1, num_total_flushes_);
  EXPECT_EQ(1, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

TEST_F(ResourceUpdateControllerTest, OnePartialUpload) {
  AppendFullUploadsToUpdateQueue(0);
  AppendPartialUploadsToUpdateQueue(1);
  UpdateTextures();

  EXPECT_EQ(1, num_total_flushes_);
  EXPECT_EQ(1, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

TEST_F(ResourceUpdateControllerTest, OneFullOnePartialUpload) {
  AppendFullUploadsToUpdateQueue(1);
  AppendPartialUploadsToUpdateQueue(1);
  UpdateTextures();

  EXPECT_EQ(1, num_total_flushes_);
  EXPECT_EQ(2, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

// This class of tests upload a number of textures that is a multiple
// of the flush period.
const int full_upload_flush_multipler = 7;
const int full_count = full_upload_flush_multipler * kFlushPeriodFull;

const int partial_upload_flush_multipler = 11;
const int partial_count =
    partial_upload_flush_multipler * kFlushPeriodPartial;

TEST_F(ResourceUpdateControllerTest, ManyFullUploads) {
  AppendFullUploadsToUpdateQueue(full_count);
  AppendPartialUploadsToUpdateQueue(0);
  UpdateTextures();

  EXPECT_EQ(full_upload_flush_multipler, num_total_flushes_);
  EXPECT_EQ(full_count, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

TEST_F(ResourceUpdateControllerTest, ManyPartialUploads) {
  AppendFullUploadsToUpdateQueue(0);
  AppendPartialUploadsToUpdateQueue(partial_count);
  UpdateTextures();

  EXPECT_EQ(partial_upload_flush_multipler, num_total_flushes_);
  EXPECT_EQ(partial_count, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

TEST_F(ResourceUpdateControllerTest, ManyFullManyPartialUploads) {
  AppendFullUploadsToUpdateQueue(full_count);
  AppendPartialUploadsToUpdateQueue(partial_count);
  UpdateTextures();

  EXPECT_EQ(full_upload_flush_multipler + partial_upload_flush_multipler,
            num_total_flushes_);
  EXPECT_EQ(full_count + partial_count, num_total_uploads_);
  EXPECT_EQ(0, num_dangling_uploads_)
      << "Last upload wasn't followed by a flush.";
}

class FakeResourceUpdateControllerClient
    : public ResourceUpdateControllerClient {
 public:
  FakeResourceUpdateControllerClient() { Reset(); }
  void Reset() { ready_to_finalize_called_ = false; }
  bool ReadyToFinalizeCalled() const { return ready_to_finalize_called_; }

  virtual void ReadyToFinalizeTextureUpdates() OVERRIDE {
    ready_to_finalize_called_ = true;
  }

 protected:
  bool ready_to_finalize_called_;
};

class FakeResourceUpdateController : public ResourceUpdateController {
 public:
  static scoped_ptr<FakeResourceUpdateController> Create(
      ResourceUpdateControllerClient* client,
      base::TestSimpleTaskRunner* task_runner,
      scoped_ptr<ResourceUpdateQueue> queue,
      ResourceProvider* resource_provider) {
    return make_scoped_ptr(new FakeResourceUpdateController(
        client, task_runner, queue.Pass(), resource_provider));
  }

  void SetNow(base::TimeTicks time) { now_ = time; }
  base::TimeTicks Now() const { return now_; }
  void SetUpdateTextureTime(base::TimeDelta time) {
    update_textures_time_ = time;
  }
  virtual base::TimeTicks UpdateMoreTexturesCompletionTime() OVERRIDE {
    size_t total_updates =
        resource_provider_->NumBlockingUploads() + update_more_textures_size_;
    return now_ + total_updates * update_textures_time_;
  }
  void SetUpdateMoreTexturesSize(size_t size) {
    update_more_textures_size_ = size;
  }
  virtual size_t UpdateMoreTexturesSize() const OVERRIDE {
    return update_more_textures_size_;
  }

 protected:
  FakeResourceUpdateController(ResourceUpdateControllerClient* client,
                               base::TestSimpleTaskRunner* task_runner,
                               scoped_ptr<ResourceUpdateQueue> queue,
                               ResourceProvider* resource_provider)
      : ResourceUpdateController(
          client, task_runner, queue.Pass(), resource_provider),
        resource_provider_(resource_provider),
        update_more_textures_size_(0) {}

  ResourceProvider* resource_provider_;
  base::TimeTicks now_;
  base::TimeDelta update_textures_time_;
  size_t update_more_textures_size_;
};

static void RunPendingTask(base::TestSimpleTaskRunner* task_runner,
                           FakeResourceUpdateController* controller) {
  EXPECT_TRUE(task_runner->HasPendingTask());
  controller->SetNow(controller->Now() + task_runner->NextPendingTaskDelay());
  task_runner->RunPendingTasks();
}

TEST_F(ResourceUpdateControllerTest, UpdateMoreTextures) {
  FakeResourceUpdateControllerClient client;
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;

  SetMaxUploadCountPerUpdate(1);
  AppendFullUploadsToUpdateQueue(3);
  AppendPartialUploadsToUpdateQueue(0);

  DebugScopedSetImplThreadAndMainThreadBlocked
  impl_thread_and_main_thread_blocked(&proxy_);
  scoped_ptr<FakeResourceUpdateController> controller(
      FakeResourceUpdateController::Create(&client,
                                           task_runner.get(),
                                           queue_.Pass(),
                                           resource_provider_.get()));

  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
  controller->SetUpdateMoreTexturesSize(1);
  // Not enough time for any updates.
  controller->PerformMoreUpdates(controller->Now() +
                                 base::TimeDelta::FromMilliseconds(90));
  EXPECT_FALSE(task_runner->HasPendingTask());

  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
  controller->SetUpdateMoreTexturesSize(1);
  // Only enough time for 1 update.
  controller->PerformMoreUpdates(controller->Now() +
                                 base::TimeDelta::FromMilliseconds(120));
  EXPECT_FALSE(task_runner->HasPendingTask());
  EXPECT_EQ(1, num_total_uploads_);

  // Complete one upload.
  MakeQueryResultAvailable();

  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
  controller->SetUpdateMoreTexturesSize(1);
  // Enough time for 2 updates.
  controller->PerformMoreUpdates(controller->Now() +
                                 base::TimeDelta::FromMilliseconds(220));
  RunPendingTask(task_runner.get(), controller.get());
  EXPECT_FALSE(task_runner->HasPendingTask());
  EXPECT_TRUE(client.ReadyToFinalizeCalled());
  EXPECT_EQ(3, num_total_uploads_);
}

TEST_F(ResourceUpdateControllerTest, NoMoreUpdates) {
  FakeResourceUpdateControllerClient client;
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;

  SetMaxUploadCountPerUpdate(1);
  AppendFullUploadsToUpdateQueue(2);
  AppendPartialUploadsToUpdateQueue(0);

  DebugScopedSetImplThreadAndMainThreadBlocked
  impl_thread_and_main_thread_blocked(&proxy_);
  scoped_ptr<FakeResourceUpdateController> controller(
      FakeResourceUpdateController::Create(&client,
                                           task_runner.get(),
                                           queue_.Pass(),
                                           resource_provider_.get()));

  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
  controller->SetUpdateMoreTexturesSize(1);
  // Enough time for 3 updates but only 2 necessary.
  controller->PerformMoreUpdates(controller->Now() +
                                 base::TimeDelta::FromMilliseconds(310));
  RunPendingTask(task_runner.get(), controller.get());
  EXPECT_FALSE(task_runner->HasPendingTask());
  EXPECT_TRUE(client.ReadyToFinalizeCalled());
  EXPECT_EQ(2, num_total_uploads_);

  client.Reset();
  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(100));
  controller->SetUpdateMoreTexturesSize(1);
  // Enough time for updates but no more updates left.
  controller->PerformMoreUpdates(controller->Now() +
                                 base::TimeDelta::FromMilliseconds(310));

  // ReadyToFinalizeTextureUpdates should only be called once.
  EXPECT_FALSE(task_runner->HasPendingTask());
  EXPECT_FALSE(client.ReadyToFinalizeCalled());
  EXPECT_EQ(2, num_total_uploads_);
}

TEST_F(ResourceUpdateControllerTest, UpdatesCompleteInFiniteTime) {
  FakeResourceUpdateControllerClient client;
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;

  SetMaxUploadCountPerUpdate(1);
  AppendFullUploadsToUpdateQueue(2);
  AppendPartialUploadsToUpdateQueue(0);

  DebugScopedSetImplThreadAndMainThreadBlocked
  impl_thread_and_main_thread_blocked(&proxy_);
  scoped_ptr<FakeResourceUpdateController> controller(
      FakeResourceUpdateController::Create(&client,
                                           task_runner.get(),
                                           queue_.Pass(),
                                           resource_provider_.get()));

  controller->SetNow(controller->Now() + base::TimeDelta::FromMilliseconds(1));
  controller->SetUpdateTextureTime(base::TimeDelta::FromMilliseconds(500));
  controller->SetUpdateMoreTexturesSize(1);

  for (int i = 0; i < 100; i++) {
    if (client.ReadyToFinalizeCalled())
      break;

    // Not enough time for any updates.
    controller->PerformMoreUpdates(controller->Now() +
                                   base::TimeDelta::FromMilliseconds(400));

    if (task_runner->HasPendingTask())
      RunPendingTask(task_runner.get(), controller.get());
  }

  EXPECT_FALSE(task_runner->HasPendingTask());
  EXPECT_TRUE(client.ReadyToFinalizeCalled());
  EXPECT_EQ(2, num_total_uploads_);
}

}  // namespace
}  // namespace cc

/* [<][>][^][v][top][bottom][index][help] */