1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/quic_network_blackhole_detector.h"
6 
7 #include "quiche/quic/core/quic_one_block_arena.h"
8 #include "quiche/quic/platform/api/quic_test.h"
9 #include "quiche/quic/test_tools/quic_test_utils.h"
10 
11 namespace quic {
12 namespace test {
13 
14 class QuicNetworkBlackholeDetectorPeer {
15  public:
GetAlarm(QuicNetworkBlackholeDetector * detector)16   static QuicAlarm* GetAlarm(QuicNetworkBlackholeDetector* detector) {
17     return detector->alarm_.get();
18   }
19 };
20 
21 namespace {
22 class MockDelegate : public QuicNetworkBlackholeDetector::Delegate {
23  public:
24   MOCK_METHOD(void, OnPathDegradingDetected, (), (override));
25   MOCK_METHOD(void, OnBlackholeDetected, (), (override));
26   MOCK_METHOD(void, OnPathMtuReductionDetected, (), (override));
27 };
28 
29 const size_t kPathDegradingDelayInSeconds = 5;
30 const size_t kPathMtuReductionDelayInSeconds = 7;
31 const size_t kBlackholeDelayInSeconds = 10;
32 
33 class QuicNetworkBlackholeDetectorTest : public QuicTest {
34  public:
QuicNetworkBlackholeDetectorTest()35   QuicNetworkBlackholeDetectorTest()
36       : detector_(&delegate_, &arena_, &alarm_factory_, /*context=*/nullptr),
37         alarm_(static_cast<MockAlarmFactory::TestAlarm*>(
38             QuicNetworkBlackholeDetectorPeer::GetAlarm(&detector_))),
39         path_degrading_delay_(
40             QuicTime::Delta::FromSeconds(kPathDegradingDelayInSeconds)),
41         path_mtu_reduction_delay_(
42             QuicTime::Delta::FromSeconds(kPathMtuReductionDelayInSeconds)),
43         blackhole_delay_(
44             QuicTime::Delta::FromSeconds(kBlackholeDelayInSeconds)) {
45     clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
46   }
47 
48  protected:
RestartDetection()49   void RestartDetection() {
50     detector_.RestartDetection(clock_.Now() + path_degrading_delay_,
51                                clock_.Now() + blackhole_delay_,
52                                clock_.Now() + path_mtu_reduction_delay_);
53   }
54 
55   testing::StrictMock<MockDelegate> delegate_;
56   QuicConnectionArena arena_;
57   MockAlarmFactory alarm_factory_;
58 
59   QuicNetworkBlackholeDetector detector_;
60 
61   MockAlarmFactory::TestAlarm* alarm_;
62   MockClock clock_;
63   const QuicTime::Delta path_degrading_delay_;
64   const QuicTime::Delta path_mtu_reduction_delay_;
65   const QuicTime::Delta blackhole_delay_;
66 };
67 
TEST_F(QuicNetworkBlackholeDetectorTest,StartAndFire)68 TEST_F(QuicNetworkBlackholeDetectorTest, StartAndFire) {
69   EXPECT_FALSE(detector_.IsDetectionInProgress());
70 
71   RestartDetection();
72   EXPECT_TRUE(detector_.IsDetectionInProgress());
73   EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline());
74 
75   // Fire path degrading alarm.
76   clock_.AdvanceTime(path_degrading_delay_);
77   EXPECT_CALL(delegate_, OnPathDegradingDetected());
78   alarm_->Fire();
79 
80   // Verify path mtu reduction detection is still in progress.
81   EXPECT_TRUE(detector_.IsDetectionInProgress());
82   EXPECT_EQ(clock_.Now() + path_mtu_reduction_delay_ - path_degrading_delay_,
83             alarm_->deadline());
84 
85   // Fire path mtu reduction detection alarm.
86   clock_.AdvanceTime(path_mtu_reduction_delay_ - path_degrading_delay_);
87   EXPECT_CALL(delegate_, OnPathMtuReductionDetected());
88   alarm_->Fire();
89 
90   // Verify blackhole detection is still in progress.
91   EXPECT_TRUE(detector_.IsDetectionInProgress());
92   EXPECT_EQ(clock_.Now() + blackhole_delay_ - path_mtu_reduction_delay_,
93             alarm_->deadline());
94 
95   // Fire blackhole detection alarm.
96   clock_.AdvanceTime(blackhole_delay_ - path_mtu_reduction_delay_);
97   EXPECT_CALL(delegate_, OnBlackholeDetected());
98   alarm_->Fire();
99   EXPECT_FALSE(detector_.IsDetectionInProgress());
100 }
101 
TEST_F(QuicNetworkBlackholeDetectorTest,RestartAndStop)102 TEST_F(QuicNetworkBlackholeDetectorTest, RestartAndStop) {
103   RestartDetection();
104 
105   clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
106   RestartDetection();
107   EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline());
108 
109   detector_.StopDetection(/*permanent=*/false);
110   EXPECT_FALSE(detector_.IsDetectionInProgress());
111 }
112 
TEST_F(QuicNetworkBlackholeDetectorTest,PathDegradingFiresAndRestart)113 TEST_F(QuicNetworkBlackholeDetectorTest, PathDegradingFiresAndRestart) {
114   EXPECT_FALSE(detector_.IsDetectionInProgress());
115   RestartDetection();
116   EXPECT_TRUE(detector_.IsDetectionInProgress());
117   EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline());
118 
119   // Fire path degrading alarm.
120   clock_.AdvanceTime(path_degrading_delay_);
121   EXPECT_CALL(delegate_, OnPathDegradingDetected());
122   alarm_->Fire();
123 
124   // Verify path mtu reduction detection is still in progress.
125   EXPECT_TRUE(detector_.IsDetectionInProgress());
126   EXPECT_EQ(clock_.Now() + path_mtu_reduction_delay_ - path_degrading_delay_,
127             alarm_->deadline());
128 
129   // After 100ms, restart detections on forward progress.
130   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
131   RestartDetection();
132   // Verify alarm is armed based on path degrading deadline.
133   EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline());
134 }
135 
136 }  // namespace
137 
138 }  // namespace test
139 }  // namespace quic
140