diff options
author | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-26 16:55:22 +0000 |
---|---|---|
committer | allanwoj@chromium.org <allanwoj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-26 16:55:22 +0000 |
commit | d363f1634213f9941e6408d654a2f72b2694f582 (patch) | |
tree | 6f79bad06e74f8aa48fdc73d9a4f1f0e43c60c5b /chrome/browser/geolocation | |
parent | befe301592b8bbb2898d046df5c74096a9ddeb3c (diff) | |
download | chromium_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')
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); +} |