// Copyright 2014 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 "base/callback.h" #include "base/message_loop/message_loop.h" #include "content/browser/geofencing/geofencing_manager.h" #include "content/browser/geofencing/geofencing_service.h" #include "content/browser/service_worker/embedded_worker_test_helper.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h" using blink::WebCircularGeofencingRegion; typedef std::map RegionMap; namespace { static const int kRenderProcessId = 99; static const char* kTestRegionId = "region-id"; static const int64 kTestGeofencingRegistrationId = 42; static const int64 kTestGeofencingRegistrationId2 = 43; bool RegionsMatch(const WebCircularGeofencingRegion& expected, const WebCircularGeofencingRegion& arg) { return testing::Matches(expected.latitude)(arg.latitude) && testing::Matches(expected.longitude)(arg.longitude) && testing::Matches(expected.radius)(arg.radius); } } namespace content { class TestGeofencingService : public GeofencingService { public: TestGeofencingService() : is_available_(false) {} bool IsServiceAvailable() override { return is_available_; } void SetIsServiceAvailable(bool is_available) { is_available_ = is_available; } MOCK_METHOD2(RegisterRegion, int64(const WebCircularGeofencingRegion& region, GeofencingRegistrationDelegate* delegate)); MOCK_METHOD1(UnregisterRegion, void(int64 geofencing_registration_id)); private: bool is_available_; }; ACTION_P(SaveDelegate, delegate) { *delegate = arg1; } ACTION_P(QuitRunner, runner) { runner->Quit(); } MATCHER_P(WebCircularGeofencingRegionEq, expected, "") { return RegionsMatch(expected, arg); } class StatusCatcher { public: StatusCatcher() : was_called_(false), runner_(new MessageLoopRunner()) {} void Done(GeofencingStatus status) { CHECK(!was_called_); result_ = status; was_called_ = true; runner_->Quit(); } GeofencingStatus Wait() { runner_->Run(); CHECK(was_called_); return result_; } private: bool was_called_; GeofencingStatus result_; scoped_refptr runner_; }; void SaveResponseCallback(bool* called, int64* store_registration_id, ServiceWorkerStatusCode status, const std::string& status_message, int64 registration_id) { EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status); *called = true; *store_registration_id = registration_id; } ServiceWorkerContextCore::RegistrationCallback MakeRegisteredCallback( bool* called, int64* store_registration_id) { return base::Bind(&SaveResponseCallback, called, store_registration_id); } void CallCompletedCallback(bool* called, ServiceWorkerStatusCode) { *called = true; } ServiceWorkerContextCore::UnregistrationCallback MakeUnregisteredCallback( bool* called) { return base::Bind(&CallCompletedCallback, called); } class GeofencingManagerTest : public testing::Test { public: GeofencingManagerTest() : service_(nullptr) { test_region_.latitude = 37.421999; test_region_.longitude = -122.084015; test_region_.radius = 100; expected_regions_[kTestRegionId] = test_region_; } void SetUp() override { helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId)); service_ = new TestGeofencingService(); manager_ = new GeofencingManager(helper_->context_wrapper()); manager_->SetServiceForTesting(service_); manager_->Init(); worker1_ = RegisterServiceWorker("1"); worker2_ = RegisterServiceWorker("2"); } void TearDown() override { worker1_ = nullptr; worker2_ = nullptr; manager_ = nullptr; delete service_; service_ = nullptr; helper_.reset(); } void SetHasProviderForTests() { service_->SetIsServiceAvailable(true); } scoped_refptr RegisterServiceWorker( const std::string& name) { GURL pattern("http://www.example.com/" + name); GURL script_url("http://www.example.com/service_worker.js"); int64 registration_id = kInvalidServiceWorkerRegistrationId; bool called = false; helper_->context()->RegisterServiceWorker( pattern, script_url, nullptr, MakeRegisteredCallback(&called, ®istration_id)); EXPECT_FALSE(called); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); return make_scoped_refptr(new ServiceWorkerRegistration( pattern, registration_id, helper_->context()->AsWeakPtr())); } void UnregisterServiceWorker( const scoped_refptr& registration) { bool called = false; helper_->context()->UnregisterServiceWorker( registration->pattern(), MakeUnregisteredCallback(&called)); EXPECT_FALSE(called); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(called); } GeofencingStatus RegisterRegionSync( int64 service_worker_registration_id, const std::string& id, const WebCircularGeofencingRegion& region) { StatusCatcher result; manager_->RegisterRegion( service_worker_registration_id, id, region, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); return result.Wait(); } GeofencingStatus RegisterRegionSyncWithServiceResult( int64 service_worker_registration_id, const std::string& id, const WebCircularGeofencingRegion& region, GeofencingStatus service_status, int64 geofencing_registration_id) { StatusCatcher result; GeofencingRegistrationDelegate* delegate = 0; EXPECT_CALL( *service_, RegisterRegion(WebCircularGeofencingRegionEq(region), testing::_)) .WillOnce(testing::DoAll(SaveDelegate(&delegate), testing::Return(geofencing_registration_id))); manager_->RegisterRegion( service_worker_registration_id, id, region, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); CHECK(delegate); delegate->RegistrationFinished(geofencing_registration_id, service_status); return result.Wait(); } GeofencingStatus UnregisterRegionSync(int64 service_worker_registration_id, const std::string& id, bool should_call_service, int64 geofencing_registration_id = 0) { StatusCatcher result; if (should_call_service) { EXPECT_CALL(*service_, UnregisterRegion(geofencing_registration_id)); } manager_->UnregisterRegion( service_worker_registration_id, id, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); return result.Wait(); } void VerifyRegions(int64 service_worker_registration_id, const RegionMap& expected_regions) { RegionMap regions; EXPECT_EQ(GEOFENCING_STATUS_OK, manager_->GetRegisteredRegions(service_worker_registration_id, ®ions)); EXPECT_EQ(expected_regions.size(), regions.size()); for (RegionMap::const_iterator it = expected_regions.begin(); it != expected_regions.end(); ++it) { EXPECT_THAT(regions[it->first], WebCircularGeofencingRegionEq(it->second)); } } protected: TestBrowserThreadBundle threads_; scoped_ptr helper_; TestGeofencingService* service_; scoped_refptr manager_; WebCircularGeofencingRegion test_region_; RegionMap expected_regions_; scoped_refptr worker1_; scoped_refptr worker2_; }; TEST_F(GeofencingManagerTest, RegisterRegion_NoService) { EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, RegisterRegionSync(worker1_->id(), kTestRegionId, test_region_)); } TEST_F(GeofencingManagerTest, UnregisterRegion_NoService) { EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, UnregisterRegionSync(worker1_->id(), kTestRegionId, false)); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_NoService) { RegionMap regions; EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, manager_->GetRegisteredRegions(worker1_->id(), ®ions)); EXPECT_TRUE(regions.empty()); } TEST_F(GeofencingManagerTest, RegisterRegion_FailsInService) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_ERROR, RegisterRegionSyncWithServiceResult(worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_ERROR, -1)); } TEST_F(GeofencingManagerTest, RegisterRegion_SucceedsInService) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), expected_regions_); } TEST_F(GeofencingManagerTest, RegisterRegion_AlreadyRegistered) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), expected_regions_); WebCircularGeofencingRegion region2; region2.latitude = 43.2; region2.longitude = 1.45; region2.radius = 8.5; EXPECT_EQ(GEOFENCING_STATUS_ERROR, RegisterRegionSync(worker1_->id(), kTestRegionId, region2)); VerifyRegions(worker1_->id(), expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_NotRegistered) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED, UnregisterRegionSync(worker1_->id(), kTestRegionId, false)); } TEST_F(GeofencingManagerTest, UnregisterRegion_Success) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); EXPECT_EQ(GEOFENCING_STATUS_OK, UnregisterRegionSync(worker1_->id(), kTestRegionId, true, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), RegionMap()); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_RegistrationInProgress) { SetHasProviderForTests(); StatusCatcher result; GeofencingRegistrationDelegate* delegate = nullptr; EXPECT_CALL( *service_, RegisterRegion(WebCircularGeofencingRegionEq(test_region_), testing::_)) .WillOnce(testing::DoAll(SaveDelegate(&delegate), testing::Return(kTestGeofencingRegistrationId))); manager_->RegisterRegion( worker1_->id(), kTestRegionId, test_region_, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); // At this point the manager should have tried registering the region with // the service, resulting in |delegate| being set. Until the callback is // called the registration is not complete though. EXPECT_NE(delegate, nullptr); VerifyRegions(worker1_->id(), RegionMap()); // Now call the callback, and verify the registration completed succesfully. delegate->RegistrationFinished(kTestGeofencingRegistrationId, GEOFENCING_STATUS_OK); EXPECT_EQ(GEOFENCING_STATUS_OK, result.Wait()); VerifyRegions(worker1_->id(), expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_RegistrationInProgress) { SetHasProviderForTests(); StatusCatcher result; GeofencingRegistrationDelegate* delegate = nullptr; EXPECT_CALL( *service_, RegisterRegion(WebCircularGeofencingRegionEq(test_region_), testing::_)) .WillOnce(testing::DoAll(SaveDelegate(&delegate), testing::Return(kTestGeofencingRegistrationId))); manager_->RegisterRegion( worker1_->id(), kTestRegionId, test_region_, base::Bind(&StatusCatcher::Done, base::Unretained(&result))); // At this point the manager should have tried registering the region with // the service, resulting in |delegate| being set. Until the callback is // called the registration is not complete though. EXPECT_NE(delegate, nullptr); EXPECT_EQ(GEOFENCING_STATUS_UNREGISTRATION_FAILED_NOT_REGISTERED, UnregisterRegionSync(worker1_->id(), kTestRegionId, false)); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_NoRegions) { SetHasProviderForTests(); VerifyRegions(worker1_->id(), RegionMap()); } TEST_F(GeofencingManagerTest, RegisterRegion_SeparateServiceWorkers) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), expected_regions_); VerifyRegions(worker2_->id(), RegionMap()); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker2_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId2)); VerifyRegions(worker1_->id(), expected_regions_); VerifyRegions(worker2_->id(), expected_regions_); } TEST_F(GeofencingManagerTest, UnregisterRegion_SeparateServiceWorkers) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker2_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId2)); EXPECT_EQ(GEOFENCING_STATUS_OK, UnregisterRegionSync(worker1_->id(), kTestRegionId, true, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), RegionMap()); VerifyRegions(worker2_->id(), expected_regions_); EXPECT_EQ(GEOFENCING_STATUS_OK, UnregisterRegionSync(worker2_->id(), kTestRegionId, true, kTestGeofencingRegistrationId2)); VerifyRegions(worker1_->id(), RegionMap()); VerifyRegions(worker2_->id(), RegionMap()); } TEST_F(GeofencingManagerTest, ShutdownCleansRegistrations) { SetHasProviderForTests(); scoped_refptr runner(new MessageLoopRunner()); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); EXPECT_CALL(*service_, UnregisterRegion(kTestGeofencingRegistrationId)) .WillOnce(QuitRunner(runner)); manager_->Shutdown(); runner->Run(); } TEST_F(GeofencingManagerTest, OnRegistrationDeleted) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker2_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId2)); EXPECT_CALL(*service_, UnregisterRegion(kTestGeofencingRegistrationId)); UnregisterServiceWorker(worker1_); VerifyRegions(worker1_->id(), RegionMap()); VerifyRegions(worker2_->id(), expected_regions_); EXPECT_CALL(*service_, UnregisterRegion(kTestGeofencingRegistrationId2)); UnregisterServiceWorker(worker2_); VerifyRegions(worker1_->id(), RegionMap()); VerifyRegions(worker2_->id(), RegionMap()); } TEST_F(GeofencingManagerTest, RegisterRegion_MockedNoService) { manager_->SetMockProvider(GeofencingMockState::SERVICE_UNAVAILABLE); EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, RegisterRegionSync(worker1_->id(), kTestRegionId, test_region_)); } TEST_F(GeofencingManagerTest, UnregisterRegion_MockedNoService) { manager_->SetMockProvider(GeofencingMockState::SERVICE_UNAVAILABLE); EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, UnregisterRegionSync(worker1_->id(), kTestRegionId, false)); } TEST_F(GeofencingManagerTest, GetRegisteredRegions_MockedNoService) { manager_->SetMockProvider(GeofencingMockState::SERVICE_UNAVAILABLE); RegionMap regions; EXPECT_EQ(GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE, manager_->GetRegisteredRegions(worker1_->id(), ®ions)); EXPECT_TRUE(regions.empty()); } TEST_F(GeofencingManagerTest, RegisterRegion_MockedService) { manager_->SetMockProvider(GeofencingMockState::SERVICE_AVAILABLE); // Make sure real service doesn't get called. EXPECT_CALL(*service_, RegisterRegion(testing::_, testing::_)).Times(0); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSync(worker1_->id(), kTestRegionId, test_region_)); VerifyRegions(worker1_->id(), expected_regions_); } TEST_F(GeofencingManagerTest, SetMockProviderClearsRegistrations) { SetHasProviderForTests(); EXPECT_EQ(GEOFENCING_STATUS_OK, RegisterRegionSyncWithServiceResult( worker1_->id(), kTestRegionId, test_region_, GEOFENCING_STATUS_OK, kTestGeofencingRegistrationId)); VerifyRegions(worker1_->id(), expected_regions_); EXPECT_CALL(*service_, UnregisterRegion(kTestGeofencingRegistrationId)); manager_->SetMockProvider(GeofencingMockState::SERVICE_AVAILABLE); VerifyRegions(worker1_->id(), RegionMap()); } } // namespace content