1//
2// Copyright (C) 2013 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "shill/cellular/active_passive_out_of_credits_detector.h"
18
19#include <string>
20#include <vector>
21
22#include <gtest/gtest.h>
23
24#include "shill/cellular/mock_cellular.h"
25#include "shill/cellular/mock_cellular_service.h"
26#include "shill/cellular/mock_modem_info.h"
27#include "shill/mock_connection.h"
28#include "shill/mock_connection_health_checker.h"
29#include "shill/mock_device_info.h"
30#include "shill/mock_manager.h"
31#include "shill/mock_traffic_monitor.h"
32#include "shill/test_event_dispatcher.h"
33
34using base::Bind;
35using base::Unretained;
36using std::string;
37using std::vector;
38using testing::_;
39using testing::AnyNumber;
40using testing::Mock;
41using testing::NiceMock;
42using testing::Return;
43using testing::ReturnPointee;
44using testing::ReturnRef;
45using testing::StrictMock;
46
47namespace shill {
48
49class ActivePassiveOutOfCreditsDetectorTest : public testing::Test {
50 public:
51  ActivePassiveOutOfCreditsDetectorTest()
52      : modem_info_(nullptr, &dispatcher_, &metrics_, &manager_),
53        device_info_(modem_info_.control_interface(), modem_info_.dispatcher(),
54                     modem_info_.metrics(), modem_info_.manager()),
55        manager_(modem_info_.control_interface(), modem_info_.dispatcher(),
56                 modem_info_.metrics()),
57        metrics_(modem_info_.dispatcher()),
58        cellular_(new NiceMock<MockCellular>(&modem_info_,
59                                             "usb0",
60                                             kAddress,
61                                             3,
62                                             Cellular::kTypeCDMA,
63                                             "",
64                                             "")),
65        service_(new NiceMock<MockCellularService>(&modem_info_, cellular_)),
66        connection_(new NiceMock<MockConnection>(&device_info_)),
67        out_of_credits_detector_(
68            new ActivePassiveOutOfCreditsDetector(
69                modem_info_.dispatcher(), modem_info_.manager(),
70                modem_info_.metrics(), service_.get())) {}
71
72  virtual void SetUp() {
73    service_->connection_ = connection_;
74    cellular_->service_ = service_;
75    service_->SetRoamingState(kRoamingStateHome);
76    ON_CALL(*connection_, interface_name())
77        .WillByDefault(ReturnRef(interface_name_));
78    ON_CALL(*connection_, dns_servers())
79        .WillByDefault(ReturnRef(dns_servers_));
80    ON_CALL(manager_, GetPortalCheckURL())
81        .WillByDefault(ReturnRef(portal_check_url_));
82    ON_CALL(*service_, explicitly_disconnected()).WillByDefault(Return(false));
83    ON_CALL(*service_, resume_start_time())
84        .WillByDefault(ReturnRef(resume_start_time_));
85  }
86
87  virtual void TearDown() {
88    cellular_->service_ = nullptr;  // Break circular reference.
89  }
90
91  void OnConnectionHealthCheckerResult(
92      ConnectionHealthChecker::Result result) {}
93
94 protected:
95  static const char kAddress[];
96
97  void SetMockServiceState(Service::ConnectState old_state,
98                           Service::ConnectState new_state) {
99    out_of_credits_detector_->NotifyServiceStateChanged(old_state, new_state);
100  }
101
102  void SetTrafficMonitor(TrafficMonitor* traffic_monitor) {
103    out_of_credits_detector_->set_traffic_monitor(traffic_monitor);
104  }
105
106  void SetConnectionHealthChecker(ConnectionHealthChecker* health_checker) {
107    out_of_credits_detector_->set_connection_health_checker(health_checker);
108  }
109
110  EventDispatcherForTest dispatcher_;
111  MockModemInfo modem_info_;
112  NiceMock<MockDeviceInfo> device_info_;
113  NiceMock<MockManager> manager_;
114  NiceMock<MockMetrics> metrics_;
115  scoped_refptr<NiceMock<MockCellular>> cellular_;
116  scoped_refptr<NiceMock<MockCellularService>> service_;
117  scoped_refptr<NiceMock<MockConnection>> connection_;
118  string interface_name_;
119  vector<string> dns_servers_;
120  string portal_check_url_;
121  base::Time resume_start_time_;
122  std::unique_ptr<ActivePassiveOutOfCreditsDetector> out_of_credits_detector_;
123};
124
125const char ActivePassiveOutOfCreditsDetectorTest::kAddress[] = "000102030405";
126
127TEST_F(ActivePassiveOutOfCreditsDetectorTest,
128    ConnectDisconnectLoopOutOfCreditsDetected) {
129  EXPECT_CALL(*service_, Connect(_, _)).Times(2);
130  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
131  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
132  SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
133  EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
134  dispatcher_.DispatchPendingEvents();
135  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
136  SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
137  SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
138  EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
139  dispatcher_.DispatchPendingEvents();
140  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
141  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
142  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
143  EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
144  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
145}
146
147TEST_F(ActivePassiveOutOfCreditsDetectorTest,
148    ConnectDisconnectLoopDetectionNotSkippedAfterSlowResume) {
149  resume_start_time_ =
150      base::Time::Now() -
151      base::TimeDelta::FromSeconds(
152      ActivePassiveOutOfCreditsDetector::kOutOfCreditsResumeIgnoreSeconds + 1);
153  EXPECT_CALL(*service_, Connect(_, _)).Times(2);
154  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
155  SetMockServiceState(Service::kStateAssociating, Service::kStateFailure);
156  EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
157  dispatcher_.DispatchPendingEvents();
158  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
159  SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
160  SetMockServiceState(Service::kStateConfiguring, Service::kStateIdle);
161  EXPECT_TRUE(out_of_credits_detector_->IsDetecting());
162  dispatcher_.DispatchPendingEvents();
163  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
164  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
165  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
166  EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
167  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
168}
169
170TEST_F(ActivePassiveOutOfCreditsDetectorTest,
171    ConnectDisconnectLoopDetectionSkippedAfterResume) {
172  resume_start_time_ = base::Time::Now();
173  ON_CALL(*service_, resume_start_time())
174      .WillByDefault(ReturnRef(resume_start_time_));
175  EXPECT_CALL(*service_, Connect(_, _)).Times(0);
176  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
177  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
178  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
179  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
180  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
181  // There should not be any pending connect requests but dispatch pending
182  // events anyway to be sure.
183  dispatcher_.DispatchPendingEvents();
184}
185
186TEST_F(ActivePassiveOutOfCreditsDetectorTest,
187    ConnectDisconnectLoopDetectionSkippedAlreadyOutOfCredits) {
188  EXPECT_CALL(*service_, Connect(_, _)).Times(0);
189  out_of_credits_detector_->ReportOutOfCredits(true);
190  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
191  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
192  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
193  // There should not be any pending connect requests but dispatch pending
194  // events anyway to be sure.
195  dispatcher_.DispatchPendingEvents();
196}
197
198TEST_F(ActivePassiveOutOfCreditsDetectorTest,
199    ConnectDisconnectLoopDetectionSkippedExplicitDisconnect) {
200  EXPECT_CALL(*service_, Connect(_, _)).Times(0);
201  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
202  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
203  EXPECT_CALL(*service_, explicitly_disconnected()).WillOnce(Return(true));
204  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
205  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
206  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
207  // There should not be any pending connect requests but dispatch pending
208  // events anyway to be sure.
209  dispatcher_.DispatchPendingEvents();
210}
211
212TEST_F(ActivePassiveOutOfCreditsDetectorTest,
213    ConnectDisconnectLoopDetectionConnectionNotDropped) {
214  EXPECT_CALL(*service_, Connect(_, _)).Times(0);
215  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
216  SetMockServiceState(Service::kStateAssociating, Service::kStateConfiguring);
217  SetMockServiceState(Service::kStateConfiguring, Service::kStateConnected);
218  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
219  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
220  // There should not be any pending connect requests but dispatch pending
221  // events anyway to be sure.
222  dispatcher_.DispatchPendingEvents();
223}
224
225TEST_F(ActivePassiveOutOfCreditsDetectorTest,
226    ConnectDisconnectLoopDetectionIntermittentNetwork) {
227  EXPECT_CALL(*service_, Connect(_, _)).Times(0);
228  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
229  out_of_credits_detector_->connect_start_time_ =
230      base::Time::Now() -
231      base::TimeDelta::FromSeconds(
232          ActivePassiveOutOfCreditsDetector::
233          kOutOfCreditsConnectionDropSeconds + 1);
234  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
235  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
236  EXPECT_FALSE(out_of_credits_detector_->IsDetecting());
237  // There should not be any pending connect requests but dispatch pending
238  // events anyway to be sure.
239  dispatcher_.DispatchPendingEvents();
240}
241
242TEST_F(ActivePassiveOutOfCreditsDetectorTest, StartTrafficMonitor) {
243  MockTrafficMonitor* traffic_monitor = new StrictMock<MockTrafficMonitor>();
244  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
245
246  // Traffic monitor should only start when the service is connected.
247  EXPECT_CALL(*traffic_monitor, Start()).Times(1);
248  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
249  Mock::VerifyAndClearExpectations(traffic_monitor);
250
251  // Traffic monitor should not start for other state transitions.
252  EXPECT_CALL(*traffic_monitor, Start()).Times(0);
253  EXPECT_CALL(*traffic_monitor, Stop()).Times(AnyNumber());
254  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
255  SetMockServiceState(Service::kStateIdle, Service::kStateConfiguring);
256  SetMockServiceState(Service::kStateConfiguring, Service::kStateFailure);
257  SetMockServiceState(Service::kStateIdle, Service::kStateAssociating);
258  SetMockServiceState(Service::kStateConfiguring, Service::kStatePortal);
259  SetMockServiceState(Service::kStatePortal, Service::kStateOnline);
260}
261
262TEST_F(ActivePassiveOutOfCreditsDetectorTest, StopTrafficMonitor) {
263  // Traffic monitor should stop when the service is disconnected.
264  MockTrafficMonitor* traffic_monitor = new StrictMock<MockTrafficMonitor>();
265  SetTrafficMonitor(traffic_monitor);  // Passes ownership.
266  EXPECT_CALL(*traffic_monitor, Start());
267  EXPECT_CALL(*traffic_monitor, Stop());
268  SetMockServiceState(Service::kStateAssociating, Service::kStateConnected);
269  SetMockServiceState(Service::kStateConnected, Service::kStateIdle);
270  Mock::VerifyAndClearExpectations(traffic_monitor);
271
272  EXPECT_CALL(*traffic_monitor, Start());
273  EXPECT_CALL(*traffic_monitor, Stop());
274  SetMockServiceState(Service::kStateIdle, Service::kStateConnected);
275  SetMockServiceState(Service::kStateConnected, Service::kStateFailure);
276  Mock::VerifyAndClearExpectations(traffic_monitor);
277
278  // Need an additional call to Stop() because |traffic_monitor| destructor
279  // will call stop.
280  EXPECT_CALL(*traffic_monitor, Stop());
281}
282
283TEST_F(ActivePassiveOutOfCreditsDetectorTest, OnNoNetworkRouting) {
284  // Make sure the connection health checker starts when there is no network
285  // routing.
286  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
287  MockConnectionHealthChecker* health_checker =
288      new MockConnectionHealthChecker(
289          service_->connection(),
290          modem_info_.dispatcher(),
291          manager_.health_checker_remote_ips(),
292          Bind(&ActivePassiveOutOfCreditsDetectorTest::
293               OnConnectionHealthCheckerResult,
294               Unretained(this)));
295  SetConnectionHealthChecker(health_checker);  // Passes ownership.
296  EXPECT_CALL(*health_checker, Start());
297  out_of_credits_detector_->OnNoNetworkRouting(0);
298  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
299  Mock::VerifyAndClearExpectations(health_checker);
300
301  // Make sure connection health checker does not start again if there is a
302  // health check in progress.
303  EXPECT_CALL(*health_checker, health_check_in_progress())
304      .WillOnce(Return(true));
305  EXPECT_CALL(*health_checker, Start()).Times(0);
306  out_of_credits_detector_->OnNoNetworkRouting(0);
307}
308
309TEST_F(ActivePassiveOutOfCreditsDetectorTest,
310    OnConnectionHealthCheckerResult) {
311  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
312  EXPECT_CALL(*service_, Disconnect(_, _)).Times(0);
313  out_of_credits_detector_->OnConnectionHealthCheckerResult(
314      ConnectionHealthChecker::kResultUnknown);
315  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
316  out_of_credits_detector_->OnConnectionHealthCheckerResult(
317      ConnectionHealthChecker::kResultConnectionFailure);
318  EXPECT_FALSE(out_of_credits_detector_->out_of_credits());
319  Mock::VerifyAndClearExpectations(service_.get());
320
321  EXPECT_CALL(*service_, Disconnect(_,
322      ::testing::StrEq("out-of-credits"))).
323          Times(1);
324  out_of_credits_detector_->OnConnectionHealthCheckerResult(
325      ConnectionHealthChecker::kResultCongestedTxQueue);
326  EXPECT_TRUE(out_of_credits_detector_->out_of_credits());
327}
328
329}  // namespace shill
330