blob: 6fe87ee96dcebd7b7c326118e94eb12591d81669 [file] [log] [blame]
// Copyright (c) 2012 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 "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/string16.h"
#include "base/synchronization/waitable_event.h"
#include "base/time.h"
#include "content/browser/geolocation/arbitrator_dependency_factory.h"
#include "content/browser/geolocation/geolocation_provider.h"
#include "content/browser/geolocation/location_arbitrator.h"
#include "content/browser/geolocation/location_provider.h"
#include "content/browser/geolocation/mock_location_provider.h"
#include "content/public/browser/access_token_store.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "googleurl/src/gurl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::Geoposition;
using testing::MakeMatcher;
using testing::Matcher;
using testing::MatcherInterface;
using testing::MatchResultListener;
namespace {
class NonSingletonGeolocationProvider : public GeolocationProvider {
public:
NonSingletonGeolocationProvider() {}
virtual ~NonSingletonGeolocationProvider() {}
};
class StartStopMockLocationProvider : public MockLocationProvider {
public:
StartStopMockLocationProvider(base::WaitableEvent* event) :
MockLocationProvider(&instance_),
event_(event) {
}
virtual ~StartStopMockLocationProvider() {
event_->Signal();
}
private:
base::WaitableEvent* event_;
};
// The AccessTokenStore will be accessed from the geolocation helper thread. The
// existing FakeAccessTokenStore class cannot be used here because it is based
// on gmock and gmock is not thread-safe on Windows.
// See: http://code.google.com/p/googlemock/issues/detail?id=156
class TestingAccessTokenStore : public content::AccessTokenStore {
public:
TestingAccessTokenStore(base::WaitableEvent* event) : event_(event) {}
virtual void LoadAccessTokens(const LoadAccessTokensCallbackType& callback)
OVERRIDE {
callback.Run(AccessTokenSet(), NULL);
event_->Signal();
}
virtual void SaveAccessToken(const GURL& server_url,
const string16& access_token) OVERRIDE {}
protected:
virtual ~TestingAccessTokenStore() {}
private:
base::WaitableEvent* event_;
};
class TestingDependencyFactory
: public DefaultGeolocationArbitratorDependencyFactory {
public:
TestingDependencyFactory(base::WaitableEvent* event) : event_(event) {}
virtual content::AccessTokenStore* NewAccessTokenStore() OVERRIDE {
return new TestingAccessTokenStore(event_);
}
virtual LocationProviderBase* NewNetworkLocationProvider(
content::AccessTokenStore* access_token_store,
net::URLRequestContextGetter* context,
const GURL& url,
const string16& access_token) OVERRIDE {
return new StartStopMockLocationProvider(event_);
}
virtual LocationProviderBase* NewSystemLocationProvider() OVERRIDE {
return NULL;
}
protected:
virtual ~TestingDependencyFactory() {}
private:
base::WaitableEvent* event_;
};
class NullGeolocationObserver : public GeolocationObserver {
public:
// GeolocationObserver
virtual void OnLocationUpdate(const content::Geoposition& position) {}
};
class MockGeolocationObserver : public GeolocationObserver {
public:
// GeolocationObserver
MOCK_METHOD1(OnLocationUpdate, void(const content::Geoposition& position));
};
class MockGeolocationCallbackWrapper {
public:
MOCK_METHOD1(Callback, void(const content::Geoposition& position));
};
class GeopositionEqMatcher
: public MatcherInterface<const content::Geoposition&> {
public:
explicit GeopositionEqMatcher(const content::Geoposition& expected)
: expected_(expected) {}
virtual bool MatchAndExplain(const content::Geoposition& actual,
MatchResultListener* listener) const OVERRIDE {
return actual.latitude == expected_.latitude &&
actual.longitude == expected_.longitude &&
actual.altitude == expected_.altitude &&
actual.accuracy == expected_.accuracy &&
actual.altitude_accuracy == expected_.altitude_accuracy &&
actual.heading == expected_.heading &&
actual.speed == expected_.speed &&
actual.timestamp == expected_.timestamp &&
actual.error_code == expected_.error_code &&
actual.error_message == expected_.error_message;
}
virtual void DescribeTo(::std::ostream* os) const OVERRIDE {
*os << "which matches the expected position";
}
virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE{
*os << "which does not match the expected position";
}
private:
content::Geoposition expected_;
DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher);
};
Matcher<const content::Geoposition&> GeopositionEq(
const content::Geoposition& expected) {
return MakeMatcher(new GeopositionEqMatcher(expected));
}
class GeolocationProviderTest : public testing::Test {
protected:
GeolocationProviderTest()
: message_loop_(),
io_thread_(content::BrowserThread::IO, &message_loop_),
event_(false, false),
dependency_factory_(new TestingDependencyFactory(&event_)),
provider_(new NonSingletonGeolocationProvider) {
GeolocationArbitrator::SetDependencyFactoryForTest(
dependency_factory_.get());
}
~GeolocationProviderTest() {
GeolocationArbitrator::SetDependencyFactoryForTest(NULL);
}
void WaitAndReset() {
event_.Wait();
event_.Reset();
}
MessageLoop message_loop_;
content::TestBrowserThread io_thread_;
base::WaitableEvent event_;
scoped_refptr<TestingDependencyFactory> dependency_factory_;
scoped_ptr<NonSingletonGeolocationProvider> provider_;
};
// Regression test for http://crbug.com/59377
TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
EXPECT_FALSE(provider_->HasPermissionBeenGranted());
provider_->OnPermissionGranted();
EXPECT_TRUE(provider_->HasPermissionBeenGranted());
}
TEST_F(GeolocationProviderTest, StartStop) {
EXPECT_FALSE(provider_->IsRunning());
NullGeolocationObserver null_observer;
GeolocationObserverOptions options;
provider_->AddObserver(&null_observer, options);
EXPECT_TRUE(provider_->IsRunning());
// Wait for token load request from the arbitrator to come through.
WaitAndReset();
EXPECT_EQ(MockLocationProvider::instance_->state_,
MockLocationProvider::LOW_ACCURACY);
provider_->RemoveObserver(&null_observer);
// Wait for the providers to be stopped now that all clients are gone.
WaitAndReset();
EXPECT_TRUE(provider_->IsRunning());
}
TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
content::Geoposition position;
position.error_code = content::Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
provider_->OverrideLocationForTesting(position);
// Adding an observer when the location is overridden should synchronously
// update the observer with our overridden position.
MockGeolocationObserver mock_observer;
EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position)));
provider_->AddObserver(&mock_observer, GeolocationObserverOptions());
// Wait for token load request from the arbitrator to come through.
WaitAndReset();
provider_->RemoveObserver(&mock_observer);
// Wait for the providers to be stopped now that all clients are gone.
WaitAndReset();
}
TEST_F(GeolocationProviderTest, Callback) {
MockGeolocationCallbackWrapper callback_wrapper;
provider_->RequestCallback(
base::Bind(&MockGeolocationCallbackWrapper::Callback,
base::Unretained(&callback_wrapper)));
// Wait for token load request from the arbitrator to come through.
WaitAndReset();
content::Geoposition position;
position.latitude = 12;
position.longitude = 34;
position.accuracy = 56;
position.timestamp = base::Time::Now();
EXPECT_CALL(callback_wrapper, Callback(GeopositionEq(position)));
provider_->OverrideLocationForTesting(position);
// Wait for the providers to be stopped now that all clients are gone.
WaitAndReset();
}
} // namespace