| // Copyright 2017 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 "components/ukm/test_ukm_recorder.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| |
| #include "base/callback_helpers.h" |
| #include "third_party/blink/renderer/core/loader/interactive_detector.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/events/event.h" |
| #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| #include "third_party/blink/renderer/core/testing/scoped_mock_overlay_scrollbars.h" |
| #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" |
| #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h" |
| #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" |
| |
| namespace blink { |
| |
| using InputEvent = ukm::builders::InputEvent; |
| using PageLoad = ukm::builders::PageLoad; |
| |
| class NetworkActivityCheckerForTest |
| : public InteractiveDetector::NetworkActivityChecker { |
| public: |
| NetworkActivityCheckerForTest(Document* document) |
| : InteractiveDetector::NetworkActivityChecker(document) {} |
| |
| virtual void SetActiveConnections(int active_connections) { |
| active_connections_ = active_connections; |
| } |
| int GetActiveConnections() override; |
| |
| private: |
| int active_connections_ = 0; |
| }; |
| |
| int NetworkActivityCheckerForTest::GetActiveConnections() { |
| return active_connections_; |
| } |
| |
| class InteractiveDetectorTest : public testing::Test, |
| public ScopedMockOverlayScrollbars { |
| public: |
| InteractiveDetectorTest() { |
| platform_->AdvanceClockSeconds(1); |
| |
| auto test_task_runner = platform_->test_task_runner(); |
| auto* tick_clock = test_task_runner->GetMockTickClock(); |
| dummy_page_holder_ = std::make_unique<DummyPageHolder>( |
| IntSize(), nullptr, nullptr, base::NullCallback(), tick_clock); |
| |
| Document* document = &dummy_page_holder_->GetDocument(); |
| detector_ = MakeGarbageCollected<InteractiveDetector>( |
| *document, new NetworkActivityCheckerForTest(document)); |
| detector_->SetTaskRunnerForTesting(test_task_runner); |
| detector_->SetTickClockForTesting(tick_clock); |
| |
| // By this time, the DummyPageHolder has created an InteractiveDetector, and |
| // sent DOMContentLoadedEnd. We overwrite it with our new |
| // InteractiveDetector, which won't have received any timestamps. |
| Supplement<Document>::ProvideTo(*document, detector_.Get()); |
| |
| // Ensure the document is using the injected InteractiveDetector. |
| DCHECK_EQ(detector_, InteractiveDetector::From(*document)); |
| } |
| |
| // Public because it's executed on a task queue. |
| void DummyTaskWithDuration(double duration_seconds) { |
| platform_->AdvanceClockSeconds(duration_seconds); |
| dummy_task_end_time_ = Now(); |
| } |
| |
| protected: |
| InteractiveDetector* GetDetector() { return detector_; } |
| |
| base::TimeTicks GetDummyTaskEndTime() { return dummy_task_end_time_; } |
| |
| NetworkActivityCheckerForTest* GetNetworkActivityChecker() { |
| // We know in this test context that network_activity_checker_ is an |
| // instance of NetworkActivityCheckerForTest, so this static_cast is safe. |
| return static_cast<NetworkActivityCheckerForTest*>( |
| detector_->network_activity_checker_.get()); |
| } |
| |
| void SimulateNavigationStart(base::TimeTicks nav_start_time) { |
| RunTillTimestamp(nav_start_time); |
| detector_->SetNavigationStartTime(nav_start_time); |
| } |
| |
| void SimulateLongTask(base::TimeTicks start, base::TimeTicks end) { |
| CHECK(end - start >= base::TimeDelta::FromSecondsD(0.05)); |
| RunTillTimestamp(end); |
| detector_->OnLongTaskDetected(start, end); |
| } |
| |
| void SimulateDOMContentLoadedEnd(base::TimeTicks dcl_time) { |
| RunTillTimestamp(dcl_time); |
| detector_->OnDomContentLoadedEnd(dcl_time); |
| } |
| |
| void SimulateFCPDetected(base::TimeTicks fcp_time, |
| base::TimeTicks detection_time) { |
| RunTillTimestamp(detection_time); |
| detector_->OnFirstContentfulPaint(fcp_time); |
| } |
| |
| void SimulateInteractiveInvalidatingInput(base::TimeTicks timestamp) { |
| RunTillTimestamp(timestamp); |
| detector_->OnInvalidatingInputEvent(timestamp); |
| } |
| |
| void RunTillTimestamp(base::TimeTicks target_time) { |
| base::TimeTicks current_time = Now(); |
| platform_->RunForPeriod( |
| std::max(base::TimeDelta(), target_time - current_time)); |
| } |
| |
| int GetActiveConnections() { |
| return GetNetworkActivityChecker()->GetActiveConnections(); |
| } |
| |
| void SetActiveConnections(int active_connections) { |
| GetNetworkActivityChecker()->SetActiveConnections(active_connections); |
| } |
| |
| void SimulateResourceLoadBegin(base::TimeTicks load_begin_time) { |
| RunTillTimestamp(load_begin_time); |
| detector_->OnResourceLoadBegin(load_begin_time); |
| // ActiveConnections is incremented after detector runs OnResourceLoadBegin; |
| SetActiveConnections(GetActiveConnections() + 1); |
| } |
| |
| void SimulateResourceLoadEnd(base::TimeTicks load_finish_time) { |
| RunTillTimestamp(load_finish_time); |
| int active_connections = GetActiveConnections(); |
| SetActiveConnections(active_connections - 1); |
| detector_->OnResourceLoadEnd(load_finish_time); |
| } |
| |
| base::TimeTicks Now() { return platform_->test_task_runner()->NowTicks(); } |
| |
| base::TimeTicks GetInteractiveTime() { return detector_->interactive_time_; } |
| |
| void SetTimeToInteractive(base::TimeTicks interactive_time) { |
| detector_->interactive_time_ = interactive_time; |
| } |
| |
| base::TimeDelta GetTotalBlockingTime() { |
| return detector_->ComputeTotalBlockingTime(); |
| } |
| |
| ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> |
| platform_; |
| |
| private: |
| Persistent<InteractiveDetector> detector_; |
| std::unique_ptr<DummyPageHolder> dummy_page_holder_; |
| base::TimeTicks dummy_task_end_time_; |
| }; |
| |
| // Note: The tests currently assume kTimeToInteractiveWindowSeconds is 5 |
| // seconds. The window size is unlikely to change, and this makes the test |
| // scenarios significantly easier to write. |
| |
| // Note: Some of the tests are named W_X_Y_Z, where W, X, Y, Z can any of the |
| // following events: |
| // FCP: First Contentful Paint |
| // DCL: DomContentLoadedEnd |
| // FcpDetect: Detection of FCP. FCP is a presentation timestamp. |
| // LT: Long Task |
| // The name shows the ordering of these events in the test. |
| |
| TEST_F(InteractiveDetectorTest, FCP_DCL_FcpDetect) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(3)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(5), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(7)); |
| // Run until 5 seconds after FCP. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSeconds(5)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // Reached TTI at FCP. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(5)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, DCL_FCP_FcpDetect) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(5)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(7)); |
| // Run until 5 seconds after FCP. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSeconds(3)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // Reached TTI at DCL. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(5)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, InstantDetectionAtFcpDetectIfPossible) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(5)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(10)); |
| // Although we just detected FCP, the FCP timestamp is more than |
| // kTimeToInteractiveWindowSeconds earlier. We should instantaneously |
| // detect that we reached TTI at DCL. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(5)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, FcpDetectFiresAfterLateLongTask) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(3)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(9), |
| t0 + base::TimeDelta::FromSecondsD(9.1)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(10)); |
| // There is a 5 second quiet window after fcp_time - the long task is 6s |
| // seconds after fcp_time. We should instantly detect we reached TTI at FCP. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(3)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, FCP_FcpDetect_DCL) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(5)); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(9)); |
| // TTI reached at DCL. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(9)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, LongTaskBeforeFCPDoesNotAffectTTI) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(3)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSecondsD(5.1), |
| t0 + base::TimeDelta::FromSecondsD(5.2)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(8), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(9)); |
| // Run till 5 seconds after FCP. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSeconds(8)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at FCP. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(8)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, DCLDoesNotResetTimer) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(5), |
| t0 + base::TimeDelta::FromSecondsD(5.1)); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(8)); |
| // Run till 5 seconds after long task end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(5.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI Reached at DCL. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(8)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, DCL_FCP_FcpDetect_LT) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(3)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(4), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(5)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); |
| // Run till 5 seconds after long task end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(7.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at long task end. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(7.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, DCL_FCP_LT_FcpDetect) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(3)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(5)); |
| // Run till 5 seconds after long task end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(7.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at long task end. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(7.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, FCP_FcpDetect_LT_DCL) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(8)); |
| // Run till 5 seconds after long task end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(7.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at DCL. Note that we do not need to wait for DCL + 5 seconds. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(8)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, DclIsMoreThan5sAfterFCP) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(10)); |
| // Have not reached TTI yet. |
| EXPECT_EQ(GetInteractiveTime(), base::TimeTicks()); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(11), |
| t0 + base::TimeDelta::FromSecondsD(11.1)); // Long task 2. |
| // Run till long task 2 end + 5 seconds. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(11.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at long task 2 end. |
| EXPECT_EQ(GetInteractiveTime(), (t0 + base::TimeDelta::FromSecondsD(11.1))); |
| } |
| |
| TEST_F(InteractiveDetectorTest, NetworkBusyBlocksTTIEvenWhenMainThreadQuiet) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSecondsD(3.4)); // Request 2 start. |
| SimulateResourceLoadBegin(t0 + base::TimeDelta::FromSecondsD( |
| 3.5)); // Request 3 start. Network busy. |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| SimulateResourceLoadEnd( |
| t0 + base::TimeDelta::FromSecondsD(12.2)); // Network quiet. |
| // Network busy kept page from reaching TTI.. |
| EXPECT_EQ(GetInteractiveTime(), base::TimeTicks()); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(13), |
| t0 + base::TimeDelta::FromSecondsD(13.1)); // Long task 2. |
| // Run till 5 seconds after long task 2 end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(13.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| EXPECT_EQ(GetInteractiveTime(), (t0 + base::TimeDelta::FromSecondsD(13.1))); |
| } |
| |
| // FCP is a presentation timestamp, which is computed by another process and |
| // thus received asynchronously by the renderer process. Therefore, there can be |
| // some delay between the time in which FCP occurs and the time in which FCP is |
| // detected by the renderer. |
| TEST_F(InteractiveDetectorTest, LongEnoughQuietWindowBetweenFCPAndFcpDetect) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSecondsD(2.1), |
| t0 + base::TimeDelta::FromSecondsD(2.2)); // Long task 1. |
| SimulateLongTask(t0 + base::TimeDelta::FromSecondsD(8.2), |
| t0 + base::TimeDelta::FromSecondsD(8.3)); // Long task 2. |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSecondsD(8.4)); // Request 2 start. |
| SimulateResourceLoadBegin(t0 + base::TimeDelta::FromSecondsD( |
| 8.5)); // Request 3 start. Network busy. |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(10)); |
| // Even though network is currently busy and we have long task finishing |
| // recently, we should be able to detect that the page already achieved TTI at |
| // FCP. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSeconds(3)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, NetworkBusyEndIsNotTTI) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSecondsD(3.4)); // Request 2 start. |
| SimulateResourceLoadBegin(t0 + base::TimeDelta::FromSecondsD( |
| 3.5)); // Request 3 start. Network busy. |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(13), |
| t0 + base::TimeDelta::FromSecondsD(13.1)); // Long task 2. |
| SimulateResourceLoadEnd(t0 + |
| base::TimeDelta::FromSeconds(14)); // Network quiet. |
| // Run till 5 seconds after network busy end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSeconds(14)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at long task 2 end, NOT at network busy end. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(13.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, LateLongTaskWithLateFCPDetection) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSecondsD(3.4)); // Request 2 start. |
| SimulateResourceLoadBegin(t0 + base::TimeDelta::FromSecondsD( |
| 3.5)); // Request 3 start. Network busy. |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| SimulateResourceLoadEnd(t0 + |
| base::TimeDelta::FromSeconds(8)); // Network quiet. |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(14), |
| t0 + base::TimeDelta::FromSecondsD(14.1)); // Long task 2. |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(20)); |
| // TTI reached at long task 1 end, NOT at long task 2 end. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(7.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, IntermittentNetworkBusyBlocksTTI) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSecondsD(7.9)); // Active connections: 2 |
| // Network busy start. |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSeconds(8)); // Active connections: 3. |
| // Network busy end. |
| SimulateResourceLoadEnd( |
| t0 + base::TimeDelta::FromSecondsD(8.5)); // Active connections: 2. |
| // Network busy start. |
| SimulateResourceLoadBegin( |
| t0 + base::TimeDelta::FromSeconds(11)); // Active connections: 3. |
| // Network busy end. |
| SimulateResourceLoadEnd( |
| t0 + base::TimeDelta::FromSeconds(12)); // Active connections: 2. |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(14), |
| t0 + base::TimeDelta::FromSecondsD(14.1)); // Long task 2. |
| // Run till 5 seconds after long task 2 end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(14.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // TTI reached at long task 2 end. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(14.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, InvalidatingUserInput) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Network is forever quiet for this test. |
| SetActiveConnections(1); |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromSeconds(3), |
| /* detection_time */ t0 + base::TimeDelta::FromSeconds(4)); |
| SimulateInteractiveInvalidatingInput(t0 + base::TimeDelta::FromSeconds(5)); |
| SimulateLongTask(t0 + base::TimeDelta::FromSeconds(7), |
| t0 + base::TimeDelta::FromSecondsD(7.1)); // Long task 1. |
| // Run till 5 seconds after long task 2 end. |
| RunTillTimestamp((t0 + base::TimeDelta::FromSecondsD(7.1)) + |
| base::TimeDelta::FromSecondsD(5.0 + 0.1)); |
| // We still detect interactive time on the blink side even if there is an |
| // invalidating user input. Page Load Metrics filters out this value in the |
| // browser process for UMA reporting. |
| EXPECT_EQ(GetInteractiveTime(), t0 + base::TimeDelta::FromSecondsD(7.1)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, TaskLongerThan5sBlocksTTI) { |
| base::TimeTicks t0 = Now(); |
| GetDetector()->SetNavigationStartTime(t0); |
| |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateFCPDetected(t0 + base::TimeDelta::FromSeconds(3), |
| t0 + base::TimeDelta::FromSeconds(4)); |
| |
| // Post a task with 6 seconds duration. |
| Thread::Current()->GetTaskRunner()->PostTask( |
| FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, |
| WTF::Unretained(this), 6.0)); |
| |
| platform_->RunUntilIdle(); |
| |
| // We should be able to detect TTI 5s after the end of long task. |
| platform_->RunForPeriodSeconds(5.1); |
| EXPECT_EQ(GetInteractiveTime(), GetDummyTaskEndTime()); |
| } |
| |
| TEST_F(InteractiveDetectorTest, LongTaskAfterTTIDoesNothing) { |
| base::TimeTicks t0 = Now(); |
| GetDetector()->SetNavigationStartTime(t0); |
| |
| SimulateDOMContentLoadedEnd(t0 + base::TimeDelta::FromSeconds(2)); |
| SimulateFCPDetected(t0 + base::TimeDelta::FromSeconds(3), |
| t0 + base::TimeDelta::FromSeconds(4)); |
| |
| // Long task 1. |
| Thread::Current()->GetTaskRunner()->PostTask( |
| FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, |
| WTF::Unretained(this), 0.1)); |
| |
| platform_->RunUntilIdle(); |
| |
| base::TimeTicks long_task_1_end_time = GetDummyTaskEndTime(); |
| // We should be able to detect TTI 5s after the end of long task. |
| platform_->RunForPeriodSeconds(5.1); |
| EXPECT_EQ(GetInteractiveTime(), long_task_1_end_time); |
| |
| // Long task 2. |
| Thread::Current()->GetTaskRunner()->PostTask( |
| FROM_HERE, WTF::Bind(&InteractiveDetectorTest::DummyTaskWithDuration, |
| WTF::Unretained(this), 0.1)); |
| |
| platform_->RunUntilIdle(); |
| // Wait 5 seconds to see if TTI time changes. |
| platform_->RunForPeriodSeconds(5.1); |
| // TTI time should not change. |
| EXPECT_EQ(GetInteractiveTime(), long_task_1_end_time); |
| } |
| |
| // In tests for Total Blocking Time (TBT) we call SetTimeToInteractive() instead |
| // of allowing TimeToInteractive to occur because the computation is gated |
| // behind tracing being enabled, which means that they won't run by default. In |
| // addition, further complication stems from the fact that the vector of |
| // longtasks is cleared at the end of OnTimeToInteractiveDetected(). Therefore, |
| // the simplest solution is to manually set all of the relevant variables and |
| // check the correctness of the method ComputeTotalBlockingTime(). This can be |
| // revisited if we move TBT computations to occur outside of the trace event. |
| TEST_F(InteractiveDetectorTest, TotalBlockingTimeZero) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Set a high number of active connections, so that |
| // OnTimeToInteractiveDetected() is not called by accident. |
| SetActiveConnections(5); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromMilliseconds(100), |
| /* detection_time */ t0 + base::TimeDelta::FromMilliseconds(100)); |
| |
| // Longtask of duration 51ms, but only 50ms occur after FCP. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(99), |
| t0 + base::TimeDelta::FromMilliseconds(150)); |
| // Longtask of duration 59ms, but only 49ms occur before TTI. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(201), |
| t0 + base::TimeDelta::FromMilliseconds(260)); |
| SetTimeToInteractive(t0 + base::TimeDelta::FromMilliseconds(250)); |
| EXPECT_EQ(GetTotalBlockingTime(), base::TimeDelta()); |
| } |
| |
| TEST_F(InteractiveDetectorTest, TotalBlockingTimeNonZero) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Set a high number of active connections, so that |
| // OnTimeToInteractiveDetected() is not called by accident. |
| SetActiveConnections(5); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromMilliseconds(100), |
| /* detection_time */ t0 + base::TimeDelta::FromMilliseconds(100)); |
| |
| // Longtask fully before FCP. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(30), |
| t0 + base::TimeDelta::FromMilliseconds(89)); |
| // Longtask of duration 70ms, 60 ms of which occur after FCP. +10ms to TBT. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(90), |
| t0 + base::TimeDelta::FromMilliseconds(160)); |
| // Longtask of duration 80ms between FCP and TTI. +30ms to TBT. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(200), |
| t0 + base::TimeDelta::FromMilliseconds(280)); |
| // Longtask of duration 90ms, 70ms of which occur before TTI. +20ms to TBT. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(300), |
| t0 + base::TimeDelta::FromMilliseconds(390)); |
| // Longtask fully after TTI. |
| SimulateLongTask(t0 + base::TimeDelta::FromMilliseconds(371), |
| t0 + base::TimeDelta::FromMilliseconds(472)); |
| SetTimeToInteractive(t0 + base::TimeDelta::FromMilliseconds(370)); |
| EXPECT_EQ(GetTotalBlockingTime(), base::TimeDelta::FromMilliseconds(60)); |
| } |
| |
| TEST_F(InteractiveDetectorTest, TotalBlockingSingleTask) { |
| base::TimeTicks t0 = Now(); |
| SimulateNavigationStart(t0); |
| // Set a high number of active connections, so that |
| // OnTimeToInteractiveDetected() is not called by accident. |
| SetActiveConnections(5); |
| SimulateFCPDetected( |
| /* fcp_time */ t0 + base::TimeDelta::FromMilliseconds(100), |
| /* detection_time */ t0 + base::TimeDelta::FromMilliseconds(100)); |
| |
| // Longtask of duration 1s, from navigation start. |
| SimulateLongTask(t0, t0 + base::TimeDelta::FromSeconds(1)); |
| SetTimeToInteractive(t0 + base::TimeDelta::FromMilliseconds(500)); |
| // Truncated longtask is of length 400. So TBT is 400 - 50 = 350 |
| EXPECT_EQ(GetTotalBlockingTime(), base::TimeDelta::FromMilliseconds(350)); |
| } |
| |
| } // namespace blink |