summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorhans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-23 07:39:28 +0000
committerhans@chromium.org <hans@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-23 07:39:28 +0000
commitddd1844a44696ec660bcbb6d939df0299a03b3a6 (patch)
tree374ae15c8c723deecbe8011209869720c6c0f62f /chrome/browser
parentc9f57d3571e01d1be5b45d484174f9f8fabbd0ee (diff)
downloadchromium_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.h21
-rw-r--r--chrome/browser/device_orientation/orientation.h6
-rw-r--r--chrome/browser/device_orientation/provider.cc15
-rw-r--r--chrome/browser/device_orientation/provider.h8
-rw-r--r--chrome/browser/device_orientation/provider_impl.cc199
-rw-r--r--chrome/browser/device_orientation/provider_impl.h87
-rw-r--r--chrome/browser/device_orientation/provider_unittest.cc369
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