// 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 #include #include #include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/time.h" #include "content/browser/geolocation/win7_location_api_win.h" #include "content/public/common/geoposition.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::NiceMock; using testing::Return; namespace { class MockLatLongReport : public ILatLongReport { public: MockLatLongReport() : ref_count_(1) { ON_CALL(*this, GetAltitude(_)) .WillByDefault(Invoke(this, &MockLatLongReport::GetAltitudeValid)); ON_CALL(*this, GetAltitudeError(_)) .WillByDefault(Invoke(this, &MockLatLongReport::GetAltitudeErrorValid)); ON_CALL(*this, GetErrorRadius(_)) .WillByDefault(Invoke(this, &MockLatLongReport::GetErrorRadiusValid)); ON_CALL(*this, GetLatitude(_)) .WillByDefault(Invoke(this, &MockLatLongReport::GetLatitudeValid)); ON_CALL(*this, GetLongitude(_)) .WillByDefault(Invoke(this, &MockLatLongReport::GetLongitudeValid)); ON_CALL(*this, GetValue(_, _)) .WillByDefault(Invoke(this, &MockLatLongReport::GetValueValid)); ON_CALL(*this, Release()) .WillByDefault(Invoke(this, &MockLatLongReport::ReleaseInternal)); ON_CALL(*this, AddRef()) .WillByDefault(Invoke(this, &MockLatLongReport::AddRefInternal)); } // ILatLongReport MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetAltitude, HRESULT(DOUBLE*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetAltitudeError, HRESULT(DOUBLE*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetErrorRadius, HRESULT(DOUBLE*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetLatitude, HRESULT(DOUBLE*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetLongitude, HRESULT(DOUBLE*)); // ILocationReport MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetSensorID, HRESULT(SENSOR_ID*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTimestamp, HRESULT(SYSTEMTIME*)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetValue, HRESULT(REFPROPERTYKEY, PROPVARIANT*)); // IUnknown MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, QueryInterface, HRESULT(REFIID, void**)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG()); HRESULT GetAltitudeValid(DOUBLE* altitude) { *altitude = 20.5; return S_OK; } HRESULT GetAltitudeErrorValid(DOUBLE* altitude_error) { *altitude_error = 10.0; return S_OK; } HRESULT GetErrorRadiusValid(DOUBLE* error) { *error = 5.0; return S_OK; } HRESULT GetLatitudeValid(DOUBLE* latitude) { *latitude = 51.0; return S_OK; } HRESULT GetLongitudeValid(DOUBLE* longitude) { *longitude = -0.1; return S_OK; } HRESULT GetValueValid(REFPROPERTYKEY prop_key, PROPVARIANT* prop) { prop->dblVal = 10.0; return S_OK; } private: ~MockLatLongReport() {} ULONG AddRefInternal() { return InterlockedIncrement(&ref_count_); } ULONG ReleaseInternal() { LONG new_ref_count = InterlockedDecrement(&ref_count_); if (0 == new_ref_count) delete this; return new_ref_count; } LONG ref_count_; }; class MockReport : public ILocationReport { public: MockReport() : ref_count_(1) { mock_lat_long_report_ = new MockLatLongReport(); ON_CALL(*this, QueryInterface(_, _)) .WillByDefault(Invoke(this, &MockReport::QueryInterfaceValid)); ON_CALL(*this, Release()) .WillByDefault(Invoke(this, &MockReport::ReleaseInternal)); ON_CALL(*this, AddRef()) .WillByDefault(Invoke(this, &MockReport::AddRefInternal)); } // ILocationReport MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetSensorID, HRESULT(SENSOR_ID*)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetTimestamp, HRESULT(SYSTEMTIME*)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetValue, HRESULT(REFPROPERTYKEY, PROPVARIANT*)); // IUnknown MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, QueryInterface, HRESULT(REFIID, void**)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG()); MockLatLongReport* mock_lat_long_report_; private: ~MockReport() { mock_lat_long_report_->Release(); } ULONG AddRefInternal() { return InterlockedIncrement(&ref_count_); } ULONG ReleaseInternal() { LONG new_ref_count = InterlockedDecrement(&ref_count_); if (0 == new_ref_count) delete this; return new_ref_count; } HRESULT QueryInterfaceValid(REFIID id, void** report) { EXPECT_TRUE(id == IID_ILatLongReport); *report = reinterpret_cast(mock_lat_long_report_); mock_lat_long_report_->AddRef(); return S_OK; } LONG ref_count_; }; class MockLocation : public ILocation { public: MockLocation() : ref_count_(1) { mock_report_ = new MockReport(); ON_CALL(*this, SetDesiredAccuracy(_, _)) .WillByDefault(Return(S_OK)); ON_CALL(*this, GetReport(_, _)) .WillByDefault(Invoke(this, &MockLocation::GetReportValid)); ON_CALL(*this, RequestPermissions(_, _, _, _)) .WillByDefault(Return(S_OK)); ON_CALL(*this, AddRef()) .WillByDefault(Invoke(this, &MockLocation::AddRefInternal)); ON_CALL(*this, Release()) .WillByDefault(Invoke(this, &MockLocation::ReleaseInternal)); } // ILocation MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetDesiredAccuracy, HRESULT(REFIID, LOCATION_DESIRED_ACCURACY*)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetReport, HRESULT(REFIID, ILocationReport**)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetReportInterval, HRESULT(REFIID, DWORD*)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, GetReportStatus, HRESULT(REFIID, LOCATION_REPORT_STATUS*)); MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, RequestPermissions, HRESULT(HWND, IID*, ULONG, BOOL)); MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, RegisterForReport, HRESULT(ILocationEvents*, REFIID, DWORD)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, SetDesiredAccuracy, HRESULT(REFIID, LOCATION_DESIRED_ACCURACY)); MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, SetReportInterval, HRESULT(REFIID, DWORD)); MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, UnregisterForReport, HRESULT(REFIID)); // IUnknown MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, QueryInterface, HRESULT(REFIID, void**)); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG()); MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG()); MockReport* mock_report_; protected: ~MockLocation() { mock_report_->Release(); } private: HRESULT GetReportValid(REFIID report_type, ILocationReport** location_report) { *location_report = reinterpret_cast(mock_report_); mock_report_->AddRef(); return S_OK; } ULONG AddRefInternal() { return InterlockedIncrement(&ref_count_); } ULONG ReleaseInternal() { LONG new_ref_count = InterlockedDecrement(&ref_count_); if (0 == new_ref_count) delete this; return new_ref_count; } LONG ref_count_; }; HRESULT __stdcall MockPropVariantToDoubleFunction(REFPROPVARIANT propvarIn, DOUBLE *pdblRet) { CHECK_EQ(10.0, propvarIn.dblVal); *pdblRet = 10.0; return S_OK; } // TODO(allanwoj): Either make mock classes into NiceMock classes // or check every mock method call. class GeolocationApiWin7Tests : public testing::Test { public: GeolocationApiWin7Tests() { } virtual void SetUp() { api_.reset(CreateMock()); report_ = locator_->mock_report_; lat_long_report_ = report_->mock_lat_long_report_; } virtual void TearDown() { locator_->Release(); api_.reset(); } ~GeolocationApiWin7Tests() { } protected: Win7LocationApi* CreateMock() { NiceMock* locator = new NiceMock(); locator_ = locator; return Win7LocationApi::CreateForTesting(&MockPropVariantToDoubleFunction, locator); } scoped_ptr api_; MockLatLongReport* lat_long_report_; NiceMock* locator_; MockReport* report_; }; TEST_F(GeolocationApiWin7Tests, PermissionDenied) { EXPECT_CALL(*locator_, GetReport(_, _)) .Times(AtLeast(1)) .WillRepeatedly(Return(E_ACCESSDENIED)); content::Geoposition position; api_->GetPosition(&position); EXPECT_EQ(content::Geoposition::ERROR_CODE_PERMISSION_DENIED, position.error_code); } TEST_F(GeolocationApiWin7Tests, GetValidPosition) { EXPECT_CALL(*locator_, GetReport(_, _)) .Times(AtLeast(1)); content::Geoposition position; api_->GetPosition(&position); EXPECT_TRUE(position.Validate()); } TEST_F(GeolocationApiWin7Tests, GetInvalidPosition) { EXPECT_CALL(*lat_long_report_, GetLatitude(_)) .Times(AtLeast(1)) .WillRepeatedly(Return(HRESULT_FROM_WIN32(ERROR_NO_DATA))); EXPECT_CALL(*locator_, GetReport(_, _)) .Times(AtLeast(1)); content::Geoposition position; api_->GetPosition(&position); EXPECT_FALSE(position.Validate()); } } // namespace