| // Copyright 2013 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 "content/browser/service_worker/service_worker_registration.h" |
| |
| #include <stdint.h> |
| #include <utility> |
| |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| #include "base/run_loop.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "content/browser/service_worker/embedded_worker_status.h" |
| #include "content/browser/service_worker/embedded_worker_test_helper.h" |
| #include "content/browser/service_worker/service_worker_context_core.h" |
| #include "content/browser/service_worker/service_worker_registration_handle.h" |
| #include "content/browser/service_worker/service_worker_test_utils.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // From service_worker_registration.cc. |
| constexpr base::TimeDelta kMaxLameDuckTime = base::TimeDelta::FromMinutes(5); |
| |
| int CreateInflightRequest(ServiceWorkerVersion* version) { |
| version->StartWorker(ServiceWorkerMetrics::EventType::PUSH, |
| base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| base::RunLoop().RunUntilIdle(); |
| return version->StartRequest( |
| ServiceWorkerMetrics::EventType::PUSH, |
| base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); |
| } |
| |
| } // namespace |
| |
| class ServiceWorkerRegistrationTest : public testing::Test { |
| public: |
| ServiceWorkerRegistrationTest() |
| : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} |
| |
| void SetUp() override { |
| helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath())); |
| |
| helper_->context()->storage()->LazyInitialize(base::Bind(&base::DoNothing)); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void TearDown() override { |
| helper_.reset(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| ServiceWorkerContextCore* context() { return helper_->context(); } |
| ServiceWorkerStorage* storage() { return helper_->context()->storage(); } |
| |
| class RegistrationListener : public ServiceWorkerRegistration::Listener { |
| public: |
| RegistrationListener() {} |
| ~RegistrationListener() { |
| if (observed_registration_.get()) |
| observed_registration_->RemoveListener(this); |
| } |
| |
| void OnVersionAttributesChanged( |
| ServiceWorkerRegistration* registration, |
| ChangedVersionAttributesMask changed_mask, |
| const ServiceWorkerRegistrationInfo& info) override { |
| observed_registration_ = registration; |
| observed_changed_mask_ = changed_mask; |
| observed_info_ = info; |
| } |
| |
| void OnRegistrationFailed( |
| ServiceWorkerRegistration* registration) override { |
| NOTREACHED(); |
| } |
| |
| void OnUpdateFound(ServiceWorkerRegistration* registration) override { |
| NOTREACHED(); |
| } |
| |
| void Reset() { |
| observed_registration_ = NULL; |
| observed_changed_mask_ = ChangedVersionAttributesMask(); |
| observed_info_ = ServiceWorkerRegistrationInfo(); |
| } |
| |
| scoped_refptr<ServiceWorkerRegistration> observed_registration_; |
| ChangedVersionAttributesMask observed_changed_mask_; |
| ServiceWorkerRegistrationInfo observed_info_; |
| }; |
| |
| protected: |
| std::unique_ptr<EmbeddedWorkerTestHelper> helper_; |
| TestBrowserThreadBundle thread_bundle_; |
| }; |
| |
| TEST_F(ServiceWorkerRegistrationTest, SetAndUnsetVersions) { |
| const GURL kScope("http://www.example.not/"); |
| const GURL kScript("http://www.example.not/service_worker.js"); |
| int64_t kRegistrationId = 1L; |
| scoped_refptr<ServiceWorkerRegistration> registration = |
| new ServiceWorkerRegistration(ServiceWorkerRegistrationOptions(kScope), |
| kRegistrationId, context()->AsWeakPtr()); |
| |
| const int64_t version_1_id = 1L; |
| const int64_t version_2_id = 2L; |
| scoped_refptr<ServiceWorkerVersion> version_1 = new ServiceWorkerVersion( |
| registration.get(), kScript, version_1_id, context()->AsWeakPtr()); |
| scoped_refptr<ServiceWorkerVersion> version_2 = new ServiceWorkerVersion( |
| registration.get(), kScript, version_2_id, context()->AsWeakPtr()); |
| |
| RegistrationListener listener; |
| registration->AddListener(&listener); |
| registration->SetActiveVersion(version_1); |
| |
| EXPECT_EQ(version_1.get(), registration->active_version()); |
| EXPECT_EQ(registration, listener.observed_registration_); |
| EXPECT_EQ(ChangedVersionAttributesMask::ACTIVE_VERSION, |
| listener.observed_changed_mask_.changed()); |
| EXPECT_EQ(kScope, listener.observed_info_.pattern); |
| EXPECT_EQ(version_1_id, listener.observed_info_.active_version.version_id); |
| EXPECT_EQ(kScript, listener.observed_info_.active_version.script_url); |
| EXPECT_EQ(listener.observed_info_.installing_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| EXPECT_EQ(listener.observed_info_.waiting_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| listener.Reset(); |
| |
| registration->SetInstallingVersion(version_2); |
| |
| EXPECT_EQ(version_2.get(), registration->installing_version()); |
| EXPECT_EQ(ChangedVersionAttributesMask::INSTALLING_VERSION, |
| listener.observed_changed_mask_.changed()); |
| EXPECT_EQ(version_1_id, listener.observed_info_.active_version.version_id); |
| EXPECT_EQ(version_2_id, |
| listener.observed_info_.installing_version.version_id); |
| EXPECT_EQ(listener.observed_info_.waiting_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| listener.Reset(); |
| |
| registration->SetWaitingVersion(version_2); |
| |
| EXPECT_EQ(version_2.get(), registration->waiting_version()); |
| EXPECT_FALSE(registration->installing_version()); |
| EXPECT_TRUE(listener.observed_changed_mask_.waiting_changed()); |
| EXPECT_TRUE(listener.observed_changed_mask_.installing_changed()); |
| EXPECT_EQ(version_1_id, listener.observed_info_.active_version.version_id); |
| EXPECT_EQ(version_2_id, listener.observed_info_.waiting_version.version_id); |
| EXPECT_EQ(listener.observed_info_.installing_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| listener.Reset(); |
| |
| registration->UnsetVersion(version_2.get()); |
| |
| EXPECT_FALSE(registration->waiting_version()); |
| EXPECT_EQ(ChangedVersionAttributesMask::WAITING_VERSION, |
| listener.observed_changed_mask_.changed()); |
| EXPECT_EQ(version_1_id, listener.observed_info_.active_version.version_id); |
| EXPECT_EQ(listener.observed_info_.waiting_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| EXPECT_EQ(listener.observed_info_.installing_version.version_id, |
| kInvalidServiceWorkerVersionId); |
| } |
| |
| TEST_F(ServiceWorkerRegistrationTest, FailedRegistrationNoCrash) { |
| const GURL kScope("http://www.example.not/"); |
| int64_t kRegistrationId = 1L; |
| scoped_refptr<ServiceWorkerRegistration> registration = |
| new ServiceWorkerRegistration(ServiceWorkerRegistrationOptions(kScope), |
| kRegistrationId, context()->AsWeakPtr()); |
| std::unique_ptr<ServiceWorkerRegistrationHandle> handle( |
| new ServiceWorkerRegistrationHandle( |
| context()->AsWeakPtr(), base::WeakPtr<ServiceWorkerProviderHost>(), |
| registration.get())); |
| registration->NotifyRegistrationFailed(); |
| // Don't crash when handle gets destructed. |
| } |
| |
| TEST_F(ServiceWorkerRegistrationTest, NavigationPreload) { |
| const GURL kScope("http://www.example.not/"); |
| const GURL kScript("https://www.example.not/service_worker.js"); |
| // Setup. |
| scoped_refptr<ServiceWorkerRegistration> registration = |
| new ServiceWorkerRegistration(ServiceWorkerRegistrationOptions(kScope), |
| storage()->NewRegistrationId(), |
| context()->AsWeakPtr()); |
| scoped_refptr<ServiceWorkerVersion> version_1 = new ServiceWorkerVersion( |
| registration.get(), kScript, storage()->NewVersionId(), |
| context()->AsWeakPtr()); |
| version_1->set_fetch_handler_existence( |
| ServiceWorkerVersion::FetchHandlerExistence::EXISTS); |
| registration->SetActiveVersion(version_1); |
| version_1->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| scoped_refptr<ServiceWorkerVersion> version_2 = new ServiceWorkerVersion( |
| registration.get(), kScript, storage()->NewVersionId(), |
| context()->AsWeakPtr()); |
| version_2->set_fetch_handler_existence( |
| ServiceWorkerVersion::FetchHandlerExistence::EXISTS); |
| registration->SetWaitingVersion(version_2); |
| version_2->SetStatus(ServiceWorkerVersion::INSTALLED); |
| |
| // Navigation preload is disabled by default. |
| EXPECT_FALSE(version_1->navigation_preload_state().enabled); |
| // Enabling it sets the flag on the active version. |
| registration->EnableNavigationPreload(true); |
| EXPECT_TRUE(version_1->navigation_preload_state().enabled); |
| // A new active version gets the flag. |
| registration->SetActiveVersion(version_2); |
| version_2->SetStatus(ServiceWorkerVersion::ACTIVATING); |
| EXPECT_TRUE(version_2->navigation_preload_state().enabled); |
| // Disabling it unsets the flag on the active version. |
| registration->EnableNavigationPreload(false); |
| EXPECT_FALSE(version_2->navigation_preload_state().enabled); |
| } |
| |
| // Sets up a registration with a waiting worker, and an active worker |
| // with a controllee and an inflight request. |
| class ServiceWorkerActivationTest : public ServiceWorkerRegistrationTest { |
| public: |
| ServiceWorkerActivationTest() : ServiceWorkerRegistrationTest() {} |
| |
| void SetUp() override { |
| ServiceWorkerRegistrationTest::SetUp(); |
| |
| const GURL kScope("https://www.example.not/"); |
| const GURL kScript("https://www.example.not/service_worker.js"); |
| |
| registration_ = new ServiceWorkerRegistration( |
| ServiceWorkerRegistrationOptions(kScope), |
| storage()->NewRegistrationId(), context()->AsWeakPtr()); |
| |
| // Create an active version. |
| scoped_refptr<ServiceWorkerVersion> version_1 = new ServiceWorkerVersion( |
| registration_.get(), kScript, storage()->NewVersionId(), |
| context()->AsWeakPtr()); |
| version_1->set_fetch_handler_existence( |
| ServiceWorkerVersion::FetchHandlerExistence::EXISTS); |
| registration_->SetActiveVersion(version_1); |
| version_1->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| |
| // Store the registration. |
| std::vector<ServiceWorkerDatabase::ResourceRecord> records; |
| records.push_back(ServiceWorkerDatabase::ResourceRecord( |
| 10, version_1->script_url(), 100)); |
| version_1->script_cache_map()->SetResources(records); |
| version_1->SetMainScriptHttpResponseInfo( |
| EmbeddedWorkerTestHelper::CreateHttpResponseInfo()); |
| ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| context()->storage()->StoreRegistration( |
| registration_.get(), version_1.get(), |
| CreateReceiverOnCurrentThread(&status)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(SERVICE_WORKER_OK, status); |
| |
| // Give the active version a controllee. |
| host_ = CreateProviderHostForWindow( |
| helper_->mock_render_process_id(), 1 /* dummy provider_id */, |
| true /* is_parent_frame_secure */, context()->AsWeakPtr(), |
| &remote_endpoint_); |
| DCHECK(remote_endpoint_.client_request()->is_pending()); |
| DCHECK(remote_endpoint_.host_ptr()->is_bound()); |
| version_1->AddControllee(host_.get()); |
| |
| // Give the active version an in-flight request. |
| inflight_request_id_ = CreateInflightRequest(version_1.get()); |
| |
| // Create a waiting version. |
| scoped_refptr<ServiceWorkerVersion> version_2 = new ServiceWorkerVersion( |
| registration_.get(), kScript, storage()->NewVersionId(), |
| context()->AsWeakPtr()); |
| version_2->set_fetch_handler_existence( |
| ServiceWorkerVersion::FetchHandlerExistence::EXISTS); |
| registration_->SetWaitingVersion(version_2); |
| version_2->SetStatus(ServiceWorkerVersion::INSTALLED); |
| |
| // Set it to activate when ready. The original version should still be |
| // active. |
| registration_->ActivateWaitingVersionWhenReady(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), registration_->active_version()); |
| } |
| |
| void TearDown() override { |
| registration_->active_version()->RemoveListener(registration_.get()); |
| ServiceWorkerRegistrationTest::TearDown(); |
| } |
| |
| ServiceWorkerRegistration* registration() { return registration_.get(); } |
| ServiceWorkerProviderHost* controllee() { return host_.get(); } |
| int inflight_request_id() const { return inflight_request_id_; } |
| |
| bool IsLameDuckTimerRunning() { |
| return registration_->lame_duck_timer_.IsRunning(); |
| } |
| |
| void RunLameDuckTimer() { registration_->RemoveLameDuckIfNeeded(); } |
| |
| void SimulateSkipWaiting(ServiceWorkerVersion* version, int request_id) { |
| version->OnSkipWaiting(request_id); |
| } |
| |
| private: |
| scoped_refptr<ServiceWorkerRegistration> registration_; |
| std::unique_ptr<ServiceWorkerProviderHost> host_; |
| ServiceWorkerRemoteProviderEndpoint remote_endpoint_; |
| int inflight_request_id_ = -1; |
| }; |
| |
| // Test activation triggered by finishing all requests. |
| TEST_F(ServiceWorkerActivationTest, NoInflightRequest) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| |
| // Remove the controllee. Since there is an in-flight request, |
| // activation should not yet happen. |
| version_1->RemoveControllee(controllee()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| |
| // Finish the request. Activation should happen. |
| version_1->FinishRequest(inflight_request_id(), true /* was_handled */, |
| base::Time::Now()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| } |
| |
| // Test activation triggered by loss of controllee. |
| TEST_F(ServiceWorkerActivationTest, NoControllee) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| |
| // Finish the request. Since there is a controllee, activation should not yet |
| // happen. |
| version_1->FinishRequest(inflight_request_id(), true /* was_handled */, |
| base::Time::Now()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| |
| // Remove the controllee. Activation should happen. |
| version_1->RemoveControllee(controllee()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| } |
| |
| // Test activation triggered by skipWaiting. |
| TEST_F(ServiceWorkerActivationTest, SkipWaiting) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| |
| // Finish the in-flight request. Since there is a controllee, |
| // activation should not happen. |
| version_1->FinishRequest(inflight_request_id(), true /* was_handled */, |
| base::Time::Now()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| |
| // Call skipWaiting. Activation should happen. |
| SimulateSkipWaiting(version_2.get(), 77 /* dummy request_id */); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| } |
| |
| // Test activation triggered by skipWaiting and finishing requests. |
| TEST_F(ServiceWorkerActivationTest, SkipWaitingWithInflightRequest) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| |
| // Set skip waiting flag. Since there is still an in-flight request, |
| // activation should not happen. |
| SimulateSkipWaiting(version_2.get(), 77 /* dummy request_id */); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| |
| // Finish the request. Activation should happen. |
| version_1->FinishRequest(inflight_request_id(), true /* was_handled */, |
| base::Time::Now()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| } |
| |
| TEST_F(ServiceWorkerActivationTest, TimeSinceSkipWaiting_Installing) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version = reg->waiting_version(); |
| base::SimpleTestTickClock* clock = new base::SimpleTestTickClock(); |
| clock->SetNowTicks(base::TimeTicks::Now()); |
| version->SetTickClockForTesting(base::WrapUnique(clock)); |
| |
| // Reset version to the installing phase. |
| reg->UnsetVersion(version.get()); |
| version->SetStatus(ServiceWorkerVersion::INSTALLING); |
| |
| // Call skipWaiting(). The time ticks since skip waiting shouldn't start |
| // since the version is not yet installed. |
| SimulateSkipWaiting(version.get(), 77 /* dummy request_id */); |
| base::RunLoop().RunUntilIdle(); |
| clock->Advance(base::TimeDelta::FromSeconds(11)); |
| EXPECT_EQ(base::TimeDelta(), version->TimeSinceSkipWaiting()); |
| |
| // Install the version. Now the skip waiting time starts ticking. |
| version->SetStatus(ServiceWorkerVersion::INSTALLED); |
| reg->SetWaitingVersion(version); |
| base::RunLoop().RunUntilIdle(); |
| clock->Advance(base::TimeDelta::FromSeconds(33)); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(33), version->TimeSinceSkipWaiting()); |
| |
| // Call skipWaiting() again. It doesn't reset the time. |
| SimulateSkipWaiting(version.get(), 88 /* dummy request_id */); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(33), version->TimeSinceSkipWaiting()); |
| } |
| |
| // Test lame duck timer triggered by skip waiting. |
| TEST_F(ServiceWorkerActivationTest, LameDuckTime_SkipWaiting) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| base::SimpleTestTickClock* clock_1 = new base::SimpleTestTickClock(); |
| base::SimpleTestTickClock* clock_2 = new base::SimpleTestTickClock(); |
| clock_1->SetNowTicks(base::TimeTicks::Now()); |
| clock_2->SetNowTicks(clock_1->NowTicks()); |
| version_1->SetTickClockForTesting(base::WrapUnique(clock_1)); |
| version_2->SetTickClockForTesting(base::WrapUnique(clock_2)); |
| |
| // Set skip waiting flag. Since there is still an in-flight request, |
| // activation should not happen. But the lame duck timer should start. |
| EXPECT_FALSE(IsLameDuckTimerRunning()); |
| SimulateSkipWaiting(version_2.get(), 77 /* dummy request_id */); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| EXPECT_TRUE(IsLameDuckTimerRunning()); |
| |
| // Move forward by lame duck time. |
| clock_2->Advance(kMaxLameDuckTime + base::TimeDelta::FromSeconds(1)); |
| |
| // Activation should happen by the lame duck timer. |
| RunLameDuckTimer(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| EXPECT_FALSE(IsLameDuckTimerRunning()); |
| } |
| |
| // Test lame duck timer triggered by loss of controllee. |
| TEST_F(ServiceWorkerActivationTest, LameDuckTime_NoControllee) { |
| scoped_refptr<ServiceWorkerRegistration> reg = registration(); |
| scoped_refptr<ServiceWorkerVersion> version_1 = reg->active_version(); |
| scoped_refptr<ServiceWorkerVersion> version_2 = reg->waiting_version(); |
| base::SimpleTestTickClock* clock_1 = new base::SimpleTestTickClock(); |
| base::SimpleTestTickClock* clock_2 = new base::SimpleTestTickClock(); |
| clock_1->SetNowTicks(base::TimeTicks::Now()); |
| clock_2->SetNowTicks(clock_1->NowTicks()); |
| version_1->SetTickClockForTesting(base::WrapUnique(clock_1)); |
| version_2->SetTickClockForTesting(base::WrapUnique(clock_2)); |
| |
| // Remove the controllee. Since there is still an in-flight request, |
| // activation should not happen. But the lame duck timer should start. |
| EXPECT_FALSE(IsLameDuckTimerRunning()); |
| version_1->RemoveControllee(controllee()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| EXPECT_TRUE(IsLameDuckTimerRunning()); |
| |
| // Move clock forward by a little bit. |
| constexpr base::TimeDelta kLittleBit = base::TimeDelta::FromMinutes(1); |
| clock_1->Advance(kLittleBit); |
| |
| // Add a controllee again to reset the lame duck period. |
| version_1->AddControllee(controllee()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(IsLameDuckTimerRunning()); |
| |
| // Remove the controllee. |
| version_1->RemoveControllee(controllee()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(IsLameDuckTimerRunning()); |
| |
| // Move clock forward to the next lame duck timer tick. |
| clock_1->Advance(kMaxLameDuckTime - kLittleBit + |
| base::TimeDelta::FromSeconds(1)); |
| |
| // Run the lame duck timer. Activation should not yet happen |
| // since the lame duck period has not expired. |
| RunLameDuckTimer(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_1.get(), reg->active_version()); |
| EXPECT_TRUE(IsLameDuckTimerRunning()); |
| |
| // Continue on to the next lame duck timer tick. |
| clock_1->Advance(kMaxLameDuckTime + base::TimeDelta::FromSeconds(1)); |
| |
| // Activation should happen by the lame duck timer. |
| RunLameDuckTimer(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(version_2.get(), reg->active_version()); |
| EXPECT_FALSE(IsLameDuckTimerRunning()); |
| } |
| |
| } // namespace content |