root/cc/scheduler/delay_based_time_source_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. Interval
  2. TEST
  3. TEST
  4. TEST
  5. TEST
  6. TEST
  7. TEST
  8. TEST
  9. TEST
  10. TEST
  11. TEST
  12. TEST
  13. TEST
  14. TEST
  15. TEST
  16. TEST
  17. TEST
  18. TEST
  19. TEST

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

#include "cc/scheduler/delay_based_time_source.h"

#include "base/basictypes.h"
#include "base/test/test_simple_task_runner.h"
#include "cc/test/scheduler_test_common.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {

base::TimeDelta Interval() {
  return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond /
                                           60);
}

TEST(DelayBasedTimeSourceTest, TaskPostedAndTickCalled) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);

  timer->SetActive(true);
  EXPECT_TRUE(timer->Active());
  EXPECT_TRUE(task_runner->HasPendingTask());

  timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(16));
  task_runner->RunPendingTasks();
  EXPECT_TRUE(timer->Active());
  EXPECT_TRUE(client.TickCalled());
}

TEST(DelayBasedTimeSource, TickNotCalledWithTaskPosted) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  EXPECT_TRUE(task_runner->HasPendingTask());
  timer->SetActive(false);
  task_runner->RunPendingTasks();
  EXPECT_FALSE(client.TickCalled());
}

TEST(DelayBasedTimeSource, StartTwiceEnqueuesOneTask) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  EXPECT_TRUE(task_runner->HasPendingTask());
  task_runner->ClearPendingTasks();
  timer->SetActive(true);
  EXPECT_FALSE(task_runner->HasPendingTask());
}

TEST(DelayBasedTimeSource, StartWhenRunningDoesntTick) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  EXPECT_TRUE(task_runner->HasPendingTask());
  task_runner->RunPendingTasks();
  task_runner->ClearPendingTasks();
  timer->SetActive(true);
  EXPECT_FALSE(task_runner->HasPendingTask());
}

// At 60Hz, when the tick returns at exactly the requested next time, make sure
// a 16ms next delay is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  timer->SetNow(timer->Now() + Interval());
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}

// At 60Hz, when the tick returns at slightly after the requested next time,
// make sure a 16ms next delay is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  timer->SetNow(timer->Now() + Interval() +
                base::TimeDelta::FromMicroseconds(1));
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}

// At 60Hz, when the tick returns at exactly 2*interval after the requested next
// time, make sure a 0ms next delay is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  timer->SetNow(timer->Now() + 2 * Interval());
  task_runner->RunPendingTasks();

  EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds());
}

// At 60Hz, when the tick returns at 2*interval and a bit after the requested
// next time, make sure a 16ms next delay is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  timer->SetNow(timer->Now() + 2 * Interval() +
                base::TimeDelta::FromMicroseconds(1));
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}

// At 60Hz, when the tick returns halfway to the next frame time, make sure
// a correct next delay value is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  timer->SetNow(timer->Now() + Interval() +
                base::TimeDelta::FromMilliseconds(8));
  task_runner->RunPendingTasks();

  EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds());
}

