Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/doodle/doodle_service.h" | 5 #include "components/doodle/doodle_service.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/memory/ref_counted.h" | |
| 14 #include "base/test/simple_test_tick_clock.h" | |
| 15 #include "base/test/test_simple_task_runner.h" | |
| 13 #include "base/time/time.h" | 16 #include "base/time/time.h" |
| 14 #include "testing/gmock/include/gmock/gmock.h" | 17 #include "testing/gmock/include/gmock/gmock.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 16 | 19 |
| 17 using testing::Eq; | 20 using testing::Eq; |
| 18 using testing::StrictMock; | 21 using testing::StrictMock; |
| 19 | 22 |
| 20 namespace doodle { | 23 namespace doodle { |
| 21 | 24 |
| 22 namespace { | 25 namespace { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 48 class MockDoodleObserver : public DoodleService::Observer { | 51 class MockDoodleObserver : public DoodleService::Observer { |
| 49 public: | 52 public: |
| 50 MOCK_METHOD1(OnDoodleConfigUpdated, | 53 MOCK_METHOD1(OnDoodleConfigUpdated, |
| 51 void(const base::Optional<DoodleConfig>&)); | 54 void(const base::Optional<DoodleConfig>&)); |
| 52 }; | 55 }; |
| 53 | 56 |
| 54 } // namespace | 57 } // namespace |
| 55 | 58 |
| 56 class DoodleServiceTest : public testing::Test { | 59 class DoodleServiceTest : public testing::Test { |
| 57 public: | 60 public: |
| 58 DoodleServiceTest() : fetcher_(nullptr) { | 61 DoodleServiceTest() : fetcher_(nullptr) { |
|
fhorschig
2017/03/02 20:12:59
expiry_timer_(nullptr)?
Marc Treib
2017/03/03 10:06:58
Indeed, thanks!
| |
| 62 tick_clock_.SetNowTicks(base::TimeTicks::UnixEpoch() + | |
| 63 base::TimeDelta::FromDays(5000)); | |
| 64 task_runner_ = new base::TestSimpleTaskRunner(); | |
|
fhorschig
2017/03/02 20:12:59
any reason you don't initialize this earlier?
(ini
Marc Treib
2017/03/03 10:06:57
Nope. Done.
| |
| 65 | |
| 66 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(&tick_clock_); | |
| 67 expiry_timer->SetTaskRunner(task_runner_); | |
| 68 expiry_timer_ = expiry_timer.get(); | |
| 69 | |
| 59 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); | 70 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); |
| 60 fetcher_ = fetcher.get(); | 71 fetcher_ = fetcher.get(); |
| 61 service_ = base::MakeUnique<DoodleService>(std::move(fetcher)); | 72 |
| 73 service_ = base::MakeUnique<DoodleService>(std::move(fetcher), | |
| 74 std::move(expiry_timer)); | |
| 62 } | 75 } |
| 63 | 76 |
| 64 DoodleService* service() { return service_.get(); } | 77 DoodleService* service() { return service_.get(); } |
| 65 FakeDoodleFetcher* fetcher() { return fetcher_; } | 78 FakeDoodleFetcher* fetcher() { return fetcher_; } |
| 66 | 79 |
| 67 base::TimeDelta some_time() const { return base::TimeDelta::FromHours(1); } | 80 base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); } |
| 81 | |
| 82 base::TimeDelta SomeTime() const { return base::TimeDelta::FromHours(1); } | |
|
fhorschig
2017/03/02 20:12:59
When it was lower_case, it used to be pretty clear
Marc Treib
2017/03/03 10:06:58
I don't know what you mean by "implementation deta
| |
| 68 | 83 |
| 69 private: | 84 private: |
| 85 // Weak, owned by the service. | |
| 86 FakeDoodleFetcher* fetcher_; | |
| 87 | |
| 88 base::SimpleTestTickClock tick_clock_; | |
| 89 scoped_refptr<base::TestSimpleTaskRunner> task_runner_; | |
| 90 // Weak, owned by the service. | |
| 91 base::OneShotTimer* expiry_timer_; | |
|
fhorschig
2017/03/02 20:12:59
nitty nit: Would be nice to visually group the wea
Marc Treib
2017/03/03 10:06:58
To me, the "logical" grouping (clock + task_runner
| |
| 92 | |
| 70 std::unique_ptr<DoodleService> service_; | 93 std::unique_ptr<DoodleService> service_; |
| 71 FakeDoodleFetcher* fetcher_; | |
| 72 }; | 94 }; |
| 73 | 95 |
| 74 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { | 96 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { |
| 75 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 97 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
| 76 | 98 |
| 77 // Request a refresh of the doodle config. | 99 // Request a refresh of the doodle config. |
| 78 service()->Refresh(); | 100 service()->Refresh(); |
| 79 // The request should have arrived at the fetcher. | 101 // The request should have arrived at the fetcher. |
| 80 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 102 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 81 | 103 |
| 82 // Serve it (with an arbitrary config). | 104 // Serve it (with an arbitrary config). |
| 83 DoodleConfig config; | 105 DoodleConfig config; |
| 84 config.doodle_type = DoodleType::SIMPLE; | 106 config.doodle_type = DoodleType::SIMPLE; |
| 85 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), config); | 107 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); |
| 86 | 108 |
| 87 // The config should be available. | 109 // The config should be available. |
| 88 EXPECT_THAT(service()->config(), Eq(config)); | 110 EXPECT_THAT(service()->config(), Eq(config)); |
| 89 | 111 |
| 90 // Request a refresh again. | 112 // Request a refresh again. |
| 91 service()->Refresh(); | 113 service()->Refresh(); |
| 92 // The request should have arrived at the fetcher again. | 114 // The request should have arrived at the fetcher again. |
| 93 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 115 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 94 | 116 |
| 95 // Serve it with a different config. | 117 // Serve it with a different config. |
| 96 DoodleConfig other_config; | 118 DoodleConfig other_config; |
| 97 other_config.doodle_type = DoodleType::SLIDESHOW; | 119 other_config.doodle_type = DoodleType::SLIDESHOW; |
| 98 DCHECK(config != other_config); | 120 DCHECK(config != other_config); |
| 99 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), | 121 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), |
| 100 other_config); | 122 other_config); |
| 101 | 123 |
| 102 // The config should have been updated. | 124 // The config should have been updated. |
| 103 EXPECT_THAT(service()->config(), Eq(other_config)); | 125 EXPECT_THAT(service()->config(), Eq(other_config)); |
| 104 } | 126 } |
| 105 | 127 |
| 106 TEST_F(DoodleServiceTest, CallsObserverOnConfigReceived) { | 128 TEST_F(DoodleServiceTest, CallsObserverOnConfigReceived) { |
| 107 StrictMock<MockDoodleObserver> observer; | 129 StrictMock<MockDoodleObserver> observer; |
| 108 service()->AddObserver(&observer); | 130 service()->AddObserver(&observer); |
| 109 | 131 |
| 110 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 132 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
| 111 | 133 |
| 112 // Request a refresh of the doodle config. | 134 // Request a refresh of the doodle config. |
| 113 service()->Refresh(); | 135 service()->Refresh(); |
| 114 // The request should have arrived at the fetcher. | 136 // The request should have arrived at the fetcher. |
| 115 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 137 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 116 | 138 |
| 117 // Serve it (with an arbitrary config). The observer should get notified. | 139 // Serve it (with an arbitrary config). The observer should get notified. |
| 118 DoodleConfig config; | 140 DoodleConfig config; |
| 119 config.doodle_type = DoodleType::SIMPLE; | 141 config.doodle_type = DoodleType::SIMPLE; |
| 120 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(config))); | 142 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(config))); |
| 121 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), config); | 143 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); |
| 122 | 144 |
| 123 // Remove the observer before the service gets destroyed. | 145 // Remove the observer before the service gets destroyed. |
| 124 service()->RemoveObserver(&observer); | 146 service()->RemoveObserver(&observer); |
| 125 } | 147 } |
| 126 | 148 |
| 127 TEST_F(DoodleServiceTest, CallsObserverOnConfigRemoved) { | 149 TEST_F(DoodleServiceTest, CallsObserverOnConfigRemoved) { |
| 128 // Load some doodle config. | 150 // Load some doodle config. |
| 129 service()->Refresh(); | 151 service()->Refresh(); |
| 130 DoodleConfig config; | 152 DoodleConfig config; |
| 131 config.doodle_type = DoodleType::SIMPLE; | 153 config.doodle_type = DoodleType::SIMPLE; |
| 132 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), config); | 154 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); |
| 133 ASSERT_THAT(service()->config(), Eq(config)); | 155 ASSERT_THAT(service()->config(), Eq(config)); |
| 134 | 156 |
| 135 // Register an observer and request a refresh. | 157 // Register an observer and request a refresh. |
| 136 StrictMock<MockDoodleObserver> observer; | 158 StrictMock<MockDoodleObserver> observer; |
| 137 service()->AddObserver(&observer); | 159 service()->AddObserver(&observer); |
| 138 service()->Refresh(); | 160 service()->Refresh(); |
| 139 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 161 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 140 | 162 |
| 141 // Serve the request with an empty doodle config. The observer should get | 163 // Serve the request with an empty doodle config. The observer should get |
| 142 // notified. | 164 // notified. |
| 143 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(base::nullopt))); | 165 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(base::nullopt))); |
| 144 fetcher()->ServeAllCallbacks(DoodleState::NO_DOODLE, base::TimeDelta(), | 166 fetcher()->ServeAllCallbacks(DoodleState::NO_DOODLE, base::TimeDelta(), |
| 145 base::nullopt); | 167 base::nullopt); |
| 146 | 168 |
| 147 // Remove the observer before the service gets destroyed. | 169 // Remove the observer before the service gets destroyed. |
| 148 service()->RemoveObserver(&observer); | 170 service()->RemoveObserver(&observer); |
| 149 } | 171 } |
| 150 | 172 |
| 151 TEST_F(DoodleServiceTest, CallsObserverOnConfigUpdated) { | 173 TEST_F(DoodleServiceTest, CallsObserverOnConfigUpdated) { |
| 152 // Load some doodle config. | 174 // Load some doodle config. |
| 153 service()->Refresh(); | 175 service()->Refresh(); |
| 154 DoodleConfig config; | 176 DoodleConfig config; |
| 155 config.doodle_type = DoodleType::SIMPLE; | 177 config.doodle_type = DoodleType::SIMPLE; |
| 156 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), config); | 178 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); |
| 157 ASSERT_THAT(service()->config(), Eq(config)); | 179 ASSERT_THAT(service()->config(), Eq(config)); |
| 158 | 180 |
| 159 // Register an observer and request a refresh. | 181 // Register an observer and request a refresh. |
| 160 StrictMock<MockDoodleObserver> observer; | 182 StrictMock<MockDoodleObserver> observer; |
| 161 service()->AddObserver(&observer); | 183 service()->AddObserver(&observer); |
| 162 service()->Refresh(); | 184 service()->Refresh(); |
| 163 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 185 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 164 | 186 |
| 165 // Serve the request with a different doodle config. The observer should get | 187 // Serve the request with a different doodle config. The observer should get |
| 166 // notified. | 188 // notified. |
| 167 DoodleConfig other_config; | 189 DoodleConfig other_config; |
| 168 other_config.doodle_type = DoodleType::SLIDESHOW; | 190 other_config.doodle_type = DoodleType::SLIDESHOW; |
| 169 DCHECK(config != other_config); | 191 DCHECK(config != other_config); |
| 170 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(other_config))); | 192 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(other_config))); |
| 171 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), | 193 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), |
| 172 other_config); | 194 other_config); |
| 173 | 195 |
| 174 // Remove the observer before the service gets destroyed. | 196 // Remove the observer before the service gets destroyed. |
| 175 service()->RemoveObserver(&observer); | 197 service()->RemoveObserver(&observer); |
| 176 } | 198 } |
| 177 | 199 |
| 178 TEST_F(DoodleServiceTest, DoesNotCallObserverWhenConfigEquivalent) { | 200 TEST_F(DoodleServiceTest, DoesNotCallObserverIfConfigEquivalent) { |
| 179 // Load some doodle config. | 201 // Load some doodle config. |
| 180 service()->Refresh(); | 202 service()->Refresh(); |
| 181 DoodleConfig config; | 203 DoodleConfig config; |
| 182 config.doodle_type = DoodleType::SIMPLE; | 204 config.doodle_type = DoodleType::SIMPLE; |
| 183 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), config); | 205 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); |
| 184 ASSERT_THAT(service()->config(), Eq(config)); | 206 ASSERT_THAT(service()->config(), Eq(config)); |
| 185 | 207 |
| 186 // Register an observer and request a refresh. | 208 // Register an observer and request a refresh. |
| 187 StrictMock<MockDoodleObserver> observer; | 209 StrictMock<MockDoodleObserver> observer; |
| 188 service()->AddObserver(&observer); | 210 service()->AddObserver(&observer); |
| 189 service()->Refresh(); | 211 service()->Refresh(); |
| 190 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 212 ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 191 | 213 |
| 192 // Serve the request with an equivalent doodle config. The observer should | 214 // Serve the request with an equivalent doodle config. The observer should |
| 193 // *not* get notified. | 215 // *not* get notified. |
| 194 DoodleConfig equivalent_config; | 216 DoodleConfig equivalent_config; |
| 195 equivalent_config.doodle_type = DoodleType::SIMPLE; | 217 equivalent_config.doodle_type = DoodleType::SIMPLE; |
| 196 DCHECK(config == equivalent_config); | 218 DCHECK(config == equivalent_config); |
| 197 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, some_time(), | 219 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), |
| 198 equivalent_config); | 220 equivalent_config); |
| 199 | 221 |
| 200 // Remove the observer before the service gets destroyed. | 222 // Remove the observer before the service gets destroyed. |
| 201 service()->RemoveObserver(&observer); | 223 service()->RemoveObserver(&observer); |
| 202 } | 224 } |
| 203 | 225 |
| 226 TEST_F(DoodleServiceTest, CallsObserverWhenConfigExpires) { | |
| 227 // Load some doodle config. | |
| 228 service()->Refresh(); | |
| 229 DoodleConfig config; | |
| 230 config.doodle_type = DoodleType::SIMPLE; | |
| 231 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); | |
| 232 ASSERT_THAT(service()->config(), Eq(config)); | |
| 233 | |
| 234 // Make sure the task arrived at the timer's task runner. | |
| 235 ASSERT_THAT(task_runner()->NumPendingTasks(), Eq(1u)); | |
| 236 EXPECT_THAT(task_runner()->NextPendingTaskDelay(), Eq(SomeTime())); | |
| 237 | |
| 238 // Register an observer. | |
| 239 StrictMock<MockDoodleObserver> observer; | |
| 240 service()->AddObserver(&observer); | |
| 241 | |
| 242 // Run the expiry task. The observer should get notified that there's no | |
| 243 // config anymore. | |
| 244 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(base::nullopt))); | |
| 245 task_runner()->RunPendingTasks(); | |
|
Marc Treib
2017/03/02 18:51:02
So, this is the famous and amazing TestSimpleTaskR
fhorschig
2017/03/02 20:12:59
What about TestMockTimeTaskRunner. As far as I kno
Marc Treib
2017/03/03 10:06:58
D'oh, forgot about that one again. Thanks for the
| |
| 246 | |
| 247 // Remove the observer before the service gets destroyed. | |
| 248 service()->RemoveObserver(&observer); | |
| 249 } | |
| 250 | |
| 251 TEST_F(DoodleServiceTest, DisregardsAlreadyExpiredConfigs) { | |
| 252 StrictMock<MockDoodleObserver> observer; | |
| 253 service()->AddObserver(&observer); | |
| 254 | |
| 255 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | |
| 256 | |
| 257 // Load an already-expired config. This should have no effect; in particular | |
| 258 // no call to the observer. | |
| 259 service()->Refresh(); | |
| 260 DoodleConfig config; | |
| 261 config.doodle_type = DoodleType::SIMPLE; | |
| 262 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 263 base::TimeDelta::FromSeconds(0), config); | |
| 264 EXPECT_THAT(service()->config(), Eq(base::nullopt)); | |
| 265 | |
| 266 // Load a doodle config as usual. Nothing to see here. | |
| 267 service()->Refresh(); | |
| 268 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(config))); | |
| 269 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, SomeTime(), config); | |
| 270 ASSERT_THAT(service()->config(), Eq(config)); | |
| 271 | |
| 272 // Now load an expired config again. The cached one should go away. | |
| 273 service()->Refresh(); | |
| 274 EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(base::nullopt))); | |
| 275 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 276 base::TimeDelta::FromSeconds(0), config); | |
| 277 | |
| 278 // Remove the observer before the service gets destroyed. | |
| 279 service()->RemoveObserver(&observer); | |
| 280 } | |
| 281 | |
| 204 } // namespace doodle | 282 } // namespace doodle |
| OLD | NEW |