Commit a036278c authored by Kalvin Lee's avatar Kalvin Lee Committed by Commit Bot

printing: implement PrinterConfigCache

This change implements the PrinterConfigCache and provides unit tests
for the same.

Bug: 888189
Change-Id: I01ca78b196fdc3392464f7c033bfa2b2db4b2a00
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2013321
Commit-Queue: Kalvin Lee <kdlee@chromium.org>
Reviewed-by: default avatarSean Kau <skau@chromium.org>
Reviewed-by: default avatarLuum Habtemariam <luum@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746892}
parent 0a3d2ad4
...@@ -91,7 +91,11 @@ component("chromeos") { ...@@ -91,7 +91,11 @@ component("chromeos") {
} }
if (is_printing_ppd_provider_v3) { if (is_printing_ppd_provider_v3) {
sources += [ "printing/ppd_provider_v3.cc" ] sources += [
"printing/ppd_provider_v3.cc",
"printing/printer_config_cache.cc",
"printing/printer_config_cache.h",
]
} else { } else {
sources += [ "printing/ppd_provider.cc" ] sources += [ "printing/ppd_provider.cc" ]
} }
...@@ -194,7 +198,6 @@ test("chromeos_unittests") { ...@@ -194,7 +198,6 @@ test("chromeos_unittests") {
"printing/epson_driver_matching_unittest.cc", "printing/epson_driver_matching_unittest.cc",
"printing/ppd_cache_unittest.cc", "printing/ppd_cache_unittest.cc",
"printing/ppd_line_reader_unittest.cc", "printing/ppd_line_reader_unittest.cc",
"printing/ppd_provider_unittest.cc",
"printing/printer_configuration_unittest.cc", "printing/printer_configuration_unittest.cc",
"printing/printer_translator_unittest.cc", "printing/printer_translator_unittest.cc",
"printing/usb_printer_id_unittest.cc", "printing/usb_printer_id_unittest.cc",
...@@ -203,6 +206,12 @@ test("chromeos_unittests") { ...@@ -203,6 +206,12 @@ test("chromeos_unittests") {
"test/run_all_unittests.cc", "test/run_all_unittests.cc",
] ]
if (is_printing_ppd_provider_v3) {
sources += [ "printing/printer_config_cache_unittest.cc" ]
} else {
sources += [ "printing/ppd_provider_unittest.cc" ]
}
data = [ "test/data/" ] data = [ "test/data/" ]
} }
......
...@@ -91,8 +91,8 @@ scoped_refptr<PpdProvider> PpdProvider::Create( ...@@ -91,8 +91,8 @@ scoped_refptr<PpdProvider> PpdProvider::Create(
const PpdProvider::Options& options) { const PpdProvider::Options& options) {
// TODO(crbug.com/888189): use |loader_factory| and do away with // TODO(crbug.com/888189): use |loader_factory| and do away with
// |ppd_cache|. // |ppd_cache|.
return base::MakeRefCounted<PpdProvider>(browser_locale, current_version, return base::MakeRefCounted<PpdProviderImpl>(browser_locale, current_version,
options); options);
} }
} // namespace chromeos } // namespace chromeos
// 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 <memory>
#include <utility>
#include <vector>
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/task/post_task.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "url/gurl.h"
namespace chromeos {
namespace {
// Defines the serving root in which all PPDs and PPD metadata reside.
const char kServingRoot[] =
"https://printerconfigurations.googleusercontent.com/"
"chromeos_printing/";
// Prepends the serving root to |name|, returning the result.
std::string PrependServingRoot(const std::string& name) {
return base::StrCat({base::StringPiece(kServingRoot), name});
}
// Accepts a relative |path| to a value in the Chrome OS Printing
// serving root) and returns a resource request to satisfy the same.
std::unique_ptr<network::ResourceRequest> FormRequest(const std::string& path) {
GURL full_url(PrependServingRoot(path));
if (!full_url.is_valid()) {
return nullptr;
}
auto request = std::make_unique<network::ResourceRequest>();
request->url = full_url;
request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
request->credentials_mode = network::mojom::CredentialsMode::kOmit;
return request;
}
} // namespace
// In case of fetch failure, only the key is meaningful feedback.
// static
PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Failure(
const std::string& key) {
return PrinterConfigCache::FetchResult{false, key, std::string(),
base::Time()};
}
// static
PrinterConfigCache::FetchResult PrinterConfigCache::FetchResult::Success(
const std::string& key,
const std::string& contents,
base::Time time_of_fetch) {
return PrinterConfigCache::FetchResult{true, key, contents, time_of_fetch};
}
class PrinterConfigCacheImpl : public PrinterConfigCache {
public:
explicit PrinterConfigCacheImpl(
const base::Clock* clock,
network::mojom::URLLoaderFactory* loader_factory)
: clock_(clock), loader_factory_(loader_factory), weak_factory_(this) {}
~PrinterConfigCacheImpl() override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void Fetch(const std::string& key,
base::TimeDelta expiration,
FetchCallback cb) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Try to answer this fetch request locally.
const auto& finding = cache_.find(key);
if (finding != cache_.end()) {
const Entry& entry = finding->second;
if (entry.time_of_fetch + expiration > clock_->Now()) {
std::move(cb).Run(
FetchResult::Success(key, entry.contents, entry.time_of_fetch));
return;
}
}
// We couldn't answer this request locally. Issue a networked fetch
// and defer the answer to when we hear back.
auto context = std::make_unique<FetchContext>(key, std::move(cb));
fetch_queue_.push(std::move(context));
TryToStartNetworkedFetch();
}
void Drop(const std::string& key) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cache_.erase(key);
}
private:
// A FetchContext saves off the key and the FetchCallback that a
// caller passes to PrinterConfigCacheImpl::Fetch().
struct FetchContext {
const std::string key;
PrinterConfigCache::FetchCallback cb;
FetchContext(const std::string& arg_key,
PrinterConfigCache::FetchCallback arg_cb)
: key(arg_key), cb(std::move(arg_cb)) {}
~FetchContext() = default;
};
// If a PrinterConfigCache maps keys to values, then Entry structs
// represent values.
struct Entry {
std::string contents;
base::Time time_of_fetch;
Entry(const std::string& arg_contents, base::Time time)
: contents(arg_contents), time_of_fetch(time) {}
~Entry() = default;
};
void TryToStartNetworkedFetch() {
// Either
// 1. a networked fetch is already in flight or
// 2. there are no more pending networked fetches to act upon.
// In either case, we can't do anything at the moment; back off
// and let a future call to Fetch() or FinishNetworkedFetch()
// return here to try again.
if (fetcher_ || fetch_queue_.empty()) {
return;
}
std::unique_ptr<FetchContext> context = std::move(fetch_queue_.front());
fetch_queue_.pop();
auto request = FormRequest(context->key);
// TODO(crbug.com/888189): add traffic annotation.
fetcher_ = network::SimpleURLLoader::Create(std::move(request),
MISSING_TRAFFIC_ANNOTATION);
fetcher_->DownloadToString(
loader_factory_,
base::BindOnce(&PrinterConfigCacheImpl::FinishNetworkedFetch,
weak_factory_.GetWeakPtr(), std::move(context)),
network::SimpleURLLoader::kMaxBoundedStringDownloadSize);
}
// Called by |fetcher_| once DownloadToString() completes.
void FinishNetworkedFetch(std::unique_ptr<FetchContext> context,
std::unique_ptr<std::string> contents) {
// Wherever |fetcher_| works its sorcery, it had better have posted
// back onto _our_ sequence.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (fetcher_->NetError() == net::Error::OK) {
// We only want to update our local cache if the |fetcher_|
// succeeded; otherwise, prefer to either retain the stale entry
// (if extant) or retain no entry at all (if not).
const Entry newly_inserted = Entry(*contents, clock_->Now());
cache_.insert_or_assign(context->key, newly_inserted);
std::move(context->cb)
.Run(FetchResult::Success(context->key, newly_inserted.contents,
newly_inserted.time_of_fetch));
} else {
std::move(context->cb).Run(FetchResult::Failure(context->key));
}
fetcher_.reset();
TryToStartNetworkedFetch();
}
// The heart of an PrinterConfigCache: the local cache itself.
base::flat_map<std::string, Entry> cache_;
// Enqueues networked requests.
base::queue<std::unique_ptr<FetchContext>> fetch_queue_;
// Dispenses Time objects to mark time of fetch on Entry instances.
const base::Clock* clock_;
// Mutably borrowed from caller at construct-time.
network::mojom::URLLoaderFactory* loader_factory_;
// Talks to the networked service to fetch resources.
//
// Because this class is sequenced, a non-nullptr value here (observed
// on-sequence) denotes an ongoing fetch. See the
// TryToStartNetworkedFetch() and FinishNetworkedFetch() methods.
std::unique_ptr<network::SimpleURLLoader> fetcher_;
SEQUENCE_CHECKER(sequence_checker_);
// Dispenses weak pointers to our |fetcher_|. This is necessary
// because |this| could be deleted while the loader is in flight
// off-sequence.
base::WeakPtrFactory<PrinterConfigCacheImpl> weak_factory_;
};
// static
std::unique_ptr<PrinterConfigCache> PrinterConfigCache::Create(
const base::Clock* clock,
network::mojom::URLLoaderFactory* loader_factory) {
return std::make_unique<PrinterConfigCacheImpl>(clock, loader_factory);
}
} // namespace chromeos
// 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.
//
// The PrinterConfigCache class accepts requests to fetch things from
// the Chrome OS Printing serving root. It only stores things in memory.
//
// In practice, the present class fetches either PPDs or PPD metadata.
#ifndef CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
#define CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
#include <memory>
#include <string>
#include "base/containers/flat_map.h"
#include "base/containers/queue.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "chromeos/chromeos_export.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace chromeos {
// A PrinterConfigCache maps keys to values. By convention, keys are
// relative paths to files in the Chrome OS Printing serving root
// (hardcoded into this class). In practice, that means keys will either
// * start with "metadata_v3/" or
// * start with "ppds_for_metadata_v3/."
//
// This class must always be constructed on, used on, and destroyed from
// a sequenced context.
//
// TODO(crbug.com/888189): remove CHROMEOS_EXPORT and refactor all this
// into a dedicated gn component.
class CHROMEOS_EXPORT PrinterConfigCache {
public:
static std::unique_ptr<PrinterConfigCache> Create(
const base::Clock* clock,
network::mojom::URLLoaderFactory* loader_factory);
virtual ~PrinterConfigCache() = default;
// Result of calling Fetch(). The |key| identifies how Fetch() was
// originally invoked. The |contents| and |time_of_fetch| are well-
// defined iff |succeeded| is true.
struct FetchResult {
static FetchResult Failure(const std::string& key);
static FetchResult Success(const std::string& key,
const std::string& contents,
base::Time time_of_fetch);
bool succeeded;
std::string key;
std::string contents;
base::Time time_of_fetch;
};
// Caller is responsible for providing sequencing of this type.
using FetchCallback = base::OnceCallback<void(const FetchResult&)>;
// Queries the Chrome OS Printing serving root for |key|. Calls |cb|
// with the contents. If an entry newer than |expiration| is resident,
// calls |cb| immediately with those contents. Caller should not pass
// keys with leading slashes.
//
// Using TimeDelta implies the caller is asking for "some entry not
// older than |expiration|," e.g. "metadata_v3/index-00.json that
// was fetched within the last 30 minutes."
//
// Naturally,
// * passing the Max() TimeDelta means "perform this Fetch() with no
// limit on staleness" and
// * passing a zero TimeDelta should practically force a networked
// fetch (less esoteric timing quirks etc.).
virtual void Fetch(const std::string& key,
base::TimeDelta expiration,
FetchCallback cb) = 0;
// Drops Entry corresponding to |key|.
virtual void Drop(const std::string& key) = 0;
};
} // namespace chromeos
#endif // CHROMEOS_PRINTING_PRINTER_CONFIG_CACHE_H_
// 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/callback_forward.h"
#include "base/location.h"
#include "base/run_loop.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:
PrinterConfigCacheTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
cache_(PrinterConfigCache::Create(&clock_, &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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment