blob: 1f310ea3ad4c26fb0b28d884d752a00b2c963c42 [file] [log] [blame]
// Copyright (c) 2012 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 <map>
#include <queue>
#include "base/message_loop/message_loop.h"
#include "base/synchronization/lock.h"
#include "content/browser/device_orientation/data_fetcher.h"
#include "content/browser/device_orientation/device_data.h"
#include "content/browser/device_orientation/orientation.h"
#include "content/browser/device_orientation/provider.h"
#include "content/browser/device_orientation/provider_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
// Class for testing multiple types of device data.
class TestData : public DeviceData {
public:
TestData()
: value_(0) {
}
// From DeviceData.
virtual IPC::Message* CreateIPCMessage(int render_view_id) const OVERRIDE {
NOTREACHED();
return NULL;
}
virtual bool ShouldFireEvent(const DeviceData* old_data) const OVERRIDE {
return true;
}
void set_value(double value) { value_ = value; }
double value() const { return value_; }
private:
virtual ~TestData() { }
double value_;
};
// Class for checking expectations on device_data updates from the Provider.
class UpdateChecker : public Provider::Observer {
public:
UpdateChecker(DeviceData::Type device_data_type,
int *expectations_count_ptr)
: Observer(device_data_type),
expectations_count_ptr_(expectations_count_ptr) {
}
virtual ~UpdateChecker() {}
// From Provider::Observer.
virtual void OnDeviceDataUpdate(const DeviceData* device_data,
DeviceData::Type device_data_type) OVERRIDE = 0;
void AddExpectation(const DeviceData* device_data) {
scoped_refptr<const DeviceData> expected_device_data(device_data);
expectations_queue_.push(expected_device_data);
++(*expectations_count_ptr_);
}
protected:
// Set up by the test fixture, which then blocks while it is accessed
// from OnDeviceDataUpdate which is executed on the test fixture's
// message_loop_.
int* expectations_count_ptr_;
std::queue<scoped_refptr<const DeviceData> > expectations_queue_;
};
// Class for checking expectations on orientation updates from the Provider.
class OrientationUpdateChecker : public UpdateChecker {
public:
explicit OrientationUpdateChecker(int* expectations_count_ptr)
: UpdateChecker(DeviceData::kTypeOrientation, expectations_count_ptr) {
}
virtual ~OrientationUpdateChecker() {}
// From UpdateChecker.
virtual void OnDeviceDataUpdate(const DeviceData* device_data,
DeviceData::Type device_data_type) OVERRIDE {
ASSERT_FALSE(expectations_queue_.empty());
ASSERT_EQ(DeviceData::kTypeOrientation, device_data_type);
scoped_refptr<const Orientation> orientation(
static_cast<const Orientation*>(device_data));
if (orientation.get() == NULL)
orientation = new Orientation();
scoped_refptr<const Orientation> expected(static_cast<const Orientation*>(
(expectations_queue_.front().get())));
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());
EXPECT_EQ(expected->can_provide_absolute(),
orientation->can_provide_absolute());
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());
if (expected->can_provide_absolute())
EXPECT_EQ(expected->absolute(), orientation->absolute());
--(*expectations_count_ptr_);
if (*expectations_count_ptr_ == 0) {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
}
};
// Class for checking expectations on test_data updates from the Provider.
class TestDataUpdateChecker : public UpdateChecker {
public:
explicit TestDataUpdateChecker(int* expectations_count_ptr)
: UpdateChecker(DeviceData::kTypeTest, expectations_count_ptr) {
}
// From UpdateChecker.
virtual void OnDeviceDataUpdate(const DeviceData* device_data,
DeviceData::Type device_data_type) OVERRIDE {
ASSERT_FALSE(expectations_queue_.empty());
ASSERT_EQ(DeviceData::kTypeTest, device_data_type);
scoped_refptr<const TestData> test_data(
static_cast<const TestData*>(device_data));
if (test_data.get() == NULL)
test_data = new TestData();
scoped_refptr<const TestData> expected(static_cast<const TestData*>(
(expectations_queue_.front().get())));
expectations_queue_.pop();
EXPECT_EQ(expected->value(), test_data->value());
--(*expectations_count_ptr_);
if (*expectations_count_ptr_ == 0) {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
}
};
// Class for injecting test device data into the Provider.
class MockDeviceDataFactory
: public base::RefCountedThreadSafe<MockDeviceDataFactory> {
public:
MockDeviceDataFactory()
: is_failing_(false) {
}
static void SetCurInstance(MockDeviceDataFactory* instance) {
if (instance) {
EXPECT_FALSE(instance_);
}
else {
EXPECT_TRUE(instance_);
}
instance_ = instance;
}
static DataFetcher* CreateDataFetcher() {
EXPECT_TRUE(instance_);
return new MockDataFetcher(instance_);
}
void SetDeviceData(const DeviceData* device_data, DeviceData::Type type) {
base::AutoLock auto_lock(lock_);
device_data_map_[type] = device_data;
}
void SetFailing(bool is_failing) {
base::AutoLock auto_lock(lock_);
is_failing_ = is_failing;
}
private:
friend class base::RefCountedThreadSafe<MockDeviceDataFactory>;
~MockDeviceDataFactory() {
}
// Owned by ProviderImpl. Holds a reference back to MockDeviceDataFactory.
class MockDataFetcher : public DataFetcher {
public:
explicit MockDataFetcher(MockDeviceDataFactory* device_data_factory)
: device_data_factory_(device_data_factory) { }
// From DataFetcher. Called by the Provider.
virtual const DeviceData* GetDeviceData(
DeviceData::Type device_data_type) OVERRIDE {
base::AutoLock auto_lock(device_data_factory_->lock_);
if (device_data_factory_->is_failing_)
return NULL;
return device_data_factory_->device_data_map_[device_data_type].get();
}
private:
scoped_refptr<MockDeviceDataFactory> device_data_factory_;
};
static MockDeviceDataFactory* instance_;
std::map<DeviceData::Type, scoped_refptr<const DeviceData> > device_data_map_;
bool is_failing_;
base::Lock lock_;
};
MockDeviceDataFactory* MockDeviceDataFactory::instance_;
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
// DataFetcherFactory factory.
void Init(ProviderImpl::DataFetcherFactory factory) {
provider_ = new ProviderImpl(factory);
Provider::SetInstanceForTests(provider_.get());
}
protected:
// Number of pending expectations.
int pending_expectations_;
// Provider instance under test.
scoped_refptr<Provider> provider_;
// Message loop for the test thread.
base::MessageLoop message_loop_;
};
TEST_F(DeviceOrientationProviderTest, FailingTest) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_ptr<OrientationUpdateChecker> checker_a(
new OrientationUpdateChecker(&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_b(
new OrientationUpdateChecker(&pending_expectations_));
checker_a->AddExpectation(new Orientation());
provider_->AddObserver(checker_a.get());
base::MessageLoop::current()->Run();
checker_b->AddExpectation(new Orientation());
provider_->AddObserver(checker_b.get());
base::MessageLoop::current()->Run();
MockDeviceDataFactory::SetCurInstance(NULL);
}
TEST_F(DeviceOrientationProviderTest, ProviderIsSingleton) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Provider> provider_a(Provider::GetInstance());
scoped_refptr<Provider> provider_b(Provider::GetInstance());
EXPECT_EQ(provider_a.get(), provider_b.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
TEST_F(DeviceOrientationProviderTest, BasicPushTest) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Orientation> test_orientation(new Orientation());
test_orientation->set_alpha(1);
test_orientation->set_beta(2);
test_orientation->set_gamma(3);
test_orientation->set_absolute(true);
scoped_ptr<OrientationUpdateChecker> checker(
new OrientationUpdateChecker(&pending_expectations_));
checker->AddExpectation(test_orientation.get());
device_data_factory->SetDeviceData(test_orientation.get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
// Tests multiple observers observing the same type of data.
TEST_F(DeviceOrientationProviderTest, MultipleObserversPushTest) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Orientation> test_orientations[] = {new Orientation(),
new Orientation(), new Orientation()};
test_orientations[0]->set_alpha(1);
test_orientations[0]->set_beta(2);
test_orientations[0]->set_gamma(3);
test_orientations[0]->set_absolute(true);
test_orientations[1]->set_alpha(4);
test_orientations[1]->set_beta(5);
test_orientations[1]->set_gamma(6);
test_orientations[1]->set_absolute(false);
test_orientations[2]->set_alpha(7);
test_orientations[2]->set_beta(8);
test_orientations[2]->set_gamma(9);
// can't provide absolute
scoped_ptr<OrientationUpdateChecker> checker_a(
new OrientationUpdateChecker(&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_b(
new OrientationUpdateChecker(&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_c(
new OrientationUpdateChecker(&pending_expectations_));
checker_a->AddExpectation(test_orientations[0].get());
device_data_factory->SetDeviceData(test_orientations[0].get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker_a.get());
base::MessageLoop::current()->Run();
checker_a->AddExpectation(test_orientations[1].get());
checker_b->AddExpectation(test_orientations[0].get());
checker_b->AddExpectation(test_orientations[1].get());
device_data_factory->SetDeviceData(test_orientations[1].get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker_b.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_a.get());
checker_b->AddExpectation(test_orientations[2].get());
checker_c->AddExpectation(test_orientations[1].get());
checker_c->AddExpectation(test_orientations[2].get());
device_data_factory->SetDeviceData(test_orientations[2].get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker_c.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_b.get());
provider_->RemoveObserver(checker_c.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
// Test for when the fetcher cannot provide the first type of data but can
// provide the second type.
TEST_F(DeviceOrientationProviderTest, FailingFirstDataTypeTest) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_ptr<TestDataUpdateChecker> test_data_checker(
new TestDataUpdateChecker(&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> orientation_checker(
new OrientationUpdateChecker(&pending_expectations_));
scoped_refptr<Orientation> test_orientation(new Orientation());
test_orientation->set_alpha(1);
test_orientation->set_beta(2);
test_orientation->set_gamma(3);
test_orientation->set_absolute(true);
test_data_checker->AddExpectation(new TestData());
provider_->AddObserver(test_data_checker.get());
base::MessageLoop::current()->Run();
orientation_checker->AddExpectation(test_orientation.get());
device_data_factory->SetDeviceData(test_orientation.get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(orientation_checker.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(test_data_checker.get());
provider_->RemoveObserver(orientation_checker.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
#if defined(OS_LINUX) || defined(OS_WIN)
// Flakily DCHECKs on Linux. See crbug.com/104950.
// FLAKY on Win. See crbug.com/104950.
#define MAYBE_ObserverNotRemoved DISABLED_ObserverNotRemoved
#else
#define MAYBE_ObserverNotRemoved ObserverNotRemoved
#endif
TEST_F(DeviceOrientationProviderTest, MAYBE_ObserverNotRemoved) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Orientation> test_orientation(new Orientation());
test_orientation->set_alpha(1);
test_orientation->set_beta(2);
test_orientation->set_gamma(3);
test_orientation->set_absolute(true);
scoped_refptr<Orientation> test_orientation2(new Orientation());
test_orientation2->set_alpha(4);
test_orientation2->set_beta(5);
test_orientation2->set_gamma(6);
test_orientation2->set_absolute(false);
scoped_ptr<OrientationUpdateChecker> checker(
new OrientationUpdateChecker(&pending_expectations_));
checker->AddExpectation(test_orientation.get());
device_data_factory->SetDeviceData(test_orientation.get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker.get());
base::MessageLoop::current()->Run();
checker->AddExpectation(test_orientation2.get());
device_data_factory->SetDeviceData(test_orientation2.get(),
DeviceData::kTypeOrientation);
base::MessageLoop::current()->Run();
MockDeviceDataFactory::SetCurInstance(NULL);
// Note that checker is not removed. This should not be a problem.
}
#if defined(OS_WIN)
// FLAKY on Win. See crbug.com/104950.
#define MAYBE_StartFailing DISABLED_StartFailing
#else
#define MAYBE_StartFailing StartFailing
#endif
TEST_F(DeviceOrientationProviderTest, MAYBE_StartFailing) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Orientation> test_orientation(new Orientation());
test_orientation->set_alpha(1);
test_orientation->set_beta(2);
test_orientation->set_gamma(3);
test_orientation->set_absolute(true);
scoped_ptr<OrientationUpdateChecker> checker_a(new OrientationUpdateChecker(
&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_b(new OrientationUpdateChecker(
&pending_expectations_));
device_data_factory->SetDeviceData(test_orientation.get(),
DeviceData::kTypeOrientation);
checker_a->AddExpectation(test_orientation.get());
provider_->AddObserver(checker_a.get());
base::MessageLoop::current()->Run();
checker_a->AddExpectation(new Orientation());
device_data_factory->SetFailing(true);
base::MessageLoop::current()->Run();
checker_b->AddExpectation(new Orientation());
provider_->AddObserver(checker_b.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_a.get());
provider_->RemoveObserver(checker_b.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
TEST_F(DeviceOrientationProviderTest, StartStopStart) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::CreateDataFetcher);
scoped_refptr<Orientation> test_orientation(new Orientation());
test_orientation->set_alpha(1);
test_orientation->set_beta(2);
test_orientation->set_gamma(3);
test_orientation->set_absolute(true);
scoped_refptr<Orientation> test_orientation2(new Orientation());
test_orientation2->set_alpha(4);
test_orientation2->set_beta(5);
test_orientation2->set_gamma(6);
test_orientation2->set_absolute(false);
scoped_ptr<OrientationUpdateChecker> checker_a(new OrientationUpdateChecker(
&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_b(new OrientationUpdateChecker(
&pending_expectations_));
checker_a->AddExpectation(test_orientation.get());
device_data_factory->SetDeviceData(test_orientation.get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker_a.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_a.get()); // This stops the Provider.
checker_b->AddExpectation(test_orientation2.get());
device_data_factory->SetDeviceData(test_orientation2.get(),
DeviceData::kTypeOrientation);
provider_->AddObserver(checker_b.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_b.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
// Tests that Orientation events only fire if the change is significant.
TEST_F(DeviceOrientationProviderTest, OrientationSignificantlyDifferent) {
scoped_refptr<MockDeviceDataFactory> device_data_factory(
new MockDeviceDataFactory());
MockDeviceDataFactory::SetCurInstance(device_data_factory.get());
Init(MockDeviceDataFactory::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;
scoped_refptr<Orientation> first_orientation(new Orientation());
first_orientation->set_alpha(kAlpha);
first_orientation->set_beta(kBeta);
first_orientation->set_gamma(kGamma);
first_orientation->set_absolute(true);
scoped_refptr<Orientation> second_orientation(new Orientation());
second_orientation->set_alpha(kAlpha + kInsignificantDifference);
second_orientation->set_beta(kBeta + kInsignificantDifference);
second_orientation->set_gamma(kGamma + kInsignificantDifference);
second_orientation->set_absolute(false);
scoped_refptr<Orientation> third_orientation(new Orientation());
third_orientation->set_alpha(kAlpha + kSignificantDifference);
third_orientation->set_beta(kBeta + kSignificantDifference);
third_orientation->set_gamma(kGamma + kSignificantDifference);
// can't provide absolute
scoped_ptr<OrientationUpdateChecker> checker_a(new OrientationUpdateChecker(
&pending_expectations_));
scoped_ptr<OrientationUpdateChecker> checker_b(new OrientationUpdateChecker(
&pending_expectations_));
device_data_factory->SetDeviceData(first_orientation.get(),
DeviceData::kTypeOrientation);
checker_a->AddExpectation(first_orientation.get());
provider_->AddObserver(checker_a.get());
base::MessageLoop::current()->Run();
// The observers should not see this insignificantly different orientation.
device_data_factory->SetDeviceData(second_orientation.get(),
DeviceData::kTypeOrientation);
checker_b->AddExpectation(first_orientation.get());
provider_->AddObserver(checker_b.get());
base::MessageLoop::current()->Run();
device_data_factory->SetDeviceData(third_orientation.get(),
DeviceData::kTypeOrientation);
checker_a->AddExpectation(third_orientation.get());
checker_b->AddExpectation(third_orientation.get());
base::MessageLoop::current()->Run();
provider_->RemoveObserver(checker_a.get());
provider_->RemoveObserver(checker_b.get());
MockDeviceDataFactory::SetCurInstance(NULL);
}
} // namespace
} // namespace content