// If the timebase and interval are updated with a jittery source, we want to
// make sure we do not double tick.
TEST(DelayBasedTimeSource, SaneHandlingOfJitteryTimebase) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Jitter timebase ~1ms late
  timer->SetNow(timer->Now() + Interval());
  timer->SetTimebaseAndInterval(
      timer->Now() + base::TimeDelta::FromMilliseconds(1), Interval());
  task_runner->RunPendingTasks();

  // Without double tick prevention, NextPendingTaskDelay would be 1.
  EXPECT_EQ(17, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Jitter timebase ~1ms early
  timer->SetNow(timer->Now() + Interval());
  timer->SetTimebaseAndInterval(
      timer->Now() - base::TimeDelta::FromMilliseconds(1), Interval());
  task_runner->RunPendingTasks();

  EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick, then shift timebase by +7ms.
  timer->SetNow(timer->Now() + Interval());
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  client.Reset();
  task_runner->ClearPendingTasks();
  task_runner->RunPendingTasks();
  base::TimeDelta jitter = base::TimeDelta::FromMilliseconds(7) +
                           base::TimeDelta::FromMicroseconds(1);
  timer->SetTimebaseAndInterval(timer->Now() + jitter, Interval());

  EXPECT_FALSE(client.TickCalled());  // Make sure pending tasks were canceled.
  EXPECT_EQ(16 + 7, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick, then shift timebase by -7ms.
  timer->SetNow(timer->Now() + Interval() + jitter);
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  client.Reset();
  task_runner->ClearPendingTasks();
  task_runner->RunPendingTasks();
  timer->SetTimebaseAndInterval(base::TimeTicks() + Interval(), Interval());

  EXPECT_FALSE(client.TickCalled());  // Make sure pending tasks were canceled.
  EXPECT_EQ(16 - 7, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);
  // Run the first tick.
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick, then double the interval.
  timer->SetNow(timer->Now() + Interval());
  task_runner->RunPendingTasks();

  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  client.Reset();
  task_runner->ClearPendingTasks();
  task_runner->RunPendingTasks();
  timer->SetTimebaseAndInterval(base::TimeTicks() + Interval(), Interval() * 2);

  EXPECT_FALSE(client.TickCalled());  // Make sure pending tasks were canceled.
  EXPECT_EQ(33, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick, then halve the interval.
  timer->SetNow(timer->Now() + Interval() * 2);
  task_runner->RunPendingTasks();

  EXPECT_EQ(33, task_runner->NextPendingTaskDelay().InMilliseconds());

  client.Reset();
  task_runner->ClearPendingTasks();
  task_runner->RunPendingTasks();
  timer->SetTimebaseAndInterval(base::TimeTicks() + Interval() * 3, Interval());

  EXPECT_FALSE(client.TickCalled());  // Make sure pending tasks were canceled.
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSource, JitteryRuntimeWithFutureTimebases) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);

  // Run the first tick.
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  base::TimeTicks future_timebase = timer->Now() + Interval() * 10;

  // 1ms jitter
  base::TimeDelta jitter1 = base::TimeDelta::FromMilliseconds(1);

  // Tick with +1ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter1);
  task_runner->RunPendingTasks();
  EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter1);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with -1ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter1);
  task_runner->RunPendingTasks();
  EXPECT_EQ(17, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter1);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // 8 ms jitter
  base::TimeDelta jitter8 = base::TimeDelta::FromMilliseconds(8);

  // Tick with +8ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter8);
  task_runner->RunPendingTasks();
  EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter8);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with -8ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter8);
  task_runner->RunPendingTasks();
  EXPECT_EQ(24, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter8);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // 15 ms jitter
  base::TimeDelta jitter15  = base::TimeDelta::FromMilliseconds(15);

  // Tick with +15ms jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter15);
  task_runner->RunPendingTasks();
  EXPECT_EQ(1, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter15);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with -15ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() - jitter15);
  task_runner->RunPendingTasks();
  EXPECT_EQ(31, task_runner->NextPendingTaskDelay().InMilliseconds());

  // Tick with 0ms of jitter
  future_timebase += Interval();
  timer->SetTimebaseAndInterval(future_timebase, Interval());
  timer->SetNow(timer->Now() + Interval() + jitter15);
  task_runner->RunPendingTasks();
  EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) {
  int num_iterations = 10;

  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);

  double total_frame_time = 0.0;
  for (int i = 0; i < num_iterations; ++i) {
    int64 delay_ms = task_runner->NextPendingTaskDelay().InMilliseconds();

    // accumulate the "delay"
    total_frame_time += delay_ms / 1000.0;

    // Run the callback exactly when asked
    timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(delay_ms));
    task_runner->RunPendingTasks();
  }
  double average_interval =
      total_frame_time / static_cast<double>(num_iterations);
  EXPECT_NEAR(1.0 / 60.0, average_interval, 0.1);
}

TEST(DelayBasedTimeSource, TestDeactivateWhilePending) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);
  timer->SetActive(true);  // Should post a task.
  timer->SetActive(false);
  timer = NULL;
  // Should run the posted task without crashing.
  EXPECT_TRUE(task_runner->HasPendingTask());
  task_runner->RunPendingTasks();
}

TEST(DelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);

  // Should run the activate task, and pick up a new timebase.
  timer->SetActive(true);
  task_runner->RunPendingTasks();

  // Stop the timer
  timer->SetActive(false);

  // Task will be pending anyway, run it
  task_runner->RunPendingTasks();

  // Start the timer again, but before the next tick time the timer previously
  // planned on using. That same tick time should still be targeted.
  timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(4));
  timer->SetActive(true);
  EXPECT_EQ(12, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) {
  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
  timer->SetClient(&client);

  // Should run the activate task, and pick up a new timebase.
  timer->SetActive(true);
  task_runner->RunPendingTasks();

  // Stop the timer.
  timer->SetActive(false);

  // Task will be pending anyway, run it.
  task_runner->RunPendingTasks();

  // Start the timer again, but before the next tick time the timer previously
  // planned on using. That same tick time should still be targeted.
  timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(20));
  timer->SetActive(true);
  EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds());
}

TEST(DelayBasedTimeSource, TestOverflow) {
  // int(big_now / interval) < 0, so this causes a crash if the number of
  // intervals elapsed is attempted to be stored in an int.
  base::TimeDelta interval = base::TimeDelta::FromInternalValue(4000);
  base::TimeTicks big_now = base::TimeTicks::FromInternalValue(8635916564000);

  scoped_refptr<base::TestSimpleTaskRunner> task_runner =
      new base::TestSimpleTaskRunner;
  FakeTimeSourceClient client;
  scoped_refptr<FakeDelayBasedTimeSource> timer =
      FakeDelayBasedTimeSource::Create(interval, task_runner.get());
  timer->SetClient(&client);
  timer->SetNow(big_now);
  timer->SetActive(true);
  EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds());
}

}  // namespace
}  // namespace cc

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