forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathprinter_config_cache_unittest.cc
430 lines (374 loc) · 18.4 KB
/
printer_config_cache_unittest.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
// Copyright 2020 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 "chromeos/printing/printer_config_cache.h"
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "net/http/http_status_code.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// Maintainer's notes:
//
// 1. The use of base::Unretained throughout this suite is appropriate
// because the sequences of each test live as long as the test does.
// Real consumers probably can't do this.
// 2. The passage of time is controlled by a mock clock, so most Fetch()
// invocations not preceded by clock advancement never hit the
// "networked fetch" codepath. In such tests, the values of the
// TimeDelta argument are arbitrary and meaningless.
namespace chromeos {
namespace {
// Defines some resources (URLs and contents) used throughout this
// test suite.
// Name of the "known-good" resource.
const char kKnownGoodResourceURL[] =
"https://printerconfigurations.googleusercontent.com/chromeos_printing/"
"known-good";
// Arbitrary content for the "known-good" resource.
const char kKnownGoodResourceContent[] = "yakisaba";
// Name of the "known-bad" resource.
const char kKnownBadResourceURL[] =
"https://printerconfigurations.googleusercontent.com/chromeos_printing/"
"known-bad";
// Defines an arbitrary time increment by which we advance the Clock.
constexpr base::TimeDelta kTestingIncrement = base::TimeDelta::FromSeconds(1LL);
// Defines a time of fetch used to construct FetchResult instances that
// you'll use with the TimeInsensitiveFetchResultEquals matcher.
constexpr base::Time kUnusedTimeOfFetch;
MATCHER_P(TimeInsensitiveFetchResultEquals, expected, "") {
return arg.succeeded == expected.succeeded && arg.key == expected.key &&
arg.contents == expected.contents;
}
MATCHER_P(FetchResultEquals, expected, "") {
return arg.succeeded == expected.succeeded && arg.key == expected.key &&
arg.contents == expected.contents &&
arg.time_of_fetch == expected.time_of_fetch;
}
class PrinterConfigCacheTest : public ::testing::Test {
public:
// Creates |this| with
// * a testing task environment for testing sequenced code,
// * a testing clock for time-aware testing, and
// * a loader factory dispenser (specified by header comment on
// Create()).
PrinterConfigCacheTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
cache_(PrinterConfigCache::Create(
&clock_,
base::BindLambdaForTesting([&]() {
return reinterpret_cast<network::mojom::URLLoaderFactory*>(
&loader_factory_);
}))) {}
// Sets up the default responses to dispense.
void SetUp() override {
// Dispenses the "known-good" resource with its content.
loader_factory_.AddResponse(kKnownGoodResourceURL,
kKnownGoodResourceContent);
// Dispenses the "known-bad" resource with no content and an
// arbitrary HTTP error.
loader_factory_.AddResponse(kKnownBadResourceURL, "",
net::HTTP_NOT_ACCEPTABLE);
}
// Method passed as a FetchCallback (partially bound) to
// cache_.Fetch(). Saves the |result| in the |fetched_results_|.
// Invokes the |quit_closure| to signal the enclosing RunLoop that
// this method has been called.
void CaptureFetchResult(base::RepeatingClosure quit_closure,
const PrinterConfigCache::FetchResult& result) {
fetched_results_.push_back(result);
// The caller may elect to pass a default-constructed
// RepeatingClosure, indicating that they don't want anything run.
if (quit_closure) {
quit_closure.Run();
}
}
void AdvanceClock(base::TimeDelta amount = kTestingIncrement) {
clock_.Advance(amount);
}
protected:
// Landing area used to collect Fetch()ed results.
std::vector<PrinterConfigCache::FetchResult> fetched_results_;
// Loader factory for testing loaned to |cache_|.
network::TestURLLoaderFactory loader_factory_;
// Environment for task schedulers.
base::test::TaskEnvironment task_environment_;
// Controlled clock that dispenses times of Fetch().
base::SimpleTestClock clock_;
// Class under test.
std::unique_ptr<PrinterConfigCache> cache_;
};
// Tests that we can succeed in Fetch()ing anything at all.
TEST_F(PrinterConfigCacheTest, SucceedAtSingleFetch) {
base::RunLoop run_loop;
// Fetches the "known-good" resource.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this), run_loop.QuitClosure())));
run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 1ULL);
EXPECT_THAT(
fetched_results_.front(),
TimeInsensitiveFetchResultEquals(PrinterConfigCache::FetchResult::Success(
"known-good", "yakisaba", kUnusedTimeOfFetch)));
}
// Tests that we fail to Fetch() the "known-bad" resource.
TEST_F(PrinterConfigCacheTest, FailAtSingleFetch) {
base::RunLoop run_loop;
// Fetches the "known-bad" resource.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-bad", base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this), run_loop.QuitClosure())));
run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 1ULL);
EXPECT_THAT(fetched_results_.front(),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Failure("known-bad")));
}
// Tests that we can force a networked Fetch() by demanding
// fresh content.
TEST_F(PrinterConfigCacheTest, RefreshSubsequentFetch) {
// Fetches the "known-good" resource with its stock contents.
base::RunLoop first_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
first_run_loop.QuitClosure())));
first_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 1ULL);
// To detect a networked fetch, we'll change the served content
// and check that the subsequent Fetch() recovers the new content.
loader_factory_.AddResponse(kKnownGoodResourceURL, "one Argentinian peso");
// We've mutated the content; now, this fetches the "known-good"
// resource with its new contents.
base::RunLoop second_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
second_run_loop.QuitClosure())));
second_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 2ULL);
EXPECT_THAT(
fetched_results_,
testing::ElementsAre(
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success(
"known-good", "one Argentinian peso", kUnusedTimeOfFetch))));
}
// Tests that we can Fetch() locally cached contents by specifying a
// wide age limit.
TEST_F(PrinterConfigCacheTest, LocallyPerformSubsequentFetch) {
// Fetches the "known-good" resource with its stock contents.
base::RunLoop first_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
first_run_loop.QuitClosure())));
first_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 1ULL);
// As in the RefreshSubsequentFetch test, we'll change the served
// content to detect networked fetch requests made.
loader_factory_.AddResponse(kKnownGoodResourceURL, "apologize darn you");
// The "live" content in the serving root has changed; now, we perform
// some local fetches without hitting the network. These Fetch()es
// will return the stock content.
base::RunLoop second_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good",
// Avoids hitting the network by using a long
// timeout. Bear in mind that this test controls
// the passage of time, so nonzero timeout is
// "long" here...
base::TimeDelta::FromSeconds(1LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
// Avoids quitting this RunLoop.
base::RepeatingClosure())));
// Performs a local Fetch() a few more times for no particular reason.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(3600LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this), base::RepeatingClosure())));
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(86400LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this), base::RepeatingClosure())));
// Performs a live Fetch(), returning the live (mutated) contents.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good",
// Forces the networked fetch.
base::TimeDelta::FromSeconds(0LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
// Ends our RunLoop.
second_run_loop.QuitClosure())));
second_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 5ULL);
EXPECT_THAT(
fetched_results_,
testing::ElementsAre(
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success(
"known-good", "apologize darn you", kUnusedTimeOfFetch))));
}
// Tests that Fetch() respects its |expiration| argument. This is a
// purely time-bound variation on the LocallyPerformSubsequentFetch
// test; the served content doesn't change between RunLoops.
TEST_F(PrinterConfigCacheTest, FetchExpirationIsRespected) {
// This Fetch() is given a useful |expiration|, but it won't matter
// here since there are no locally resident cache entries at this
// time; it'll have to be a networked fetch.
base::RunLoop first_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(32LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
first_run_loop.QuitClosure())));
first_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 1ULL);
const base::Time time_zero = clock_.Now();
// Advance clock to T+31.
AdvanceClock(base::TimeDelta::FromSeconds(31LL));
// This Fetch() is given the same useful |expiration|; it only matters
// in that the clock does not yet indicate that the locally resident
// cache entry has expired.
base::RunLoop second_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(32LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
second_run_loop.QuitClosure())));
second_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 2ULL);
// We don't capture the time right Now() because the above Fetch()
// should have replied with local contents, fetched at time_zero.
// Advance clock to T+32.
AdvanceClock(base::TimeDelta::FromSeconds(1));
// This third Fetch() will be given the same |expiration| as ever.
// The two previous calls to AdvanceClock() will have moved the time
// beyond the staleness threshold, though, so this Fetch() will be
// networked.
base::RunLoop third_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good",
// Entry fetched at T+0 is now stale at T+32.
base::TimeDelta::FromSeconds(32LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
third_run_loop.QuitClosure())));
third_run_loop.Run();
ASSERT_EQ(fetched_results_.size(), 3ULL);
const base::Time time_of_third_fetch = clock_.Now();
EXPECT_THAT(fetched_results_,
testing::ElementsAre(
FetchResultEquals(PrinterConfigCache::FetchResult::Success(
"known-good", "yakisaba", time_zero)),
FetchResultEquals(PrinterConfigCache::FetchResult::Success(
"known-good", "yakisaba", time_zero)),
FetchResultEquals(PrinterConfigCache::FetchResult::Success(
"known-good", "yakisaba", time_of_third_fetch))));
}
// Tests that we can Drop() locally cached contents.
TEST_F(PrinterConfigCacheTest, DropLocalContents) {
base::RunLoop first_run_loop;
// Fetches the "known-good" resource with its stock contents.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(604800LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
first_run_loop.QuitClosure())));
first_run_loop.Run();
// Drops that which we just fetched. This isn't immediately externally
// visible, but its effects will soon be made apparent.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&PrinterConfigCache::Drop,
base::Unretained(cache_.get()), "known-good"));
// Mutates the contents served for the "known-good" resource.
loader_factory_.AddResponse(kKnownGoodResourceURL, "ultimate dogeza");
// Fetches the "known-good" resource anew with a wide timeout.
// This is where the side effect of the prior Drop() call manifests:
// the "known-good" resource is no longer cached, so not even a wide
// timeout will spare us a networked fetch.
base::RunLoop second_run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&PrinterConfigCache::Fetch, base::Unretained(cache_.get()),
"known-good", base::TimeDelta::FromSeconds(18748800LL),
base::BindOnce(&PrinterConfigCacheTest::CaptureFetchResult,
base::Unretained(this),
second_run_loop.QuitClosure())));
second_run_loop.Run();
// We detect the networked fetch to by observing mutated
// contents.
ASSERT_EQ(fetched_results_.size(), 2ULL);
EXPECT_THAT(
fetched_results_,
testing::ElementsAre(
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success("known-good", "yakisaba",
kUnusedTimeOfFetch)),
TimeInsensitiveFetchResultEquals(
PrinterConfigCache::FetchResult::Success(
"known-good", "ultimate dogeza", kUnusedTimeOfFetch))));
}
} // namespace
} // namespace chromeos