1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/network_time/network_time_tracker.h"
6
7#include <math.h>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/compiler_specific.h"
12#include "base/message_loop/message_loop.h"
13#include "content/public/test/test_browser_thread.h"
14#include "net/base/network_time_notifier.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace {
18
19// These are all in milliseconds.
20const int64 kLatency1 = 50;
21const int64 kLatency2 = 500;
22
23// Can not be smaller than 15, it's the NowFromSystemTime() resolution.
24const int64 kResolution1 = 17;
25const int64 kResolution2 = 177;
26
27const int64 kPseudoSleepTime1 = 500000001;
28const int64 kPseudoSleepTime2 = 1888;
29
30// A custom tick clock that will return an arbitrary time.
31class TestTickClock : public base::TickClock {
32 public:
33  explicit TestTickClock(base::TimeTicks* ticks_now) : ticks_now_(ticks_now) {}
34  virtual ~TestTickClock() {}
35
36  virtual base::TimeTicks NowTicks() OVERRIDE {
37    return *ticks_now_;
38  }
39
40 private:
41  base::TimeTicks* ticks_now_;
42};
43
44}  // namespace
45
46class NetworkTimeTrackerTest : public testing::Test {
47 public:
48  NetworkTimeTrackerTest()
49      : ui_thread(content::BrowserThread::UI, &message_loop_),
50        io_thread(content::BrowserThread::IO, &message_loop_),
51        now_(base::Time::NowFromSystemTime()),
52        tick_clock_(new TestTickClock(&ticks_now_)),
53        network_time_notifier_(
54            new net::NetworkTimeNotifier(
55                tick_clock_.PassAs<base::TickClock>())) {}
56  virtual ~NetworkTimeTrackerTest() {}
57
58  virtual void TearDown() OVERRIDE {
59    message_loop_.RunUntilIdle();
60  }
61
62  base::Time Now() const {
63    return now_ + (ticks_now_ - base::TimeTicks());
64  }
65
66  base::TimeTicks TicksNow() const {
67    return ticks_now_;
68  }
69
70  void AddToTicksNow(int64 ms) {
71    ticks_now_ += base::TimeDelta::FromMilliseconds(ms);
72  }
73
74  void StartTracker() {
75    network_time_tracker_.reset(new NetworkTimeTracker());
76    network_time_notifier_->AddObserver(
77        network_time_tracker_->BuildObserverCallback());
78    message_loop_.RunUntilIdle();
79  }
80
81  void StopTracker() {
82    network_time_tracker_.reset();
83  }
84
85  // Updates the notifier's time with the specified parameters and waits until
86  // the observers have been updated.
87  void UpdateNetworkTime(const base::Time& network_time,
88                         const base::TimeDelta& resolution,
89                         const base::TimeDelta& latency,
90                         const base::TimeTicks& post_time) {
91    message_loop_.PostTask(
92        FROM_HERE,
93        base::Bind(&net::NetworkTimeNotifier::UpdateNetworkTime,
94                   base::Unretained(network_time_notifier_.get()),
95                   network_time,
96                   resolution,
97                   latency,
98                   post_time));
99    message_loop_.RunUntilIdle();
100  }
101
102  // Ensures the network time tracker has a network time and that the
103  // disparity between the network time version of |ticks_now_| and the actual
104  // |ticks_now_| value is within the uncertainty (should always be true
105  // because the network time notifier uses |ticks_now_| for the tick clock).
106  testing::AssertionResult ValidateExpectedTime() const {
107    base::Time network_time;
108    base::TimeDelta uncertainty;
109    if (!network_time_tracker_->GetNetworkTime(TicksNow(),
110                                               &network_time,
111                                               &uncertainty))
112      return testing::AssertionFailure() << "Failed to get network time.";
113    if (fabs(static_cast<double>(Now().ToInternalValue() -
114                                 network_time.ToInternalValue())) >
115             static_cast<double>(uncertainty.ToInternalValue())) {
116      return testing::AssertionFailure()
117          << "Expected network time not within uncertainty.";
118    }
119    return testing::AssertionSuccess();
120  }
121
122  NetworkTimeTracker* network_time_tracker() {
123    return network_time_tracker_.get();
124  }
125
126 private:
127  // Message loop and threads for the tracker's internal logic.
128  base::MessageLoop message_loop_;
129  content::TestBrowserThread ui_thread;
130  content::TestBrowserThread io_thread;
131
132  // Used in building the current time that |tick_clock_| reports. See Now()
133  // for details.
134  base::Time now_;
135  base::TimeTicks ticks_now_;
136
137  // A custom clock that allows arbitrary time delays.
138  scoped_ptr<TestTickClock> tick_clock_;
139
140  // The network time notifier that receives time updates and posts them to
141  // the tracker.
142  scoped_ptr<net::NetworkTimeNotifier> network_time_notifier_;
143
144  // The network time tracker being tested.
145  scoped_ptr<NetworkTimeTracker> network_time_tracker_;
146};
147
148// Should not return a value before UpdateNetworkTime gets called.
149TEST_F(NetworkTimeTrackerTest, Uninitialized) {
150  base::Time network_time;
151  base::TimeDelta uncertainty;
152  StartTracker();
153  EXPECT_FALSE(network_time_tracker()->GetNetworkTime(base::TimeTicks(),
154                                                      &network_time,
155                                                      &uncertainty));
156}
157
158// Verify that the the tracker receives and properly handles updates to the
159// network time.
160TEST_F(NetworkTimeTrackerTest, NetworkTimeUpdates) {
161  StartTracker();
162  UpdateNetworkTime(
163      Now(),
164      base::TimeDelta::FromMilliseconds(kResolution1),
165      base::TimeDelta::FromMilliseconds(kLatency1),
166      TicksNow());
167  EXPECT_TRUE(ValidateExpectedTime());
168
169  // Fake a wait for kPseudoSleepTime1 to make sure we keep tracking.
170  AddToTicksNow(kPseudoSleepTime1);
171  EXPECT_TRUE(ValidateExpectedTime());
172
173  // Update the time with a new now value and kLatency2.
174  UpdateNetworkTime(
175      Now(),
176      base::TimeDelta::FromMilliseconds(kResolution2),
177      base::TimeDelta::FromMilliseconds(kLatency2),
178      TicksNow());
179
180  // Fake a wait for kPseudoSleepTime2 to make sure we keep tracking still.
181  AddToTicksNow(kPseudoSleepTime2);
182  EXPECT_TRUE(ValidateExpectedTime());
183
184  // Fake a long delay between update task post time and the network notifier
185  // updating its network time. The uncertainty should account for the
186  // disparity.
187  base::Time old_now = Now();
188  base::TimeTicks old_ticks = TicksNow();
189  AddToTicksNow(kPseudoSleepTime2);
190  UpdateNetworkTime(
191      old_now,
192      base::TimeDelta::FromMilliseconds(kResolution2),
193      base::TimeDelta::FromMilliseconds(kLatency2),
194      old_ticks);
195  EXPECT_TRUE(ValidateExpectedTime());
196}
197
198// Starting the tracker after the network time has been set with the notifier
199// should update the tracker's time as well.
200TEST_F(NetworkTimeTrackerTest, UpdateThenStartTracker) {
201  UpdateNetworkTime(
202      Now(),
203      base::TimeDelta::FromMilliseconds(kResolution1),
204      base::TimeDelta::FromMilliseconds(kLatency1),
205      TicksNow());
206  StartTracker();
207  EXPECT_TRUE(ValidateExpectedTime());
208}
209
210// Time updates after the tracker has been destroyed should not attempt to
211// dereference the destroyed tracker.
212TEST_F(NetworkTimeTrackerTest, UpdateAfterTrackerDestroyed) {
213  StartTracker();
214  StopTracker();
215  UpdateNetworkTime(
216      Now(),
217      base::TimeDelta::FromMilliseconds(kResolution1),
218      base::TimeDelta::FromMilliseconds(kLatency1),
219      TicksNow());
220}
221