Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS 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 "shill/link_monitor.h" |
| 6 | |
Alex Vakulenko | 8a53229 | 2014-06-16 17:18:44 -0700 | [diff] [blame] | 7 | #include <string> |
| 8 | |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 9 | #include <base/bind.h> |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 10 | #include <gtest/gtest.h> |
| 11 | |
Christopher Wiley | b691efd | 2012-08-09 13:51:51 -0700 | [diff] [blame] | 12 | #include "shill/logging.h" |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 13 | #include "shill/mock_active_link_monitor.h" |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 14 | #include "shill/mock_connection.h" |
Alex Vakulenko | a41ab51 | 2014-07-23 14:24:23 -0700 | [diff] [blame] | 15 | #include "shill/mock_control.h" |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 16 | #include "shill/mock_device_info.h" |
| 17 | #include "shill/mock_event_dispatcher.h" |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 18 | #include "shill/mock_log.h" |
Paul Stewart | ff845fc | 2012-08-07 07:28:44 -0700 | [diff] [blame] | 19 | #include "shill/mock_metrics.h" |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 20 | #include "shill/mock_passive_link_monitor.h" |
Peter Qiu | 8d6b597 | 2014-10-28 15:33:34 -0700 | [diff] [blame] | 21 | #include "shill/net/byte_string.h" |
Peter Qiu | 8d6b597 | 2014-10-28 15:33:34 -0700 | [diff] [blame] | 22 | #include "shill/net/mock_time.h" |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 23 | |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 24 | using base::Bind; |
| 25 | using base::Unretained; |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 26 | using std::string; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 27 | using testing::_; |
| 28 | using testing::AnyNumber; |
| 29 | using testing::HasSubstr; |
| 30 | using testing::Invoke; |
| 31 | using testing::Mock; |
| 32 | using testing::NiceMock; |
| 33 | using testing::Return; |
| 34 | using testing::ReturnRef; |
| 35 | using testing::SetArgumentPointee; |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 36 | using testing::StrictMock; |
| 37 | using testing::Test; |
| 38 | |
| 39 | namespace shill { |
| 40 | |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 41 | namespace { |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 42 | const uint8_t kGatewayMACAddress[] = { 0, 1, 2, 3, 4, 5 }; |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 43 | } // namespace |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 44 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 45 | class LinkMonitorObserver { |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 46 | public: |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 47 | LinkMonitorObserver() |
| 48 | : failure_callback_( |
| 49 | Bind(&LinkMonitorObserver::OnFailureCallback, Unretained(this))), |
| 50 | gateway_change_callback_( |
| 51 | Bind(&LinkMonitorObserver::OnGatewayChangeCallback, |
| 52 | Unretained(this))) {} |
| 53 | virtual ~LinkMonitorObserver() {} |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 54 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 55 | MOCK_METHOD0(OnFailureCallback, void()); |
| 56 | MOCK_METHOD0(OnGatewayChangeCallback, void()); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 57 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 58 | const LinkMonitor::FailureCallback failure_callback() { |
| 59 | return failure_callback_; |
| 60 | } |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 61 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 62 | const LinkMonitor::GatewayChangeCallback gateway_change_callback() { |
| 63 | return gateway_change_callback_; |
| 64 | } |
Peter Qiu | b5d124f | 2014-04-14 12:05:02 -0700 | [diff] [blame] | 65 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 66 | private: |
| 67 | LinkMonitor::FailureCallback failure_callback_; |
| 68 | LinkMonitor::GatewayChangeCallback gateway_change_callback_; |
| 69 | |
| 70 | DISALLOW_COPY_AND_ASSIGN(LinkMonitorObserver); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 71 | }; |
| 72 | |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 73 | class LinkMonitorTest : public Test { |
| 74 | public: |
| 75 | LinkMonitorTest() |
Thieu Le | 6c1e3bb | 2013-02-06 15:20:35 -0800 | [diff] [blame] | 76 | : metrics_(&dispatcher_), |
Ben Chan | cc225ef | 2014-09-30 13:26:51 -0700 | [diff] [blame] | 77 | device_info_(&control_, nullptr, nullptr, nullptr), |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 78 | connection_(new StrictMock<MockConnection>(&device_info_)), |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 79 | active_link_monitor_(new MockActiveLinkMonitor()), |
| 80 | passive_link_monitor_(new MockPassiveLinkMonitor()), |
| 81 | monitor_(connection_, |
| 82 | &dispatcher_, |
| 83 | &metrics_, |
| 84 | &device_info_, |
| 85 | observer_.failure_callback(), |
| 86 | observer_.gateway_change_callback()) {} |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 87 | virtual ~LinkMonitorTest() {} |
| 88 | |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 89 | virtual void SetUp() { |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 90 | monitor_.active_link_monitor_.reset(active_link_monitor_); |
| 91 | monitor_.passive_link_monitor_.reset(passive_link_monitor_); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 92 | monitor_.time_ = &time_; |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 93 | |
Paul Stewart | 0443aa5 | 2012-08-09 10:43:50 -0700 | [diff] [blame] | 94 | time_val_.tv_sec = 0; |
| 95 | time_val_.tv_usec = 0; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 96 | EXPECT_CALL(time_, GetTimeMonotonic(_)) |
| 97 | .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0))); |
Alex Deymo | fddc09a | 2013-07-03 18:41:31 -0700 | [diff] [blame] | 98 | EXPECT_CALL(*connection_, technology()) |
| 99 | .WillRepeatedly(Return(Technology::kEthernet)); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 100 | } |
| 101 | |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 102 | void AdvanceTime(int time_ms) { |
Han Shen | fc34925 | 2012-08-30 11:36:04 -0700 | [diff] [blame] | 103 | struct timeval adv_time = { |
| 104 | static_cast<time_t>(time_ms/1000), |
| 105 | static_cast<time_t>((time_ms % 1000) * 1000) }; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 106 | timeradd(&time_val_, &adv_time, &time_val_); |
| 107 | EXPECT_CALL(time_, GetTimeMonotonic(_)) |
| 108 | .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0))); |
| 109 | } |
| 110 | |
Paul Stewart | 3b30ca5 | 2015-06-16 13:13:10 -0700 | [diff] [blame^] | 111 | void SetGatewayMacAddress(const ByteString& gateway_mac_address) { |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 112 | monitor_.gateway_mac_address_ = gateway_mac_address; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 113 | } |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 114 | |
Paul Stewart | 3b30ca5 | 2015-06-16 13:13:10 -0700 | [diff] [blame^] | 115 | void VerifyGatewayMacAddress(const ByteString& gateway_mac_address) { |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 116 | EXPECT_TRUE(monitor_.gateway_mac_address_.Equals(gateway_mac_address)); |
| 117 | } |
| 118 | |
| 119 | void TriggerActiveLinkMonitorFailure(Metrics::LinkMonitorFailure failure, |
| 120 | int broadcast_failure_count, |
| 121 | int unicast_failure_count) { |
| 122 | monitor_.OnActiveLinkMonitorFailure(failure, |
| 123 | broadcast_failure_count, |
| 124 | unicast_failure_count); |
| 125 | } |
| 126 | |
| 127 | void TriggerActiveLinkMonitorSuccess() { |
| 128 | monitor_.OnActiveLinkMonitorSuccess(); |
| 129 | } |
| 130 | |
| 131 | void TriggerPassiveLinkMonitorResultCallback(bool status) { |
| 132 | monitor_.OnPassiveLinkMonitorResultCallback(status); |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 133 | } |
| 134 | |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 135 | protected: |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 136 | MockEventDispatcher dispatcher_; |
Paul Stewart | ff845fc | 2012-08-07 07:28:44 -0700 | [diff] [blame] | 137 | StrictMock<MockMetrics> metrics_; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 138 | MockControl control_; |
| 139 | NiceMock<MockDeviceInfo> device_info_; |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 140 | scoped_refptr<MockConnection> connection_; |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 141 | MockTime time_; |
| 142 | struct timeval time_val_; |
Paul Stewart | 3b30ca5 | 2015-06-16 13:13:10 -0700 | [diff] [blame^] | 143 | MockActiveLinkMonitor* active_link_monitor_; |
| 144 | MockPassiveLinkMonitor* passive_link_monitor_; |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 145 | LinkMonitorObserver observer_; |
| 146 | LinkMonitor monitor_; |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 147 | }; |
| 148 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 149 | MATCHER_P(IsMacAddress, mac_address, "") { |
| 150 | return mac_address.Equals(arg); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 151 | } |
| 152 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 153 | TEST_F(LinkMonitorTest, Start) { |
| 154 | EXPECT_CALL(*active_link_monitor_, |
| 155 | Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| 156 | .WillOnce(Return(false)); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 157 | EXPECT_FALSE(monitor_.Start()); |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 158 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 159 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 160 | EXPECT_CALL(*active_link_monitor_, |
| 161 | Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| 162 | .WillOnce(Return(true)); |
| 163 | EXPECT_TRUE(monitor_.Start()); |
| 164 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 165 | } |
| 166 | |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 167 | TEST_F(LinkMonitorTest, OnAfterResume) { |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 168 | ByteString gateway_mac(kGatewayMACAddress, arraysize(kGatewayMACAddress)); |
| 169 | const bool kGatewayUnicastArpSupport = true; |
| 170 | SetGatewayMacAddress(gateway_mac); |
| 171 | // Verify gateway settings persist when link monitor is restarted, and |
| 172 | // active link monitor is started with fast test period. |
| 173 | EXPECT_CALL(*active_link_monitor_, Stop()).Times(1); |
| 174 | EXPECT_CALL(*passive_link_monitor_, Stop()).Times(1); |
| 175 | EXPECT_CALL(*active_link_monitor_, gateway_supports_unicast_arp()) |
| 176 | .WillOnce(Return(kGatewayUnicastArpSupport)); |
| 177 | EXPECT_CALL(*active_link_monitor_, |
| 178 | set_gateway_mac_address(IsMacAddress(gateway_mac))); |
| 179 | EXPECT_CALL(*active_link_monitor_, |
| 180 | set_gateway_supports_unicast_arp(kGatewayUnicastArpSupport)); |
| 181 | EXPECT_CALL(*active_link_monitor_, |
| 182 | Start(ActiveLinkMonitor::kFastTestPeriodMilliseconds)); |
| 183 | monitor_.OnAfterResume(); |
| 184 | VerifyGatewayMacAddress(gateway_mac); |
| 185 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
| 186 | Mock::VerifyAndClearExpectations(passive_link_monitor_); |
| 187 | } |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 188 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 189 | TEST_F(LinkMonitorTest, OnActiveLinkMonitorFailure) { |
| 190 | // Start link monitor. |
| 191 | EXPECT_CALL(*active_link_monitor_, |
| 192 | Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)) |
| 193 | .WillOnce(Return(true)); |
| 194 | EXPECT_TRUE(monitor_.Start()); |
| 195 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
Paul Stewart | b434ce5 | 2013-09-23 13:53:49 -0700 | [diff] [blame] | 196 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 197 | const int kBroadcastFailureCount = 5; |
| 198 | const int kUnicastFailureCount = 3; |
| 199 | const int kElapsedTimeMilliseconds = 5000; |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 200 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 201 | // Active monitor failed after 5 seconds. |
| 202 | EXPECT_CALL(observer_, OnFailureCallback()).Times(1); |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 203 | EXPECT_CALL(metrics_, SendEnumToUMA( |
| 204 | HasSubstr("LinkMonitorFailure"), |
| 205 | Metrics::kLinkMonitorFailureThresholdReached, _)); |
| 206 | EXPECT_CALL(metrics_, SendToUMA( |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 207 | HasSubstr("LinkMonitorSecondsToFailure"), kElapsedTimeMilliseconds / 1000, |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 208 | _, _, _)); |
| 209 | EXPECT_CALL(metrics_, SendToUMA( |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 210 | HasSubstr("BroadcastErrorsAtFailure"), kBroadcastFailureCount, |
mukesh agrawal | bb2231c | 2013-07-17 16:32:24 -0700 | [diff] [blame] | 211 | _, _, _)); |
Paul Stewart | b434ce5 | 2013-09-23 13:53:49 -0700 | [diff] [blame] | 212 | EXPECT_CALL(metrics_, SendToUMA( |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 213 | HasSubstr("UnicastErrorsAtFailure"), kUnicastFailureCount, |
Paul Stewart | b434ce5 | 2013-09-23 13:53:49 -0700 | [diff] [blame] | 214 | _, _, _)); |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 215 | AdvanceTime(kElapsedTimeMilliseconds); |
| 216 | TriggerActiveLinkMonitorFailure(Metrics::kLinkMonitorFailureThresholdReached, |
| 217 | kBroadcastFailureCount, |
| 218 | kUnicastFailureCount); |
Paul Stewart | b434ce5 | 2013-09-23 13:53:49 -0700 | [diff] [blame] | 219 | } |
| 220 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 221 | TEST_F(LinkMonitorTest, OnActiveLinkMonitorSuccess) { |
| 222 | ByteString gateway_mac(kGatewayMACAddress, |
| 223 | arraysize(kGatewayMACAddress)); |
| 224 | EXPECT_CALL(*active_link_monitor_, gateway_mac_address()) |
| 225 | .WillRepeatedly(ReturnRef(gateway_mac)); |
| 226 | |
| 227 | // Active link monitor succeed for the first time, gateway MAC address will be |
| 228 | // updated. |
| 229 | EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(1); |
| 230 | EXPECT_CALL(*passive_link_monitor_, Start( |
| 231 | PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1); |
| 232 | TriggerActiveLinkMonitorSuccess(); |
| 233 | VerifyGatewayMacAddress(gateway_mac); |
| 234 | Mock::VerifyAndClearExpectations(&observer_); |
| 235 | Mock::VerifyAndClearExpectations(passive_link_monitor_); |
| 236 | |
| 237 | // Active link monitor succeed again, gateway MAC address not changed. |
| 238 | EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(0); |
| 239 | EXPECT_CALL(*passive_link_monitor_, Start( |
| 240 | PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1); |
| 241 | TriggerActiveLinkMonitorSuccess(); |
| 242 | VerifyGatewayMacAddress(gateway_mac); |
| 243 | Mock::VerifyAndClearExpectations(&observer_); |
| 244 | Mock::VerifyAndClearExpectations(passive_link_monitor_); |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 245 | } |
| 246 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 247 | TEST_F(LinkMonitorTest, OnPassiveLinkMonitorResultCallback) { |
| 248 | // Active link monitor should start regardless of the result of the passive |
| 249 | // link monitor. |
Paul Stewart | 6c72c97 | 2012-07-27 11:29:20 -0700 | [diff] [blame] | 250 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 251 | EXPECT_CALL(*active_link_monitor_, |
| 252 | Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)); |
| 253 | TriggerPassiveLinkMonitorResultCallback(true); |
| 254 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 255 | |
Peter Qiu | ffa5637 | 2015-01-22 14:25:23 -0800 | [diff] [blame] | 256 | EXPECT_CALL(*active_link_monitor_, |
| 257 | Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds)); |
| 258 | TriggerPassiveLinkMonitorResultCallback(false); |
| 259 | Mock::VerifyAndClearExpectations(active_link_monitor_); |
Paul Stewart | f1961f8 | 2012-09-11 20:45:39 -0700 | [diff] [blame] | 260 | } |
| 261 | |
Paul Stewart | 3f43f43 | 2012-07-16 12:12:45 -0700 | [diff] [blame] | 262 | } // namespace shill |