diff options
author | hans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-23 07:39:28 +0000 |
---|---|---|
committer | hans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-23 07:39:28 +0000 |
commit | ddd1844a44696ec660bcbb6d939df0299a03b3a6 (patch) | |
tree | 374ae15c8c723deecbe8011209869720c6c0f62f /chrome/browser | |
parent | c9f57d3571e01d1be5b45d484174f9f8fabbd0ee (diff) | |
download | chromium_src-ddd1844a44696ec660bcbb6d939df0299a03b3a6.zip chromium_src-ddd1844a44696ec660bcbb6d939df0299a03b3a6.tar.gz chromium_src-ddd1844a44696ec660bcbb6d939df0299a03b3a6.tar.bz2 |
Implement device_orientation::Provider.
Provider provides its registered observers with device orientation data
by finding and polling a DataFetcher on a background thread.
BUG=44654
TEST=unit_tests --gtest_filter="DeviceOrientationProviderTest.*"
Review URL: http://codereview.chromium.org/3136008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57036 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/device_orientation/data_fetcher.h | 21 | ||||
-rw-r--r-- | chrome/browser/device_orientation/orientation.h | 6 | ||||
-rw-r--r-- | chrome/browser/device_orientation/provider.cc | 15 | ||||
-rw-r--r-- | chrome/browser/device_orientation/provider.h | 8 | ||||
-rw-r--r-- | chrome/browser/device_orientation/provider_impl.cc | 199 | ||||
-rw-r--r-- | chrome/browser/device_orientation/provider_impl.h | 87 | ||||
-rw-r--r-- | chrome/browser/device_orientation/provider_unittest.cc | 369 |
7 files changed, 697 insertions, 8 deletions
diff --git a/chrome/browser/device_orientation/data_fetcher.h b/chrome/browser/device_orientation/data_fetcher.h new file mode 100644 index 0000000..1a599b7 --- /dev/null +++ b/chrome/browser/device_orientation/data_fetcher.h @@ -0,0 +1,21 @@ +// 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_DEVICE_ORIENTATION_DATA_FETCHER_H_ +#define CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_ + +namespace device_orientation { + +class Orientation; + +class DataFetcher { + public: + virtual ~DataFetcher() {} + virtual bool GetOrientation(Orientation*) = 0; + virtual int MinSamplingIntervalMs() const { return 0; } +}; + +} // namespace device_orientation + +#endif // CHROME_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_ diff --git a/chrome/browser/device_orientation/orientation.h b/chrome/browser/device_orientation/orientation.h index d43a989..2193170 100644 --- a/chrome/browser/device_orientation/orientation.h +++ b/chrome/browser/device_orientation/orientation.h @@ -34,6 +34,12 @@ class Orientation { can_provide_gamma_(false) { } + static Orientation Empty() { return Orientation(); } + + bool IsEmpty() { + return !can_provide_alpha_ && !can_provide_beta_ && !can_provide_gamma_; + } + double alpha_; double beta_; double gamma_; diff --git a/chrome/browser/device_orientation/provider.cc b/chrome/browser/device_orientation/provider.cc index 16b24b3..d1d335b 100644 --- a/chrome/browser/device_orientation/provider.cc +++ b/chrome/browser/device_orientation/provider.cc @@ -4,13 +4,20 @@ #include "chrome/browser/device_orientation/provider.h" +#include "base/logging.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/device_orientation/data_fetcher.h" +#include "chrome/browser/device_orientation/provider_impl.h" + namespace device_orientation { Provider* Provider::GetInstance() { - if (!instance_) - // TODO(hans) This is not finished. We will create an instance of the real - // Provider implementation once it is implemented. - instance_ = new Provider(); + if (!instance_) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + const ProviderImpl::DataFetcherFactory default_factories[] = { NULL }; + + instance_ = new ProviderImpl(MessageLoop::current(), default_factories); + } return instance_; } diff --git a/chrome/browser/device_orientation/provider.h b/chrome/browser/device_orientation/provider.h index aaa5106..f32b0f6 100644 --- a/chrome/browser/device_orientation/provider.h +++ b/chrome/browser/device_orientation/provider.h @@ -12,7 +12,7 @@ namespace device_orientation { class Orientation; -class Provider : public base::RefCounted<Provider> { +class Provider : public base::RefCountedThreadSafe<Provider> { public: class Observer { public: @@ -33,8 +33,8 @@ class Provider : public base::RefCounted<Provider> { // injected object's reference count. static void SetInstanceForTests(Provider* provider); - virtual void AddObserver(Observer* observer) {} - virtual void RemoveObserver(Observer* observer) {} + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; protected: Provider() {} @@ -44,7 +44,7 @@ class Provider : public base::RefCounted<Provider> { } private: - friend class base::RefCounted<Provider>; + friend class base::RefCountedThreadSafe<Provider>; static Provider* instance_; DISALLOW_COPY_AND_ASSIGN(Provider); diff --git a/chrome/browser/device_orientation/provider_impl.cc b/chrome/browser/device_orientation/provider_impl.cc new file mode 100644 index 0000000..184e5f8 --- /dev/null +++ b/chrome/browser/device_orientation/provider_impl.cc @@ -0,0 +1,199 @@ +// 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 <cmath> +#include <set> +#include <vector> + +#include "base/logging.h" +#include "base/task.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/device_orientation/orientation.h" +#include "chrome/browser/device_orientation/provider_impl.h" + +namespace device_orientation { + +ProviderImpl::ProviderImpl(MessageLoop* message_loop, + const DataFetcherFactory factories[]) + : creator_loop_(message_loop), + ALLOW_THIS_IN_INITIALIZER_LIST(do_poll_method_factory_(this)) { + for (const DataFetcherFactory* fp = factories; *fp; ++fp) + factories_.push_back(*fp); +} + +ProviderImpl::~ProviderImpl() { +} + +void ProviderImpl::AddObserver(Observer* observer) { + DCHECK(MessageLoop::current() == creator_loop_); + + observers_.insert(observer); + if (observers_.size() == 1) + Start(); + else + observer->OnOrientationUpdate(last_notification_); +} + +void ProviderImpl::RemoveObserver(Observer* observer) { + DCHECK(MessageLoop::current() == creator_loop_); + + observers_.erase(observer); + if (observers_.empty()) + Stop(); +} + +void ProviderImpl::Start() { + DCHECK(MessageLoop::current() == creator_loop_); + DCHECK(!polling_thread_.get()); + + polling_thread_.reset(new base::Thread("Device orientation polling thread")); + if (!polling_thread_->Start()) { + LOG(ERROR) << "Failed to start device orientation polling thread"; + polling_thread_.reset(); + return; + } + ScheduleInitializePollingThread(); +} + +void ProviderImpl::Stop() { + DCHECK(MessageLoop::current() == creator_loop_); + polling_thread_.reset(); + data_fetcher_.reset(); +} + +void ProviderImpl::DoInitializePollingThread( + std::vector<DataFetcherFactory> factories) { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + typedef std::vector<DataFetcherFactory>::const_iterator Iterator; + for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) { + DataFetcherFactory factory = *i; + scoped_ptr<DataFetcher> fetcher(factory()); + Orientation orientation; + + if (fetcher.get() && fetcher->GetOrientation(&orientation)) { + // Pass ownership of fetcher to provider_. + data_fetcher_.swap(fetcher); + last_orientation_ = orientation; + + // Notify observers. + ScheduleDoNotify(orientation); + + // Start polling. + ScheduleDoPoll(); + return; + } + } + + // When no orientation data can be provided. + ScheduleDoNotify(Orientation::Empty()); +} + +void ProviderImpl::ScheduleInitializePollingThread() { + DCHECK(MessageLoop::current() == creator_loop_); + + Task* task = NewRunnableMethod(this, + &ProviderImpl::DoInitializePollingThread, + factories_); + MessageLoop* polling_loop = polling_thread_->message_loop(); + polling_loop->PostTask(FROM_HERE, task); +} + +void ProviderImpl::DoNotify(Orientation orientation) { + DCHECK(MessageLoop::current() == creator_loop_); + + last_notification_ = orientation; + + typedef std::set<Observer*>::const_iterator Iterator; + for (Iterator i = observers_.begin(), e = observers_.end(); i != e; ++i) + (*i)->OnOrientationUpdate(orientation); + + if (orientation.IsEmpty()) { + // Notify observers about failure to provide data exactly once. + observers_.clear(); + Stop(); + } +} + +void ProviderImpl::ScheduleDoNotify(Orientation orientation) { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Task* task = NewRunnableMethod(this, &ProviderImpl::DoNotify, orientation); + creator_loop_->PostTask(FROM_HERE, task); +} + +void ProviderImpl::DoPoll() { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Orientation orientation; + if (!data_fetcher_->GetOrientation(&orientation)) { + LOG(ERROR) << "Failed to poll device orientation data fetcher."; + + ScheduleDoNotify(Orientation::Empty()); + return; + } + + if (SignificantlyDifferent(orientation, last_orientation_)) { + last_orientation_ = orientation; + ScheduleDoNotify(orientation); + } + + ScheduleDoPoll(); +} + +void ProviderImpl::ScheduleDoPoll() { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Task* task = do_poll_method_factory_.NewRunnableMethod(&ProviderImpl::DoPoll); + MessageLoop* polling_loop = polling_thread_->message_loop(); + polling_loop->PostDelayedTask(FROM_HERE, task, SamplingIntervalMs()); +} + +namespace { +bool IsElementSignificantlyDifferent(bool can_provide_element1, + bool can_provide_element2, + double element1, + double element2) { + const double kThreshold = 0.1; + + if (can_provide_element1 != can_provide_element2) + return true; + if (can_provide_element1 && + std::fabs(element1 - element2) >= kThreshold) + return true; + return false; +} +} // namespace + +// Returns true if two orientations are considered different enough that +// observers should be notified of the new orientation. +bool ProviderImpl::SignificantlyDifferent(const Orientation& o1, + const Orientation& o2) { + return IsElementSignificantlyDifferent(o1.can_provide_alpha_, + o2.can_provide_alpha_, + o1.alpha_, + o2.alpha_) || + IsElementSignificantlyDifferent(o1.can_provide_beta_, + o2.can_provide_beta_, + o1.beta_, + o2.beta_) || + IsElementSignificantlyDifferent(o1.can_provide_gamma_, + o2.can_provide_gamma_, + o1.gamma_, + o2.gamma_); +} + +int ProviderImpl::SamplingIntervalMs() const { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + DCHECK(data_fetcher_.get()); + + int fetcher_interval = data_fetcher_->MinSamplingIntervalMs(); + + if (fetcher_interval > kDesiredSamplingIntervalMs) + return fetcher_interval; + else + return kDesiredSamplingIntervalMs; +} + +} // namespace device_orientation diff --git a/chrome/browser/device_orientation/provider_impl.h b/chrome/browser/device_orientation/provider_impl.h new file mode 100644 index 0000000..bf2e27e --- /dev/null +++ b/chrome/browser/device_orientation/provider_impl.h @@ -0,0 +1,87 @@ +// 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_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ +#define CHROME_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ + +#include <set> +#include <vector> + +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "chrome/browser/device_orientation/data_fetcher.h" +#include "chrome/browser/device_orientation/orientation.h" +#include "chrome/browser/device_orientation/provider.h" + +class MessageLoop; + +namespace base { +class Thread; +} + +namespace device_orientation { + +class ProviderImpl : public Provider { + public: + typedef DataFetcher* (*DataFetcherFactory)(); + + // Create a ProviderImpl that expects calls to AddObserver and RemoveObserver + // on message_loop, sends notifications to observers on message_loop, + // and uses the NULL-terminated factories array to find a DataFetcher + // that can provide orientation data. + ProviderImpl(MessageLoop* message_loop, const DataFetcherFactory factories[]); + + // From Provider. + virtual void AddObserver(Observer* observer); + virtual void RemoveObserver(Observer* observer); + + private: + virtual ~ProviderImpl(); + + // Starts or Stops the provider. Called from creator_loop_. + void Start(); + void Stop(); + + // Method for finding a suitable DataFetcher and starting the polling. + // Runs on the polling_thread_. + void DoInitializePollingThread(std::vector<DataFetcherFactory> factories); + void ScheduleInitializePollingThread(); + + // Method for polling a DataFetcher. Runs on the polling_thread_. + void DoPoll(); + void ScheduleDoPoll(); + + // Method for notifying observers of an orientation update. + // Runs on the creator_thread_. + void DoNotify(Orientation orientation); + void ScheduleDoNotify(Orientation orientation); + + static bool SignificantlyDifferent(const Orientation& orientation1, + const Orientation& orientation2); + + enum { kDesiredSamplingIntervalMs = 100 }; + int SamplingIntervalMs() const; + + // The Message Loop on which this object was created. + // Typically the I/O loop, but may be something else during testing. + MessageLoop* creator_loop_; + + // Members below are only to be used from the creator_loop_. + std::vector<DataFetcherFactory> factories_; + std::set<Observer*> observers_; + Orientation last_notification_; + + // When polling_thread_ is running, members below are only to be used + // from that thread. + scoped_ptr<DataFetcher> data_fetcher_; + Orientation last_orientation_; + ScopedRunnableMethodFactory<ProviderImpl> do_poll_method_factory_; + + // Polling is done on this background thread. + scoped_ptr<base::Thread> polling_thread_; +}; + +} // namespace device_orientation + +#endif // CHROME_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ diff --git a/chrome/browser/device_orientation/provider_unittest.cc b/chrome/browser/device_orientation/provider_unittest.cc new file mode 100644 index 0000000..cadc174 --- /dev/null +++ b/chrome/browser/device_orientation/provider_unittest.cc @@ -0,0 +1,369 @@ +// 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 <queue> + +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "chrome/browser/device_orientation/data_fetcher.h" +#include "chrome/browser/device_orientation/orientation.h" +#include "chrome/browser/device_orientation/provider.h" +#include "chrome/browser/device_orientation/provider_impl.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device_orientation { +namespace { + +// Class for checking expectations on orientation updates from the Provider. +class UpdateChecker : public Provider::Observer { + public: + explicit UpdateChecker(int* expectations_count_ptr) + : expectations_count_ptr_(expectations_count_ptr) { + } + + // From Provider::Observer. + virtual void OnOrientationUpdate(const Orientation& orientation) { + ASSERT_FALSE(expectations_queue_.empty()); + + Orientation expected = expectations_queue_.front(); + expectations_queue_.pop(); + + EXPECT_EQ(expected.can_provide_alpha_, orientation.can_provide_alpha_); + EXPECT_EQ(expected.can_provide_beta_, orientation.can_provide_beta_); + EXPECT_EQ(expected.can_provide_gamma_, orientation.can_provide_gamma_); + if (expected.can_provide_alpha_) + EXPECT_EQ(expected.alpha_, orientation.alpha_); + if (expected.can_provide_beta_) + EXPECT_EQ(expected.beta_, orientation.beta_); + if (expected.can_provide_gamma_) + EXPECT_EQ(expected.gamma_, orientation.gamma_); + + --(*expectations_count_ptr_); + + if (*expectations_count_ptr_ == 0) { + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + } + + void AddExpectation(const Orientation& orientation) { + expectations_queue_.push(orientation); + ++(*expectations_count_ptr_); + } + + private: + // Set up by the test fixture, which then blocks while it is accessed + // from OnOrientationUpdate which is executed on the test fixture's + // message_loop_. + int* expectations_count_ptr_; + std::queue<Orientation> expectations_queue_; +}; + +// Class for injecting test orientation data into the Provider. +class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> { + public: + MockOrientationFactory() { + EXPECT_FALSE(instance_); + instance_ = this; + } + + ~MockOrientationFactory() { + instance_ = NULL; + } + + static DataFetcher* CreateDataFetcher() { + EXPECT_TRUE(instance_); + return new MockDataFetcher(instance_); + } + + void SetOrientation(const Orientation& orientation) { + AutoLock auto_lock(lock_); + orientation_ = orientation; + } + + private: + // Owned by ProviderImpl. Holds a reference back to MockOrientationFactory. + class MockDataFetcher : public DataFetcher { + public: + explicit MockDataFetcher(MockOrientationFactory* orientation_factory) + : orientation_factory_(orientation_factory) { } + + // From DataFetcher. Called by the Provider. + virtual bool GetOrientation(Orientation* orientation) { + AutoLock auto_lock(orientation_factory_->lock_); + *orientation = orientation_factory_->orientation_; + return true; + } + + private: + scoped_refptr<MockOrientationFactory> orientation_factory_; + }; + + static MockOrientationFactory* instance_; + Orientation orientation_; + Lock lock_; +}; + +MockOrientationFactory* MockOrientationFactory::instance_; + +// Mock DataFetcher that always fails to provide any orientation data. +class FailingDataFetcher : public DataFetcher { + public: + // Factory method; passed to and called by the ProviderImpl. + static DataFetcher* Create() { + return new FailingDataFetcher(); + } + + // From DataFetcher. + virtual bool GetOrientation(Orientation* orientation) { + return false; + } + + private: + FailingDataFetcher() {} +}; + +class DeviceOrientationProviderTest : public testing::Test { + public: + DeviceOrientationProviderTest() { + } + + // Initialize the test fixture with a ProviderImpl that uses the + // DataFetcherFactories in the null-terminated factories array. + void Init(ProviderImpl::DataFetcherFactory* factories) { + provider_ = new ProviderImpl(&message_loop_, factories); + Provider::SetInstanceForTests(provider_); + } + + // Initialize the test fixture with a ProviderImpl that uses the + // DataFetcherFactory factory. + void Init(ProviderImpl::DataFetcherFactory factory) { + ProviderImpl::DataFetcherFactory factories[] = { factory, NULL }; + Init(factories); + } + + void ScheduleAddObserver(Provider::Observer* observer) { + Task* task = NewRunnableMethod(provider_.get(), &Provider::AddObserver, + observer); + message_loop_.PostTask(FROM_HERE, task); + } + + void RemoveObserver(Provider::Observer* observer) { + Task* task = NewRunnableMethod(provider_.get(), &Provider::RemoveObserver, + observer); + message_loop_.PostTask(FROM_HERE, task); + message_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); + message_loop_.Run(); + } + + MessageLoop* message_loop() { + return &message_loop_; + } + + private: + // Provider instance under test. + scoped_refptr<Provider> provider_; + + // Message loop for adding or removing observers of the provider, and + // receiving orientation updates. In non-test situations, this would + // be the message loop of the IO thread. + MessageLoop message_loop_; +}; + +TEST_F(DeviceOrientationProviderTest, FailingTest) { + Init(FailingDataFetcher::Create); + int expectations_count = 0; + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker(&expectations_count)); + + checker_a->AddExpectation(Orientation::Empty()); + ScheduleAddObserver(checker_a.get()); + message_loop()->Run(); + + checker_b->AddExpectation(Orientation::Empty()); + ScheduleAddObserver(checker_b.get()); + message_loop()->Run(); +} + +TEST_F(DeviceOrientationProviderTest, ProviderIsSingleton) { + Init(FailingDataFetcher::Create); + + scoped_refptr<Provider> provider_a(Provider::GetInstance()); + scoped_refptr<Provider> provider_b(Provider::GetInstance()); + + EXPECT_EQ(provider_a.get(), provider_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, BasicPushTest) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + + scoped_ptr<UpdateChecker> checker(new UpdateChecker(&expectations_count)); + checker->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + ScheduleAddObserver(checker.get()); + message_loop()->Run(); + + RemoveObserver(checker.get()); +} + +TEST_F(DeviceOrientationProviderTest, MultipleObserversPushTest) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + const Orientation kTestOrientations[] = { + Orientation(true, 1, true, 2, true, 3), + Orientation(true, 4, true, 5, true, 6), + Orientation(true, 7, true, 8, true, 9)}; + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_c(new UpdateChecker(&expectations_count)); + + checker_a->AddExpectation(kTestOrientations[0]); + orientation_factory->SetOrientation(kTestOrientations[0]); + ScheduleAddObserver(checker_a.get()); + message_loop()->Run(); + + checker_a->AddExpectation(kTestOrientations[1]); + checker_b->AddExpectation(kTestOrientations[0]); + checker_b->AddExpectation(kTestOrientations[1]); + orientation_factory->SetOrientation(kTestOrientations[1]); + ScheduleAddObserver(checker_b.get()); + message_loop()->Run(); + + RemoveObserver(checker_a.get()); + checker_b->AddExpectation(kTestOrientations[2]); + checker_c->AddExpectation(kTestOrientations[1]); + checker_c->AddExpectation(kTestOrientations[2]); + orientation_factory->SetOrientation(kTestOrientations[2]); + ScheduleAddObserver(checker_c.get()); + message_loop()->Run(); + + RemoveObserver(checker_b.get()); + RemoveObserver(checker_c.get()); +} + +TEST_F(DeviceOrientationProviderTest, ObserverNotRemoved) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + + scoped_ptr<UpdateChecker> checker(new UpdateChecker(&expectations_count)); + checker->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + ScheduleAddObserver(checker.get()); + message_loop()->Run(); + + // Note that checker is not removed. This should not be a problem. +} + +TEST_F(DeviceOrientationProviderTest, StartFailing) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker(&expectations_count)); + + orientation_factory->SetOrientation(kTestOrientation); + checker_a->AddExpectation(kTestOrientation); + ScheduleAddObserver(checker_a.get()); + message_loop()->Run(); + + checker_a->AddExpectation(Orientation::Empty()); + orientation_factory->SetOrientation(Orientation::Empty()); + message_loop()->Run(); + + checker_b->AddExpectation(Orientation::Empty()); + ScheduleAddObserver(checker_b.get()); + message_loop()->Run(); + + RemoveObserver(checker_a.get()); + RemoveObserver(checker_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, StartStopStart) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + const Orientation kTestOrientation2(true, 4, true, 5, true, 6); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker(&expectations_count)); + + checker_a->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + ScheduleAddObserver(checker_a.get()); + message_loop()->Run(); + + RemoveObserver(checker_a.get()); // This stops the Provider. + + checker_b->AddExpectation(kTestOrientation2); + orientation_factory->SetOrientation(kTestOrientation2); + ScheduleAddObserver(checker_b.get()); + message_loop()->Run(); + RemoveObserver(checker_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, SignificantlyDifferent) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + int expectations_count = 0; + + // Values that should be well below or above the implementation's + // significane threshold. + const double kInsignificantDifference = 1e-6; + const double kSignificantDifference = 30; + const double kAlpha = 4, kBeta = 5, kGamma = 6; + + const Orientation first_orientation(true, kAlpha, true, kBeta, true, kGamma); + + const Orientation second_orientation(true, kAlpha + kInsignificantDifference, + true, kBeta + kInsignificantDifference, + true, kGamma + kInsignificantDifference); + + const Orientation third_orientation(true, kAlpha + kSignificantDifference, + true, kBeta + kSignificantDifference, + true, kGamma + kSignificantDifference); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker(&expectations_count)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker(&expectations_count)); + + + orientation_factory->SetOrientation(first_orientation); + checker_a->AddExpectation(first_orientation); + ScheduleAddObserver(checker_a.get()); + message_loop()->Run(); + + // The observers should not see this insignificantly different orientation. + orientation_factory->SetOrientation(second_orientation); + checker_b->AddExpectation(first_orientation); + ScheduleAddObserver(checker_b.get()); + message_loop()->Run(); + + orientation_factory->SetOrientation(third_orientation); + checker_a->AddExpectation(third_orientation); + checker_b->AddExpectation(third_orientation); + message_loop()->Run(); + + RemoveObserver(checker_a.get()); + RemoveObserver(checker_b.get()); +} + +} // namespace + +} // namespace device_orientation |