普通文本  |  259行  |  8.78 KB

// 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/message_loop.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "content/browser/geolocation/geolocation_provider_impl.h"
#include "content/browser/geolocation/mock_location_arbitrator.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::MakeMatcher;
using testing::Matcher;
using testing::MatcherInterface;
using testing::MatchResultListener;

namespace content {

class LocationProviderForTestArbitrator : public GeolocationProviderImpl {
 public:
  LocationProviderForTestArbitrator() : mock_arbitrator_(NULL) {}
  virtual ~LocationProviderForTestArbitrator() {}

  // Only valid for use on the geolocation thread.
  MockLocationArbitrator* mock_arbitrator() const {
    return mock_arbitrator_;
  }

 protected:
  // GeolocationProviderImpl implementation:
  virtual LocationArbitrator* CreateArbitrator() OVERRIDE;

 private:
  MockLocationArbitrator* mock_arbitrator_;
};

LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() {
  DCHECK(mock_arbitrator_ == NULL);
  mock_arbitrator_ = new MockLocationArbitrator;
  return mock_arbitrator_;
}

class GeolocationObserver {
 public:
  virtual ~GeolocationObserver() {}
  virtual void OnLocationUpdate(const Geoposition& position) = 0;
};

class MockGeolocationObserver : public GeolocationObserver {
 public:
  MOCK_METHOD1(OnLocationUpdate, void(const Geoposition& position));
};

class AsyncMockGeolocationObserver : public MockGeolocationObserver {
 public:
  virtual void OnLocationUpdate(const Geoposition& position) OVERRIDE {
    MockGeolocationObserver::OnLocationUpdate(position);
    base::MessageLoop::current()->Quit();
  }
};

class MockGeolocationCallbackWrapper {
 public:
  MOCK_METHOD1(Callback, void(const Geoposition& position));
};

class GeopositionEqMatcher
    : public MatcherInterface<const Geoposition&> {
 public:
  explicit GeopositionEqMatcher(const Geoposition& expected)
      : expected_(expected) {}

  virtual bool MatchAndExplain(const 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:
  Geoposition expected_;

  DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher);
};

Matcher<const Geoposition&> GeopositionEq(const Geoposition& expected) {
  return MakeMatcher(new GeopositionEqMatcher(expected));
}

class GeolocationProviderTest : public testing::Test {
 protected:
  GeolocationProviderTest()
      : message_loop_(),
        ui_thread_(BrowserThread::UI, &message_loop_),
        provider_(new LocationProviderForTestArbitrator) {
  }

  virtual ~GeolocationProviderTest() {}

  LocationProviderForTestArbitrator* provider() { return provider_.get(); }

  // Called on test thread.
  bool ProvidersStarted();
  void SendMockLocation(const Geoposition& position);

 private:
  // Called on provider thread.
  void GetProvidersStarted(bool* started);

  base::MessageLoop message_loop_;
  TestBrowserThread ui_thread_;
  scoped_ptr<LocationProviderForTestArbitrator> provider_;
};


bool GeolocationProviderTest::ProvidersStarted() {
  DCHECK(provider_->IsRunning());
  DCHECK(base::MessageLoop::current() == &message_loop_);
  bool started;
  provider_->message_loop_proxy()->PostTaskAndReply(
      FROM_HERE,
      base::Bind(&GeolocationProviderTest::GetProvidersStarted,
                 base::Unretained(this),
                 &started),
      base::MessageLoop::QuitClosure());
  message_loop_.Run();
  return started;
}

void GeolocationProviderTest::GetProvidersStarted(bool* started) {
  DCHECK(base::MessageLoop::current() == provider_->message_loop());
  *started = provider_->mock_arbitrator()->providers_started();
}

void GeolocationProviderTest::SendMockLocation(const Geoposition& position) {
  DCHECK(provider_->IsRunning());
  DCHECK(base::MessageLoop::current() == &message_loop_);
  provider_->message_loop()
      ->PostTask(FROM_HERE,
                 base::Bind(&GeolocationProviderImpl::OnLocationUpdate,
                            base::Unretained(provider_.get()),
                            position));
}

// Regression test for http://crbug.com/59377
TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) {
  EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing());
  provider()->UserDidOptIntoLocationServices();
  EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing());
}

void DummyFunction(const Geoposition& position) {
}

TEST_F(GeolocationProviderTest, StartStop) {
  EXPECT_FALSE(provider()->IsRunning());
  GeolocationProviderImpl::LocationUpdateCallback callback =
      base::Bind(&DummyFunction);
  scoped_ptr<content::GeolocationProvider::Subscription> subscription =
      provider()->AddLocationUpdateCallback(callback, false);
  EXPECT_TRUE(provider()->IsRunning());
  EXPECT_TRUE(ProvidersStarted());

  subscription.reset();

  EXPECT_FALSE(ProvidersStarted());
  EXPECT_TRUE(provider()->IsRunning());
}

TEST_F(GeolocationProviderTest, StalePositionNotSent) {
  Geoposition first_position;
  first_position.latitude = 12;
  first_position.longitude = 34;
  first_position.accuracy = 56;
  first_position.timestamp = base::Time::Now();

  AsyncMockGeolocationObserver first_observer;
  GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind(
      &MockGeolocationObserver::OnLocationUpdate,
      base::Unretained(&first_observer));
  EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position)));
  scoped_ptr<content::GeolocationProvider::Subscription> subscription =
      provider()->AddLocationUpdateCallback(first_callback, false);
  SendMockLocation(first_position);
  base::MessageLoop::current()->Run();

  subscription.reset();

  Geoposition second_position;
  second_position.latitude = 13;
  second_position.longitude = 34;
  second_position.accuracy = 56;
  second_position.timestamp = base::Time::Now();

  AsyncMockGeolocationObserver second_observer;

  // After adding a second observer, check that no unexpected position update
  // is sent.
  EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0);
  GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind(
      &MockGeolocationObserver::OnLocationUpdate,
      base::Unretained(&second_observer));
  scoped_ptr<content::GeolocationProvider::Subscription> subscription2 =
      provider()->AddLocationUpdateCallback(second_callback, false);
  base::MessageLoop::current()->RunUntilIdle();

  // The second observer should receive the new position now.
  EXPECT_CALL(second_observer,
              OnLocationUpdate(GeopositionEq(second_position)));
  SendMockLocation(second_position);
  base::MessageLoop::current()->Run();

  subscription2.reset();
  EXPECT_FALSE(ProvidersStarted());
}

TEST_F(GeolocationProviderTest, OverrideLocationForTesting) {
  Geoposition position;
  position.error_code = 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)));
  GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind(
      &MockGeolocationObserver::OnLocationUpdate,
      base::Unretained(&mock_observer));
  scoped_ptr<content::GeolocationProvider::Subscription> subscription =
      provider()->AddLocationUpdateCallback(callback, false);
  subscription.reset();
  // Wait for the providers to be stopped now that all clients are gone.
  EXPECT_FALSE(ProvidersStarted());
}

}  // namespace content