| // Copyright 2018 The Chromium OS 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 "shill/icmp_session.h" |
| |
| #include <base/test/simple_test_tick_clock.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/mock_event_dispatcher.h" |
| #include "shill/mock_icmp.h" |
| #include "shill/net/ip_address.h" |
| #include "shill/net/mock_io_handler_factory.h" |
| |
| using base::Bind; |
| using base::Unretained; |
| using testing::_; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::StrictMock; |
| using testing::Test; |
| |
| namespace shill { |
| |
| namespace { |
| |
| // Note: this IPv4 header is given in network byte order, since |
| // IcmpSession::OnEchoReplyReceived expects to receive a raw IP packet. |
| const uint8_t kIpHeader[] = {0x45, 0x80, 0x00, 0x1c, 0x63, 0xd3, 0x00, |
| 0x00, 0x39, 0x01, 0xcc, 0x9f, 0x4a, 0x7d, |
| 0xe0, 0x18, 0x64, 0x6e, 0xc1, 0xea}; |
| // ICMPv4 echo replies with 0 bytes of data and and echo ID 0. Sequence numbers |
| // are 0x8, 0x9, and 0xa respectively to simulate replies to a sequence of sent |
| // echo requests. Note that these only match on little-endian hosts. |
| const uint8_t kIcmpEchoReply1[] = {0x00, 0x00, 0xf7, 0xff, |
| 0x00, 0x00, 0x08, 0x00}; |
| const uint16_t kIcmpEchoReply1_SeqNum = 0x08; |
| const uint8_t kIcmpEchoReply2[] = {0x00, 0x00, 0xf6, 0xff, |
| 0x00, 0x00, 0x09, 0x00}; |
| const uint16_t kIcmpEchoReply2_SeqNum = 0x09; |
| const uint8_t kIcmpEchoReply3[] = {0x00, 0x00, 0xf5, 0xff, |
| 0x00, 0x00, 0x0a, 0x00}; |
| const uint16_t kIcmpEchoReply3_SeqNum = 0x0a; |
| |
| // ICMPv6 echo reply with 0 bytes of data, echo ID 0, and sequence number 0x8. |
| const uint8_t kIcmpV6EchoReply1[] = {0x81, 0x00, 0x76, 0xff, |
| 0x00, 0x00, 0x08, 0x00}; |
| |
| // This ICMPv4 echo reply has an echo ID of 0xe, which is different from the |
| // echo ID used in the unit tests (0). |
| const uint8_t kIcmpEchoReplyDifferentEchoID[] = {0x00, 0x00, 0xea, 0xff, |
| 0x0e, 0x00, 0x0b, 0x00}; |
| |
| } // namespace |
| |
| MATCHER_P(IsIPAddress, address, "") { |
| // IPAddress objects don't support the "==" operator as per style, so we need |
| // a custom matcher. |
| return address.Equals(arg); |
| } |
| |
| class IcmpSessionTest : public Test { |
| public: |
| IcmpSessionTest() : icmp_session_(&dispatcher_) {} |
| ~IcmpSessionTest() override = default; |
| |
| void SetUp() override { |
| icmp_session_.io_handler_factory_ = &io_handler_factory_; |
| icmp_session_.tick_clock_ = &testing_clock_; |
| icmp_ = new NiceMock<MockIcmp>(); |
| // Passes ownership. |
| icmp_session_.icmp_.reset(icmp_); |
| ON_CALL(*icmp_, IsStarted()).WillByDefault(Return(false)); |
| } |
| |
| void TearDown() override { |
| EXPECT_CALL(*icmp_, IsStarted()); |
| IcmpSession::kNextUniqueEchoId = 0; |
| } |
| |
| MOCK_METHOD(void, ResultCallback, (const IcmpSession::IcmpSessionResult&)); |
| |
| protected: |
| static const char kIPAddress[]; |
| static const char kIP6Address[]; |
| static const int kInterfaceIndex; |
| |
| void StartAndVerify(const IPAddress& destination, int interface_index) { |
| EXPECT_CALL(*icmp_, IsStarted()); |
| EXPECT_CALL(*icmp_, Start(IsIPAddress(destination), interface_index)) |
| .WillOnce(Return(true)); |
| icmp_->destination_ = destination; |
| EXPECT_CALL(io_handler_factory_, |
| CreateIOInputHandler(icmp_->socket(), _, _)); |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, GetTimeoutSeconds() * 1000)); |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, 0)); |
| EXPECT_TRUE(Start(destination, interface_index)); |
| EXPECT_TRUE(GetSeqNumToSentRecvTime()->empty()); |
| EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty()); |
| EXPECT_CALL(*icmp_, IsStarted()).WillRepeatedly(Return(true)); |
| } |
| |
| bool Start(const IPAddress& destination, int interface_index) { |
| return icmp_session_.Start( |
| destination, interface_index, |
| Bind(&IcmpSessionTest::ResultCallback, Unretained(this))); |
| } |
| |
| void Stop() { icmp_session_.Stop(); } |
| |
| bool SeqNumToSentRecvTimeContains(uint16_t seq_num) { |
| return icmp_session_.seq_num_to_sent_recv_time_.find(seq_num) != |
| icmp_session_.seq_num_to_sent_recv_time_.end(); |
| } |
| |
| bool ReceivedEchoReplySeqNumbersContains(uint16_t seq_num) { |
| return icmp_session_.received_echo_reply_seq_numbers_.find(seq_num) != |
| icmp_session_.received_echo_reply_seq_numbers_.end(); |
| } |
| |
| void TransmitEchoRequestTask(bool transmit_request_success) { |
| EXPECT_CALL(*icmp_, TransmitEchoRequest(icmp_session_.echo_id_, |
| GetCurrentSequenceNumber())) |
| .WillOnce(Return(transmit_request_success)); |
| icmp_session_.TransmitEchoRequestTask(); |
| } |
| |
| void ReportResultAndStopSession() { |
| icmp_session_.ReportResultAndStopSession(); |
| } |
| |
| void VerifyIcmpSessionStopped() { |
| EXPECT_TRUE(icmp_session_.timeout_callback_.IsCancelled()); |
| EXPECT_FALSE(icmp_session_.echo_reply_handler_); |
| } |
| |
| void OnEchoReplyReceived(InputData* data) { |
| icmp_session_.OnEchoReplyReceived(data); |
| } |
| |
| IcmpSession::IcmpSessionResult GenerateIcmpResult() { |
| return icmp_session_.GenerateIcmpResult(); |
| } |
| |
| std::map<uint16_t, IcmpSession::SentRecvTimePair>* GetSeqNumToSentRecvTime() { |
| return &icmp_session_.seq_num_to_sent_recv_time_; |
| } |
| std::set<uint16_t>* GetReceivedEchoReplySeqNumbers() { |
| return &icmp_session_.received_echo_reply_seq_numbers_; |
| } |
| uint16_t GetNextUniqueEchoId() const { |
| return IcmpSession::kNextUniqueEchoId; |
| } |
| int GetTotalNumEchoRequests() const { |
| return IcmpSession::kTotalNumEchoRequests; |
| } |
| int GetCurrentSequenceNumber() const { |
| return icmp_session_.current_sequence_number_; |
| } |
| void SetCurrentSequenceNumber(uint16_t val) { |
| icmp_session_.current_sequence_number_ = val; |
| } |
| size_t GetTimeoutSeconds() const { return IcmpSession::kTimeoutSeconds; } |
| int GetEchoRequestIntervalSeconds() const { |
| return IcmpSession::kEchoRequestIntervalSeconds; |
| } |
| |
| MockIcmp* icmp_; |
| MockIOHandlerFactory io_handler_factory_; |
| StrictMock<MockEventDispatcher> dispatcher_; |
| IcmpSession icmp_session_; |
| base::SimpleTestTickClock testing_clock_; |
| }; |
| |
| const char IcmpSessionTest::kIPAddress[] = "10.0.1.1"; |
| const char IcmpSessionTest::kIP6Address[] = "2001:db8::1234:5678"; |
| const int IcmpSessionTest::kInterfaceIndex = 3; |
| |
| TEST_F(IcmpSessionTest, Constructor) { |
| // |icmp_session_| should have been assigned the value of |kNextUniqueEchoId| |
| // on construction, and caused the value of this static variable to be |
| // incremented. |
| uint16_t saved_echo_id = GetNextUniqueEchoId(); |
| EXPECT_EQ(saved_echo_id - 1, icmp_session_.echo_id_); |
| |
| // The next IcmpSession object constructed, |session| should get the next |
| // unique value of |kNextUniqueEchoId|, and further increment this variable. |
| IcmpSession session(&dispatcher_); |
| EXPECT_EQ(saved_echo_id, session.echo_id_); |
| EXPECT_EQ(saved_echo_id + 1, GetNextUniqueEchoId()); |
| } |
| |
| TEST_F(IcmpSessionTest, StartWhileAlreadyStarted) { |
| IPAddress ipv4_destination(IPAddress::kFamilyIPv4); |
| EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress)); |
| StartAndVerify(ipv4_destination, kInterfaceIndex); |
| |
| // Since an ICMP session is already started, we should fail to start it again. |
| EXPECT_CALL(*icmp_, Start(IsIPAddress(ipv4_destination), kInterfaceIndex)) |
| .Times(0); |
| EXPECT_CALL(io_handler_factory_, CreateIOInputHandler(_, _, _)).Times(0); |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, _)).Times(0); |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, 0)).Times(0); |
| EXPECT_FALSE(Start(ipv4_destination, kInterfaceIndex)); |
| } |
| |
| TEST_F(IcmpSessionTest, StopWhileNotStarted) { |
| // Attempting to stop the ICMP session while it is not started should do |
| // nothing. |
| EXPECT_CALL(*icmp_, IsStarted()).WillOnce(Return(false)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| EXPECT_CALL(*icmp_, Stop()).Times(0); |
| Stop(); |
| } |
| |
| TEST_F(IcmpSessionTest, SessionSuccess) { |
| // Test a successful ICMP session where the sending of requests and receiving |
| // of replies are interleaved. Moreover, test the case where transmitting an |
| // echo request fails. |
| |
| base::TimeTicks now = testing_clock_.NowTicks(); |
| base::TimeTicks kSentTime1 = base::TimeTicks::FromInternalValue(10); |
| base::TimeTicks kRecvTime1 = base::TimeTicks::FromInternalValue(20); |
| base::TimeTicks kSentTime2 = base::TimeTicks::FromInternalValue(30); |
| base::TimeTicks kSentTime3 = base::TimeTicks::FromInternalValue(40); |
| base::TimeTicks kRecvTime2 = base::TimeTicks::FromInternalValue(50); |
| base::TimeTicks kWrongEchoIDRecvTime = base::TimeTicks::FromInternalValue(60); |
| base::TimeTicks kRecvTime3 = base::TimeTicks::FromInternalValue(70); |
| |
| IcmpSession::IcmpSessionResult expected_result = { |
| kRecvTime1 - kSentTime1, |
| kRecvTime2 - kSentTime2, |
| kRecvTime3 - kSentTime3, |
| }; |
| |
| // Initiate session. |
| IPAddress ipv4_destination(IPAddress::kFamilyIPv4); |
| EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress)); |
| StartAndVerify(ipv4_destination, kInterfaceIndex); |
| |
| // Send the first echo request. |
| testing_clock_.Advance(kSentTime1 - now); |
| now = testing_clock_.NowTicks(); |
| SetCurrentSequenceNumber(kIcmpEchoReply1_SeqNum); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(true); |
| EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty()); |
| EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply1_SeqNum)); |
| EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply1_SeqNum).first); |
| EXPECT_EQ(kIcmpEchoReply2_SeqNum, GetCurrentSequenceNumber()); |
| |
| // Receive first reply. |
| testing_clock_.Advance(kRecvTime1 - now); |
| now = testing_clock_.NowTicks(); |
| uint8_t buffer_1[sizeof(kIpHeader) + sizeof(kIcmpEchoReply1)]; |
| memcpy(buffer_1, kIpHeader, sizeof(kIpHeader)); |
| memcpy(buffer_1 + sizeof(kIpHeader), kIcmpEchoReply1, |
| sizeof(kIcmpEchoReply1)); |
| InputData data_1(reinterpret_cast<unsigned char*>(buffer_1), |
| sizeof(buffer_1)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| OnEchoReplyReceived(&data_1); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply1_SeqNum)); |
| |
| // Send the second echo request. |
| testing_clock_.Advance(kSentTime2 - now); |
| now = testing_clock_.NowTicks(); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(true); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply2_SeqNum)); |
| EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply2_SeqNum).first); |
| EXPECT_EQ(kIcmpEchoReply3_SeqNum, GetCurrentSequenceNumber()); |
| |
| // Sending final request. |
| testing_clock_.Advance(kSentTime3 - now); |
| now = testing_clock_.NowTicks(); |
| EXPECT_CALL(dispatcher_, PostDelayedTask(_, _, _)).Times(0); |
| EXPECT_CALL(*icmp_, Stop()).Times(0); |
| TransmitEchoRequestTask(true); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply3_SeqNum)); |
| EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply3_SeqNum).first); |
| EXPECT_EQ(kIcmpEchoReply3_SeqNum + 1, GetCurrentSequenceNumber()); |
| |
| // Receive second reply. |
| testing_clock_.Advance(kRecvTime2 - now); |
| now = testing_clock_.NowTicks(); |
| uint8_t buffer_2[sizeof(kIpHeader) + sizeof(kIcmpEchoReply2)]; |
| memcpy(buffer_2, kIpHeader, sizeof(kIpHeader)); |
| memcpy(buffer_2 + sizeof(kIpHeader), kIcmpEchoReply2, |
| sizeof(kIcmpEchoReply2)); |
| InputData data_2(reinterpret_cast<unsigned char*>(buffer_2), |
| sizeof(buffer_2)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| EXPECT_CALL(*icmp_, Stop()).Times(0); |
| OnEchoReplyReceived(&data_2); |
| EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_EQ(2, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply2_SeqNum)); |
| |
| // Receive a reply that has an echo ID that does not match that of this |
| // ICMP session. This reply will not be processed. |
| testing_clock_.Advance(kWrongEchoIDRecvTime - now); |
| now = testing_clock_.NowTicks(); |
| uint8_t buffer_3[sizeof(kIpHeader) + sizeof(kIcmpEchoReplyDifferentEchoID)]; |
| memcpy(buffer_3, kIpHeader, sizeof(kIpHeader)); |
| memcpy(buffer_3 + sizeof(kIpHeader), kIcmpEchoReplyDifferentEchoID, |
| sizeof(kIcmpEchoReplyDifferentEchoID)); |
| InputData data_3(reinterpret_cast<unsigned char*>(buffer_3), |
| sizeof(buffer_3)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| EXPECT_CALL(*icmp_, Stop()).Times(0); |
| OnEchoReplyReceived(&data_3); |
| EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_EQ(2, GetReceivedEchoReplySeqNumbers()->size()); |
| |
| // Receive third reply, which concludes the ICMP session. |
| testing_clock_.Advance(kRecvTime3 - now); |
| now = testing_clock_.NowTicks(); |
| uint8_t buffer_4[sizeof(kIpHeader) + sizeof(kIcmpEchoReply3)]; |
| memcpy(buffer_4, kIpHeader, sizeof(kIpHeader)); |
| memcpy(buffer_4 + sizeof(kIpHeader), kIcmpEchoReply3, |
| sizeof(kIcmpEchoReply3)); |
| InputData data_4(reinterpret_cast<unsigned char*>(buffer_4), |
| sizeof(buffer_4)); |
| EXPECT_CALL(*this, ResultCallback(expected_result)); |
| EXPECT_CALL(*icmp_, Stop()); |
| OnEchoReplyReceived(&data_4); |
| EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_EQ(3, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply3_SeqNum)); |
| |
| VerifyIcmpSessionStopped(); |
| } |
| |
| TEST_F(IcmpSessionTest, ICMPv6) { |
| // Initiate session. |
| IPAddress ipv6_destination(IPAddress::kFamilyIPv6); |
| EXPECT_TRUE(ipv6_destination.SetAddressFromString(kIP6Address)); |
| StartAndVerify(ipv6_destination, kInterfaceIndex); |
| |
| // Send an echo request. |
| SetCurrentSequenceNumber(kIcmpEchoReply1_SeqNum); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(true); |
| EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty()); |
| EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply1_SeqNum)); |
| EXPECT_EQ(kIcmpEchoReply2_SeqNum, GetCurrentSequenceNumber()); |
| |
| // Receive a reply. |
| uint8_t buffer_1[sizeof(kIcmpV6EchoReply1)]; |
| memcpy(buffer_1, kIcmpV6EchoReply1, sizeof(kIcmpV6EchoReply1)); |
| InputData data_1(reinterpret_cast<unsigned char*>(buffer_1), |
| sizeof(buffer_1)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| OnEchoReplyReceived(&data_1); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply1_SeqNum)); |
| |
| EXPECT_CALL(*icmp_, Stop()); |
| Stop(); |
| VerifyIcmpSessionStopped(); |
| } |
| |
| TEST_F(IcmpSessionTest, SessionTimeoutOrInterrupted) { |
| // Test a failed ICMP session where we neither send out all echo requests nor |
| // receive all echo replies before stopping the ICMP session (because of a |
| // timeout or a manually-triggered stop). Moreover, test that echo requests |
| // that are sent unsuccessfully are sent again. |
| |
| base::TimeTicks now = testing_clock_.NowTicks(); |
| base::TimeTicks kSentTime1 = base::TimeTicks::FromInternalValue(10); |
| base::TimeTicks kSentTime2 = base::TimeTicks::FromInternalValue(20); |
| base::TimeTicks kRecvTime1 = base::TimeTicks::FromInternalValue(30); |
| base::TimeTicks kResendTime1 = base::TimeTicks::FromInternalValue(40); |
| |
| IcmpSession::IcmpSessionResult expected_partial_result = { |
| kRecvTime1 - kSentTime1, |
| base::TimeDelta(), |
| }; |
| |
| // Initiate session. |
| IPAddress ipv4_destination(IPAddress::kFamilyIPv4); |
| EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress)); |
| StartAndVerify(ipv4_destination, kInterfaceIndex); |
| |
| // Send the first echo request successfully. |
| testing_clock_.Advance(kSentTime1 - now); |
| now = testing_clock_.NowTicks(); |
| SetCurrentSequenceNumber(kIcmpEchoReply1_SeqNum); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(true); |
| EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty()); |
| EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply1_SeqNum)); |
| EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply1_SeqNum).first); |
| EXPECT_EQ(kIcmpEchoReply2_SeqNum, GetCurrentSequenceNumber()); |
| |
| // Send the second echo request unsuccessfully. |
| testing_clock_.Advance(kSentTime2 - now); |
| now = testing_clock_.NowTicks(); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(false); |
| EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty()); |
| EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_FALSE(SeqNumToSentRecvTimeContains(kIcmpEchoReply2_SeqNum)); |
| // The sequence number should still be incremented when we fail to transmit an |
| // echo request. |
| EXPECT_EQ(kIcmpEchoReply3_SeqNum, GetCurrentSequenceNumber()); |
| |
| // Receive first reply. |
| testing_clock_.Advance(kRecvTime1 - now); |
| now = testing_clock_.NowTicks(); |
| uint8_t buffer_1[sizeof(kIpHeader) + sizeof(kIcmpEchoReply1)]; |
| memcpy(buffer_1, kIpHeader, sizeof(kIpHeader)); |
| memcpy(buffer_1 + sizeof(kIpHeader), kIcmpEchoReply1, |
| sizeof(kIcmpEchoReply1)); |
| InputData data_1(reinterpret_cast<unsigned char*>(buffer_1), |
| sizeof(buffer_1)); |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| OnEchoReplyReceived(&data_1); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply1_SeqNum)); |
| |
| // Resend second echo request successfully. |
| testing_clock_.Advance(kResendTime1 - now); |
| now = testing_clock_.NowTicks(); |
| EXPECT_CALL(dispatcher_, |
| PostDelayedTask(_, _, GetEchoRequestIntervalSeconds() * 1000)); |
| TransmitEchoRequestTask(true); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply3_SeqNum)); |
| EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply3_SeqNum).first); |
| EXPECT_EQ(kIcmpEchoReply3_SeqNum + 1, GetCurrentSequenceNumber()); |
| |
| // Timeout triggered, so report partial results. |
| EXPECT_CALL(*this, ResultCallback(expected_partial_result)); |
| EXPECT_CALL(*icmp_, Stop()); |
| ReportResultAndStopSession(); |
| EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size()); |
| EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size()); |
| VerifyIcmpSessionStopped(); |
| } |
| |
| TEST_F(IcmpSessionTest, DoNotReportResultsOnStop) { |
| // Initiate session. |
| IPAddress ipv4_destination(IPAddress::kFamilyIPv4); |
| EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress)); |
| StartAndVerify(ipv4_destination, kInterfaceIndex); |
| |
| // Session interrupted manually by calling Stop(), so do not report results. |
| EXPECT_CALL(*this, ResultCallback(_)).Times(0); |
| EXPECT_CALL(*icmp_, Stop()); |
| Stop(); |
| VerifyIcmpSessionStopped(); |
| } |
| |
| TEST_F(IcmpSessionTest, AnyRepliesReceived) { |
| IcmpSession::IcmpSessionResult none_sent; |
| EXPECT_FALSE(IcmpSession::AnyRepliesReceived(none_sent)); |
| |
| IcmpSession::IcmpSessionResult two_sent_none_received = { |
| base::TimeDelta(), |
| base::TimeDelta(), |
| }; |
| EXPECT_FALSE(IcmpSession::AnyRepliesReceived(two_sent_none_received)); |
| |
| IcmpSession::IcmpSessionResult one_sent_one_received = { |
| base::TimeDelta::FromSeconds(10), |
| }; |
| EXPECT_TRUE(IcmpSession::AnyRepliesReceived(one_sent_one_received)); |
| |
| IcmpSession::IcmpSessionResult two_sent_one_received = { |
| base::TimeDelta::FromSeconds(20), |
| base::TimeDelta(), |
| }; |
| EXPECT_TRUE(IcmpSession::AnyRepliesReceived(two_sent_one_received)); |
| } |
| |
| TEST_F(IcmpSessionTest, IsPacketLossPercentageGreaterThan) { |
| // If we sent no echo requests out, we expect no replies, therefore we have |
| // 0% packet loss. |
| IcmpSession::IcmpSessionResult none_sent_none_received; |
| EXPECT_FALSE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| none_sent_none_received, 0)); |
| |
| // If we receive all replies, we experience 0% packet loss. |
| IcmpSession::IcmpSessionResult three_sent_three_received = { |
| base::TimeDelta::FromSeconds(10), |
| base::TimeDelta::FromSeconds(10), |
| base::TimeDelta::FromSeconds(10), |
| }; |
| EXPECT_FALSE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| three_sent_three_received, 0)); |
| |
| // If we sent 3 requests and received 2 replies, we have ~33% packet loss. |
| IcmpSession::IcmpSessionResult three_sent_two_received = { |
| base::TimeDelta::FromSeconds(10), |
| base::TimeDelta::FromSeconds(10), |
| base::TimeDelta(), |
| }; |
| EXPECT_FALSE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| three_sent_two_received, 60)); |
| EXPECT_FALSE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| three_sent_two_received, 33)); |
| EXPECT_TRUE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| three_sent_two_received, 32)); |
| EXPECT_TRUE(IcmpSession::IsPacketLossPercentageGreaterThan( |
| three_sent_two_received, 10)); |
| } |
| |
| } // namespace shill |