blob: 0dc6e06f303e2046e97ae38a05e0fd997392557e [file] [log] [blame]
// Copyright 2017 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 "services/device/geolocation/public_ip_address_geolocator.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/public/cpp/bindings/strong_binding_set.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
const char kTestGeolocationApiKey[] = "";
// Simple request context producer that immediately produces a
// TestURLRequestContextGetter.
void TestRequestContextProducer(
const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
response_callback) {
std::move(response_callback)
.Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
network_task_runner));
}
class PublicIpAddressGeolocatorTest : public testing::Test {
public:
PublicIpAddressGeolocatorTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO) {
notifier_.reset(new PublicIpAddressLocationNotifier(
base::Bind(&TestRequestContextProducer,
scoped_task_environment_.GetMainThreadTaskRunner()),
kTestGeolocationApiKey));
}
~PublicIpAddressGeolocatorTest() override = default;
protected:
void SetUp() override {
// Intercept Mojo bad-message errors.
mojo::edk::SetDefaultProcessErrorCallback(
base::Bind(&PublicIpAddressGeolocatorTest::OnMojoBadMessage,
base::Unretained(this)));
binding_set_.AddBinding(
std::make_unique<PublicIpAddressGeolocator>(
PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS, notifier_.get(),
base::Bind(&PublicIpAddressGeolocatorTest::OnGeolocatorBadMessage,
base::Unretained(this))),
mojo::MakeRequest(&public_ip_address_geolocator_));
}
void TearDown() override {
// Stop intercepting Mojo bad-message errors.
mojo::edk::SetDefaultProcessErrorCallback(
mojo::edk::ProcessErrorCallback());
}
// Deal with mojo bad message.
void OnMojoBadMessage(const std::string& error) {
bad_messages_.push_back(error);
}
// Deal with PublicIpAddressGeolocator bad message.
void OnGeolocatorBadMessage(const std::string& message) {
binding_set_.ReportBadMessage(message);
}
// Invokes QueryNextPosition on |public_ip_address_geolocator_|, and runs
// |done_closure| when the response comes back.
void QueryNextPosition(base::Closure done_closure) {
public_ip_address_geolocator_->QueryNextPosition(base::BindOnce(
&PublicIpAddressGeolocatorTest::OnQueryNextPositionResponse,
base::Unretained(this), done_closure));
}
// Callback for QueryNextPosition() that records the result in |position_| and
// then invokes |done_closure|.
void OnQueryNextPositionResponse(base::Closure done_closure,
mojom::GeopositionPtr position) {
position_ = std::move(position);
done_closure.Run();
}
// Result of the latest completed call to QueryNextPosition.
mojom::GeopositionPtr position_;
// StrongBindingSet to mojom::Geolocation.
mojo::StrongBindingSet<mojom::Geolocation> binding_set_;
// Test task runner.
base::test::ScopedTaskEnvironment scoped_task_environment_;
// List of any Mojo bad-message errors raised.
std::vector<std::string> bad_messages_;
// PublicIpAddressGeolocator requires a notifier.
std::unique_ptr<PublicIpAddressLocationNotifier> notifier_;
// The object under test.
mojom::GeolocationPtr public_ip_address_geolocator_;
// PublicIpAddressGeolocator implementation.
// std::unique_ptr<PublicIpAddressGeolocator> impl_;
DISALLOW_COPY_AND_ASSIGN(PublicIpAddressGeolocatorTest);
};
// Observer that waits until a TestURLFetcher with the specified fetcher_id
// starts, after which it is made available through .fetcher().
class TestURLFetcherObserver : public net::TestURLFetcher::DelegateForTests {
public:
explicit TestURLFetcherObserver(int expected_fetcher_id)
: expected_fetcher_id_(expected_fetcher_id) {
factory_.SetDelegateForTests(this);
}
virtual ~TestURLFetcherObserver() {}
void Wait() { loop_.Run(); }
net::TestURLFetcher* fetcher() { return fetcher_; }
// net::TestURLFetcher::DelegateForTests:
void OnRequestStart(int fetcher_id) override {
if (fetcher_id == expected_fetcher_id_) {
fetcher_ = factory_.GetFetcherByID(fetcher_id);
fetcher_->SetDelegateForTests(nullptr);
factory_.SetDelegateForTests(nullptr);
loop_.Quit();
}
}
void OnChunkUpload(int fetcher_id) override {}
void OnRequestEnd(int fetcher_id) override {}
private:
const int expected_fetcher_id_;
net::TestURLFetcher* fetcher_ = nullptr;
net::TestURLFetcherFactory factory_;
base::RunLoop loop_;
};
// Basic test of a client invoking QueryNextPosition.
TEST_F(PublicIpAddressGeolocatorTest, BindAndQuery) {
// Intercept the URLFetcher from network geolocation request.
TestURLFetcherObserver observer(
device::NetworkLocationRequest::url_fetcher_id_for_tests);
// Invoke QueryNextPosition and wait for a URLFetcher.
base::RunLoop loop;
QueryNextPosition(loop.QuitClosure());
observer.Wait();
DCHECK(observer.fetcher());
// Issue a valid response to the URLFetcher.
observer.fetcher()->set_url(observer.fetcher()->GetOriginalURL());
observer.fetcher()->set_status(net::URLRequestStatus());
observer.fetcher()->set_response_code(200);
observer.fetcher()->SetResponseString(R"({
"accuracy": 100.0,
"location": {
"lat": 10.0,
"lng": 20.0
}
})");
scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&net::URLFetcherDelegate::OnURLFetchComplete,
base::Unretained(observer.fetcher()->delegate()),
base::Unretained(observer.fetcher())));
// Wait for QueryNextPosition to return.
loop.Run();
EXPECT_THAT(position_->accuracy, testing::Eq(100.0));
EXPECT_THAT(position_->latitude, testing::Eq(10.0));
EXPECT_THAT(position_->longitude, testing::Eq(20.0));
EXPECT_THAT(bad_messages_, testing::IsEmpty());
}
// Tests that multiple overlapping calls to QueryNextPosition result in a
// connection error and reports a bad message.
TEST_F(PublicIpAddressGeolocatorTest, ProhibitedOverlappingCalls) {
base::RunLoop loop;
public_ip_address_geolocator_.set_connection_error_handler(
loop.QuitClosure());
// Issue two overlapping calls to QueryNextPosition.
QueryNextPosition(base::Closure());
QueryNextPosition(base::Closure());
// This terminates only in case of connection error, which we expect.
loop.Run();
// Verify that the geolocator reported a bad message.
EXPECT_THAT(bad_messages_, testing::SizeIs(1));
}
} // namespace
} // namespace device