summaryrefslogtreecommitdiffstats
path: root/chrome/browser/geolocation
diff options
context:
space:
mode:
authorallanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 16:55:22 +0000
committerallanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 16:55:22 +0000
commitd363f1634213f9941e6408d654a2f72b2694f582 (patch)
tree6f79bad06e74f8aa48fdc73d9a4f1f0e43c60c5b /chrome/browser/geolocation
parentbefe301592b8bbb2898d046df5c74096a9ddeb3c (diff)
downloadchromium_src-d363f1634213f9941e6408d654a2f72b2694f582.zip
chromium_src-d363f1634213f9941e6408d654a2f72b2694f582.tar.gz
chromium_src-d363f1634213f9941e6408d654a2f72b2694f582.tar.bz2
Base class for gateway data providers
Provides common code for platform specific implementations of gateway data providers. Might be a good idea to derive all network data providers from a single base class in a future patch as there is a lot of code replication between them. BUG=NONE TEST=Unit test Review URL: http://codereview.chromium.org/3174031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57536 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/geolocation')
-rw-r--r--chrome/browser/geolocation/gateway_data_provider_common.cc89
-rw-r--r--chrome/browser/geolocation/gateway_data_provider_common.h101
-rw-r--r--chrome/browser/geolocation/gateway_data_provider_common_unittest.cc187
3 files changed, 377 insertions, 0 deletions
diff --git a/chrome/browser/geolocation/gateway_data_provider_common.cc b/chrome/browser/geolocation/gateway_data_provider_common.cc
new file mode 100644
index 0000000..5ad7d04
--- /dev/null
+++ b/chrome/browser/geolocation/gateway_data_provider_common.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2010 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 "chrome/browser/geolocation/gateway_data_provider_common.h"
+
+#include "base/utf_string_conversions.h"
+
+GatewayDataProviderCommon::GatewayDataProviderCommon()
+ : Thread("Geolocation_gateway_provider"),
+ is_first_scan_complete_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
+}
+
+GatewayDataProviderCommon::~GatewayDataProviderCommon() {
+ // Thread must be stopped before entering destructor chain to avoid race
+ // conditions; see comment in DeviceDataProvider::Unregister.
+ DCHECK(!IsRunning()) << "Must call StopDataProvider before destroying me";
+}
+
+bool GatewayDataProviderCommon::StartDataProvider() {
+ DCHECK(CalledOnClientThread());
+ return Start();
+}
+
+void GatewayDataProviderCommon::StopDataProvider() {
+ DCHECK(CalledOnClientThread());
+ Stop();
+}
+
+bool GatewayDataProviderCommon::GetData(GatewayData *data) {
+ DCHECK(CalledOnClientThread());
+ DCHECK(data);
+ AutoLock lock(data_mutex_);
+ *data = gateway_data_;
+ // If we've successfully completed a scan, indicate that we have all of the
+ // data we can get.
+ return is_first_scan_complete_;
+}
+
+// Thread implementation.
+void GatewayDataProviderCommon::Init() {
+ DCHECK(gateway_api_ == NULL);
+ gateway_api_.reset(NewGatewayApi());
+ if (gateway_api_ == NULL) {
+ // Error! Can't do scans, so don't try and schedule one.
+ is_first_scan_complete_ = true;
+ return;
+ }
+ DCHECK(polling_policy_ == NULL);
+ polling_policy_.reset(NewPollingPolicy());
+ DCHECK(polling_policy_ != NULL);
+ ScheduleNextScan(0);
+}
+
+void GatewayDataProviderCommon::CleanUp() {
+ // Destroy these instances in the thread on which they were created.
+ gateway_api_.reset();
+ polling_policy_.reset();
+}
+
+void GatewayDataProviderCommon::DoRouterScanTask() {
+ bool update_available = false;
+ GatewayData new_data;
+ if (!gateway_api_->GetRouterData(&new_data.router_data)) {
+ ScheduleNextScan(polling_policy_->NoRouterInterval());
+ } else {
+ {
+ AutoLock lock(data_mutex_);
+ update_available = gateway_data_.DiffersSignificantly(new_data);
+ gateway_data_ = new_data;
+ }
+ ScheduleNextScan(polling_policy_->PollingInterval());
+ }
+ if (update_available || !is_first_scan_complete_) {
+ is_first_scan_complete_ = true;
+ NotifyListeners();
+ }
+}
+
+void GatewayDataProviderCommon::ScheduleNextScan(int interval) {
+ message_loop()->PostDelayedTask(
+ FROM_HERE, task_factory_.NewRunnableMethod(
+ &GatewayDataProviderCommon::DoRouterScanTask), interval);
+}
+
+PollingPolicyInterface* GatewayDataProviderCommon::NewPollingPolicy() {
+ return new GenericPollingPolicy;
+}
diff --git a/chrome/browser/geolocation/gateway_data_provider_common.h b/chrome/browser/geolocation/gateway_data_provider_common.h
new file mode 100644
index 0000000..1f8693c
--- /dev/null
+++ b/chrome/browser/geolocation/gateway_data_provider_common.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_COMMON_H_
+#define CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_COMMON_H_
+#pragma once
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "chrome/browser/geolocation/device_data_provider.h"
+
+namespace {
+const int kDefaultInterval = 20 * 60 * 1000; // 20 mins
+const int kNoRouterInterval = 30 * 1000; // 30s
+}
+
+// Allows sharing and mocking of the update polling policy function.
+class PollingPolicyInterface {
+ public:
+ virtual ~PollingPolicyInterface() {}
+
+ // Returns the polling interval to be used when we are connected to a router
+ // via Ethernet.
+ virtual int PollingInterval() = 0;
+ // Returns the polling interval to be used when there are no adapters
+ // connected to a router via Ethernet.
+ virtual int NoRouterInterval() = 0;
+};
+
+// Base class to promote sharing between platform specific gateway data
+// providers.
+// TODO(allanwoj): This class and WifiDataProviderCommon are extremely similar
+// so we might as well derive all data providers from a single base class
+// generalised for different network data types.
+class GatewayDataProviderCommon
+ : public GatewayDataProviderImplBase,
+ private base::Thread {
+ public:
+ // Interface to abstract the low level data OS library call, and to allow
+ // mocking (hence public).
+ class GatewayApiInterface {
+ public:
+ virtual ~GatewayApiInterface() {}
+ // Gets wifi data for all visible access points.
+ virtual bool GetRouterData(GatewayData::RouterDataSet* data) = 0;
+ };
+
+ GatewayDataProviderCommon();
+
+ // GatewayDataProviderImplBase implementation.
+ virtual bool StartDataProvider();
+ virtual void StopDataProvider();
+ virtual bool GetData(GatewayData *data);
+
+ protected:
+ virtual ~GatewayDataProviderCommon();
+
+ // Returns ownership. Will be called from the worker thread.
+ virtual GatewayApiInterface* NewGatewayApi() = 0;
+ // Returns ownership. Will be called from the worker thread.
+ virtual PollingPolicyInterface* NewPollingPolicy();
+
+ private:
+ class GenericPollingPolicy : public PollingPolicyInterface {
+ public:
+ GenericPollingPolicy() {}
+
+ // PollingPolicyInterface
+ virtual int PollingInterval() { return kDefaultInterval; }
+ virtual int NoRouterInterval() { return kNoRouterInterval; }
+ };
+
+ // Thread implementation.
+ virtual void Init();
+ virtual void CleanUp();
+
+ // Task which run in the child thread.
+ void DoRouterScanTask();
+ // Will schedule a scan; i.e. enqueue DoRouterScanTask deferred task.
+ void ScheduleNextScan(int interval);
+
+ Lock data_mutex_;
+ // Whether we've successfully completed a scan for gateway data (or the
+ // polling thread has terminated early).
+ bool is_first_scan_complete_;
+ // Underlying OS gateway API.
+ scoped_ptr<GatewayApiInterface> gateway_api_;
+ GatewayData gateway_data_;
+ // Controls the polling update interval.
+ scoped_ptr<PollingPolicyInterface> polling_policy_;
+ // Holder for the tasks which run on the thread; takes care of cleanup.
+ ScopedRunnableMethodFactory<GatewayDataProviderCommon> task_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(GatewayDataProviderCommon);
+};
+
+#endif // CHROME_BROWSER_GEOLOCATION_GATEWAY_DATA_PROVIDER_COMMON_H_
diff --git a/chrome/browser/geolocation/gateway_data_provider_common_unittest.cc b/chrome/browser/geolocation/gateway_data_provider_common_unittest.cc
new file mode 100644
index 0000000..424353c
--- /dev/null
+++ b/chrome/browser/geolocation/gateway_data_provider_common_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2010 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 "chrome/browser/geolocation/gateway_data_provider_common.h"
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "base/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AtLeast;
+using testing::DoDefault;
+using testing::Invoke;
+using testing::Return;
+
+class MockGatewayApi : public GatewayDataProviderCommon::GatewayApiInterface {
+ public:
+ MockGatewayApi() {
+ ON_CALL(*this, GetRouterData(_))
+ .WillByDefault(Invoke(this, &MockGatewayApi::GetRouterDataInternal));
+ }
+
+ MOCK_METHOD1(GetRouterData, bool(GatewayData::RouterDataSet* data));
+
+ GatewayData::RouterDataSet data_out_;
+
+ private:
+ bool GetRouterDataInternal(GatewayData::RouterDataSet* data) {
+ *data = data_out_;
+ return true;
+ }
+};
+
+// Stops the specified (nested) message loop when the listener is called back.
+class MessageLoopQuitListener
+ : public GatewayDataProviderCommon::ListenerInterface {
+ public:
+ explicit MessageLoopQuitListener(MessageLoop* message_loop)
+ : message_loop_to_quit_(message_loop) {
+ CHECK(message_loop_to_quit_);
+ }
+
+ // ListenerInterface
+ virtual void DeviceDataUpdateAvailable(
+ DeviceDataProvider<GatewayData>* provider) {
+ // Provider should call back on client's thread.
+ EXPECT_EQ(MessageLoop::current(), message_loop_to_quit_);
+ provider_ = provider;
+ message_loop_to_quit_->Quit();
+ }
+
+ MessageLoop* message_loop_to_quit_;
+ DeviceDataProvider<GatewayData>* provider_;
+};
+
+class MockGatewayPollingPolicy : public PollingPolicyInterface {
+ public:
+ MockGatewayPollingPolicy() {
+ ON_CALL(*this, PollingInterval())
+ .WillByDefault(Return(1));
+ ON_CALL(*this, NoRouterInterval())
+ .WillByDefault(Return(2));
+ }
+
+ // PollingPolicyInterface
+ MOCK_METHOD0(PollingInterval, int());
+ MOCK_METHOD0(NoRouterInterval, int());
+ };
+
+class GatewayDataProviderCommonWithMock : public GatewayDataProviderCommon {
+ public:
+ GatewayDataProviderCommonWithMock()
+ : new_gateway_api_(new MockGatewayApi),
+ new_polling_policy_(new MockGatewayPollingPolicy){
+ }
+
+ // GatewayDataProviderCommon
+ virtual GatewayApiInterface* NewGatewayApi() {
+ CHECK(new_gateway_api_ != NULL);
+ return new_gateway_api_.release();
+ }
+ virtual PollingPolicyInterface* NewPollingPolicy() {
+ CHECK(new_polling_policy_ != NULL);
+ return new_polling_policy_.release();
+ }
+
+ scoped_ptr<MockGatewayApi> new_gateway_api_;
+ scoped_ptr<MockGatewayPollingPolicy> new_polling_policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(GatewayDataProviderCommonWithMock);
+};
+
+// Main test fixture.
+class GeolocationGatewayDataProviderCommonTest : public testing::Test {
+ public:
+ GeolocationGatewayDataProviderCommonTest()
+ : quit_listener_(&main_message_loop_) {
+ }
+
+ virtual void SetUp() {
+ provider_ = new GatewayDataProviderCommonWithMock;
+ gateway_api_ = provider_->new_gateway_api_.get();
+ polling_policy_ = provider_->new_polling_policy_.get();
+ provider_->AddListener(&quit_listener_);
+ }
+ virtual void TearDown() {
+ provider_->RemoveListener(&quit_listener_);
+ provider_->StopDataProvider();
+ provider_ = NULL;
+ }
+
+ protected:
+ MessageLoop main_message_loop_;
+ MessageLoopQuitListener quit_listener_;
+ scoped_refptr<GatewayDataProviderCommonWithMock> provider_;
+ MockGatewayApi* gateway_api_;
+ MockGatewayPollingPolicy* polling_policy_;
+};
+
+TEST_F(GeolocationGatewayDataProviderCommonTest, CreateDestroy) {
+ // Test fixture members were SetUp correctly.
+ EXPECT_EQ(&main_message_loop_, MessageLoop::current());
+ EXPECT_TRUE(NULL != provider_.get());
+ EXPECT_TRUE(NULL != gateway_api_);
+}
+
+TEST_F(GeolocationGatewayDataProviderCommonTest, StartThread) {
+ EXPECT_CALL(*gateway_api_, GetRouterData(_))
+ .Times(AtLeast(1));
+ EXPECT_CALL(*polling_policy_, PollingInterval())
+ .Times(AtLeast(1));
+ EXPECT_TRUE(provider_->StartDataProvider());
+ provider_->StopDataProvider();
+ SUCCEED();
+}
+
+TEST_F(GeolocationGatewayDataProviderCommonTest, IntermittentWifi){
+ EXPECT_CALL(*polling_policy_, PollingInterval())
+ .Times(AtLeast(1));
+ EXPECT_CALL(*polling_policy_, NoRouterInterval())
+ .Times(1);
+ EXPECT_CALL(*gateway_api_, GetRouterData(_))
+ .WillOnce(Return(true))
+ .WillOnce(Return(false))
+ .WillRepeatedly(DoDefault());
+
+ // Set MAC address for output.
+ RouterData single_router;
+ single_router.mac_address = ASCIIToUTF16("12-34-56-78-54-32");
+ gateway_api_->data_out_.insert(single_router);
+
+ provider_->StartDataProvider();
+ main_message_loop_.Run();
+ main_message_loop_.Run();
+}
+
+TEST_F(GeolocationGatewayDataProviderCommonTest, DoAnEmptyScan) {
+ EXPECT_CALL(*gateway_api_, GetRouterData(_))
+ .Times(AtLeast(1));
+ EXPECT_CALL(*polling_policy_, PollingInterval())
+ .Times(AtLeast(1));
+ EXPECT_TRUE(provider_->StartDataProvider());
+ main_message_loop_.Run();
+ GatewayData data;
+ EXPECT_TRUE(provider_->GetData(&data));
+ EXPECT_EQ(0, static_cast<int>(data.router_data.size()));
+}
+
+TEST_F(GeolocationGatewayDataProviderCommonTest, DoScanWithResults) {
+ EXPECT_CALL(*gateway_api_, GetRouterData(_))
+ .Times(AtLeast(1));
+ EXPECT_CALL(*polling_policy_, PollingInterval())
+ .Times(AtLeast(1));
+ RouterData single_router;
+ single_router.mac_address = ASCIIToUTF16("12-34-56-78-54-32");
+ gateway_api_->data_out_.insert(single_router);
+ EXPECT_TRUE(provider_->StartDataProvider());
+ main_message_loop_.Run();
+ GatewayData data;
+ EXPECT_TRUE(provider_->GetData(&data));
+ EXPECT_EQ(1, static_cast<int>(data.router_data.size()));
+ EXPECT_EQ(single_router.mac_address, data.router_data.begin()->mac_address);
+}