// 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 #include "base/message_loop.h" #include "base/synchronization/lock.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 expectations_queue_; }; // Class for injecting test orientation data into the Provider. class MockOrientationFactory : public base::RefCounted { 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) { base::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) { base::AutoLock auto_lock(orientation_factory_->lock_); *orientation = orientation_factory_->orientation_; return true; } private: scoped_refptr orientation_factory_; }; static MockOrientationFactory* instance_; Orientation orientation_; base::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() : pending_expectations_(0) { } virtual void TearDown() { provider_ = NULL; // Make sure it is really gone. EXPECT_FALSE(Provider::GetInstanceForTests()); // Clean up in any case, so as to not break subsequent test. Provider::SetInstanceForTests(NULL); } // 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(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); } protected: // Number of pending expectations. int pending_expectations_; // Provider instance under test. scoped_refptr provider_; // Message loop for the test thread. MessageLoop message_loop_; }; TEST_F(DeviceOrientationProviderTest, FailingTest) { Init(FailingDataFetcher::Create); scoped_ptr checker_a( new UpdateChecker(&pending_expectations_)); scoped_ptr checker_b( new UpdateChecker(&pending_expectations_)); checker_a->AddExpectation(Orientation::Empty()); provider_->AddObserver(checker_a.get()); MessageLoop::current()->Run(); checker_b->AddExpectation(Orientation::Empty()); provider_->AddObserver(checker_b.get()); MessageLoop::current()->Run(); } TEST_F(DeviceOrientationProviderTest, ProviderIsSingleton) { Init(FailingDataFetcher::Create); scoped_refptr provider_a(Provider::GetInstance()); scoped_refptr provider_b(Provider::GetInstance()); EXPECT_EQ(provider_a.get(), provider_b.get()); } TEST_F(DeviceOrientationProviderTest, BasicPushTest) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); const Orientation kTestOrientation(true, 1, true, 2, true, 3); scoped_ptr checker(new UpdateChecker(&pending_expectations_)); checker->AddExpectation(kTestOrientation); orientation_factory->SetOrientation(kTestOrientation); provider_->AddObserver(checker.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker.get()); } TEST_F(DeviceOrientationProviderTest, MultipleObserversPushTest) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); 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 checker_a( new UpdateChecker(&pending_expectations_)); scoped_ptr checker_b( new UpdateChecker(&pending_expectations_)); scoped_ptr checker_c( new UpdateChecker(&pending_expectations_)); checker_a->AddExpectation(kTestOrientations[0]); orientation_factory->SetOrientation(kTestOrientations[0]); provider_->AddObserver(checker_a.get()); MessageLoop::current()->Run(); checker_a->AddExpectation(kTestOrientations[1]); checker_b->AddExpectation(kTestOrientations[0]); checker_b->AddExpectation(kTestOrientations[1]); orientation_factory->SetOrientation(kTestOrientations[1]); provider_->AddObserver(checker_b.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_a.get()); checker_b->AddExpectation(kTestOrientations[2]); checker_c->AddExpectation(kTestOrientations[1]); checker_c->AddExpectation(kTestOrientations[2]); orientation_factory->SetOrientation(kTestOrientations[2]); provider_->AddObserver(checker_c.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_b.get()); provider_->RemoveObserver(checker_c.get()); } TEST_F(DeviceOrientationProviderTest, ObserverNotRemoved) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); const Orientation kTestOrientation(true, 1, true, 2, true, 3); const Orientation kTestOrientation2(true, 4, true, 5, true, 6); scoped_ptr checker(new UpdateChecker(&pending_expectations_)); checker->AddExpectation(kTestOrientation); orientation_factory->SetOrientation(kTestOrientation); provider_->AddObserver(checker.get()); MessageLoop::current()->Run(); checker->AddExpectation(kTestOrientation2); orientation_factory->SetOrientation(kTestOrientation2); MessageLoop::current()->Run(); // Note that checker is not removed. This should not be a problem. } TEST_F(DeviceOrientationProviderTest, StartFailing) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); const Orientation kTestOrientation(true, 1, true, 2, true, 3); scoped_ptr checker_a(new UpdateChecker( &pending_expectations_)); scoped_ptr checker_b(new UpdateChecker( &pending_expectations_)); orientation_factory->SetOrientation(kTestOrientation); checker_a->AddExpectation(kTestOrientation); provider_->AddObserver(checker_a.get()); MessageLoop::current()->Run(); checker_a->AddExpectation(Orientation::Empty()); orientation_factory->SetOrientation(Orientation::Empty()); MessageLoop::current()->Run(); checker_b->AddExpectation(Orientation::Empty()); provider_->AddObserver(checker_b.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_a.get()); provider_->RemoveObserver(checker_b.get()); } TEST_F(DeviceOrientationProviderTest, StartStopStart) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); const Orientation kTestOrientation(true, 1, true, 2, true, 3); const Orientation kTestOrientation2(true, 4, true, 5, true, 6); scoped_ptr checker_a(new UpdateChecker( &pending_expectations_)); scoped_ptr checker_b(new UpdateChecker( &pending_expectations_)); checker_a->AddExpectation(kTestOrientation); orientation_factory->SetOrientation(kTestOrientation); provider_->AddObserver(checker_a.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_a.get()); // This stops the Provider. checker_b->AddExpectation(kTestOrientation2); orientation_factory->SetOrientation(kTestOrientation2); provider_->AddObserver(checker_b.get()); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_b.get()); } TEST_F(DeviceOrientationProviderTest, SignificantlyDifferent) { scoped_refptr orientation_factory( new MockOrientationFactory()); Init(MockOrientationFactory::CreateDataFetcher); // 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 checker_a(new UpdateChecker( &pending_expectations_)); scoped_ptr checker_b(new UpdateChecker( &pending_expectations_)); orientation_factory->SetOrientation(first_orientation); checker_a->AddExpectation(first_orientation); provider_->AddObserver(checker_a.get()); MessageLoop::current()->Run(); // The observers should not see this insignificantly different orientation. orientation_factory->SetOrientation(second_orientation); checker_b->AddExpectation(first_orientation); provider_->AddObserver(checker_b.get()); MessageLoop::current()->Run(); orientation_factory->SetOrientation(third_orientation); checker_a->AddExpectation(third_orientation); checker_b->AddExpectation(third_orientation); MessageLoop::current()->Run(); provider_->RemoveObserver(checker_a.get()); provider_->RemoveObserver(checker_b.get()); } } // namespace } // namespace device_orientation