| // Copyright (c) 2013 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/ethernet.h" |
| |
| #include <netinet/ether.h> |
| #include <linux/if.h> // Needs definitions from netinet/ether.h |
| |
| #include <base/memory/ref_counted.h> |
| #include <base/memory/scoped_ptr.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/mock_device_info.h" |
| #include "shill/mock_dhcp_config.h" |
| #include "shill/mock_dhcp_provider.h" |
| #include "shill/mock_eap_credentials.h" |
| #include "shill/mock_eap_listener.h" |
| #include "shill/mock_event_dispatcher.h" |
| #include "shill/mock_ethernet_eap_provider.h" |
| #include "shill/mock_ethernet_service.h" |
| #include "shill/mock_glib.h" |
| #include "shill/mock_log.h" |
| #include "shill/mock_manager.h" |
| #include "shill/mock_metrics.h" |
| #include "shill/mock_rtnl_handler.h" |
| #include "shill/mock_service.h" |
| #include "shill/mock_supplicant_interface_proxy.h" |
| #include "shill/mock_supplicant_process_proxy.h" |
| #include "shill/nice_mock_control.h" |
| #include "shill/proxy_factory.h" |
| |
| using std::string; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::EndsWith; |
| using testing::Eq; |
| using testing::Mock; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::ReturnRef; |
| using testing::StrEq; |
| using testing::StrictMock; |
| using testing::Throw; |
| |
| namespace shill { |
| |
| class EthernetTest : public testing::Test { |
| public: |
| EthernetTest() |
| : metrics_(NULL), |
| manager_(&control_interface_, NULL, &metrics_, &glib_), |
| device_info_(&control_interface_, &dispatcher_, &metrics_, &manager_), |
| ethernet_(new Ethernet(&control_interface_, |
| &dispatcher_, |
| &metrics_, |
| &manager_, |
| kDeviceName, |
| kDeviceAddress, |
| kInterfaceIndex)), |
| dhcp_config_(new MockDHCPConfig(&control_interface_, |
| kDeviceName)), |
| eap_listener_(new MockEapListener()), |
| mock_service_(new MockEthernetService(&control_interface_, &metrics_)), |
| mock_eap_service_(new MockService(&control_interface_, |
| &dispatcher_, |
| &metrics_, |
| &manager_)), |
| proxy_factory_(this), |
| supplicant_interface_proxy_( |
| new NiceMock<MockSupplicantInterfaceProxy>()), |
| supplicant_process_proxy_(new NiceMock<MockSupplicantProcessProxy>()) {} |
| |
| virtual void SetUp() { |
| ethernet_->rtnl_handler_ = &rtnl_handler_; |
| ethernet_->proxy_factory_ = &proxy_factory_; |
| // Transfers ownership. |
| ethernet_->eap_listener_.reset(eap_listener_); |
| |
| ethernet_->set_dhcp_provider(&dhcp_provider_); |
| ON_CALL(manager_, device_info()).WillByDefault(Return(&device_info_)); |
| EXPECT_CALL(manager_, UpdateEnabledTechnologies()).Times(AnyNumber()); |
| EXPECT_CALL(manager_, ethernet_eap_provider()) |
| .WillRepeatedly(Return(ðernet_eap_provider_)); |
| ethernet_eap_provider_.set_service(mock_eap_service_); |
| } |
| |
| virtual void TearDown() { |
| ethernet_eap_provider_.set_service(NULL); |
| ethernet_->set_dhcp_provider(NULL); |
| ethernet_->eap_listener_.reset(); |
| } |
| |
| protected: |
| class TestProxyFactory : public ProxyFactory { |
| public: |
| explicit TestProxyFactory(EthernetTest *test) : test_(test) {} |
| |
| virtual SupplicantProcessProxyInterface *CreateSupplicantProcessProxy( |
| const char */*dbus_path*/, const char */*dbus_addr*/) { |
| return test_->supplicant_process_proxy_.release(); |
| } |
| |
| virtual SupplicantInterfaceProxyInterface *CreateSupplicantInterfaceProxy( |
| SupplicantEventDelegateInterface */*delegate*/, |
| const DBus::Path &/*object_path*/, |
| const char */*dbus_addr*/) { |
| return test_->supplicant_interface_proxy_.release(); |
| } |
| |
| MOCK_METHOD2(CreateSupplicantNetworkProxy, |
| SupplicantNetworkProxyInterface *( |
| const DBus::Path &object_path, |
| const char *dbus_addr)); |
| |
| private: |
| EthernetTest *test_; |
| }; |
| |
| static const char kDeviceName[]; |
| static const char kDeviceAddress[]; |
| static const char kInterfacePath[]; |
| static const int kInterfaceIndex; |
| |
| bool GetIsEapAuthenticated() { return ethernet_->is_eap_authenticated_; } |
| void SetIsEapAuthenticated(bool is_eap_authenticated) { |
| ethernet_->is_eap_authenticated_ = is_eap_authenticated; |
| } |
| bool GetIsEapDetected() { return ethernet_->is_eap_detected_; } |
| void SetIsEapDetected(bool is_eap_detected) { |
| ethernet_->is_eap_detected_ = is_eap_detected; |
| } |
| bool GetLinkUp() { return ethernet_->link_up_; } |
| const ServiceRefPtr &GetSelectedService() { |
| return ethernet_->selected_service(); |
| } |
| ServiceRefPtr GetService() { return ethernet_->service_; } |
| void SetService(const EthernetServiceRefPtr &service) { |
| ethernet_->service_ = service; |
| } |
| const PropertyStore &GetStore() { return ethernet_->store(); } |
| void StartEthernet() { |
| EXPECT_CALL(rtnl_handler_, |
| SetInterfaceFlags(kInterfaceIndex, IFF_UP, IFF_UP)); |
| ethernet_->Start(NULL, EnabledStateChangedCallback()); |
| } |
| const SupplicantInterfaceProxyInterface *GetSupplicantInterfaceProxy() { |
| return ethernet_->supplicant_interface_proxy_.get(); |
| } |
| const SupplicantProcessProxyInterface *GetSupplicantProcessProxy() { |
| return ethernet_->supplicant_process_proxy_.get(); |
| } |
| const string &GetSupplicantInterfacePath() { |
| return ethernet_->supplicant_interface_path_; |
| } |
| const string &GetSupplicantNetworkPath() { |
| return ethernet_->supplicant_network_path_; |
| } |
| void SetSupplicantNetworkPath(const string &network_path) { |
| ethernet_->supplicant_network_path_ = network_path; |
| } |
| bool InvokeStartSupplicant() { |
| return ethernet_->StartSupplicant(); |
| } |
| void InvokeStopSupplicant() { |
| return ethernet_->StopSupplicant(); |
| } |
| bool InvokeStartEapAuthentication() { |
| return ethernet_->StartEapAuthentication(); |
| } |
| void StartSupplicant() { |
| SupplicantInterfaceProxyInterface *interface = |
| supplicant_interface_proxy_.get(); |
| SupplicantProcessProxyInterface *process = supplicant_process_proxy_.get(); |
| EXPECT_CALL(*supplicant_process_proxy_.get(), CreateInterface(_)) |
| .WillOnce(Return(kInterfacePath)); |
| EXPECT_TRUE(InvokeStartSupplicant()); |
| EXPECT_EQ(interface, GetSupplicantInterfaceProxy()); |
| EXPECT_EQ(process, GetSupplicantProcessProxy()); |
| EXPECT_EQ(kInterfacePath, GetSupplicantInterfacePath()); |
| } |
| void TriggerOnEapDetected() { ethernet_->OnEapDetected(); } |
| void TriggerCertification(const string &subject, uint32 depth) { |
| ethernet_->CertificationTask(subject, depth); |
| } |
| void TriggerTryEapAuthentication() { |
| ethernet_->TryEapAuthenticationTask(); |
| } |
| |
| StrictMock<MockEventDispatcher> dispatcher_; |
| MockGLib glib_; |
| NiceMockControl control_interface_; |
| MockMetrics metrics_; |
| MockManager manager_; |
| MockDeviceInfo device_info_; |
| EthernetRefPtr ethernet_; |
| MockEthernetEapProvider ethernet_eap_provider_; |
| MockDHCPProvider dhcp_provider_; |
| scoped_refptr<MockDHCPConfig> dhcp_config_; |
| |
| // Owned by Ethernet instance, but tracked here for expectations. |
| MockEapListener *eap_listener_; |
| |
| MockRTNLHandler rtnl_handler_; |
| scoped_refptr<MockEthernetService> mock_service_; |
| scoped_refptr<MockService> mock_eap_service_; |
| NiceMock<TestProxyFactory> proxy_factory_; |
| scoped_ptr<MockSupplicantInterfaceProxy> supplicant_interface_proxy_; |
| scoped_ptr<MockSupplicantProcessProxy> supplicant_process_proxy_; |
| }; |
| |
| // static |
| const char EthernetTest::kDeviceName[] = "eth0"; |
| const char EthernetTest::kDeviceAddress[] = "000102030405"; |
| const char EthernetTest::kInterfacePath[] = "/interface/path"; |
| const int EthernetTest::kInterfaceIndex = 123; |
| |
| TEST_F(EthernetTest, Construct) { |
| EXPECT_FALSE(GetLinkUp()); |
| EXPECT_FALSE(GetIsEapAuthenticated()); |
| EXPECT_FALSE(GetIsEapDetected()); |
| EXPECT_TRUE(GetStore().Contains(kEapAuthenticationCompletedProperty)); |
| EXPECT_TRUE(GetStore().Contains(kEapAuthenticatorDetectedProperty)); |
| EXPECT_EQ(NULL, GetService().get()); |
| } |
| |
| TEST_F(EthernetTest, StartStop) { |
| StartEthernet(); |
| |
| EXPECT_FALSE(GetService().get() == NULL); |
| |
| Service* service = GetService().get(); |
| EXPECT_CALL(manager_, DeregisterService(Eq(service))); |
| ethernet_->Stop(NULL, EnabledStateChangedCallback()); |
| EXPECT_EQ(NULL, GetService().get()); |
| } |
| |
| TEST_F(EthernetTest, LinkEvent) { |
| StartEthernet(); |
| |
| // Link-down event while already down. |
| EXPECT_CALL(manager_, DeregisterService(_)).Times(0); |
| EXPECT_CALL(*eap_listener_, Start()).Times(0); |
| ethernet_->LinkEvent(0, IFF_LOWER_UP); |
| EXPECT_FALSE(GetLinkUp()); |
| EXPECT_FALSE(GetIsEapDetected()); |
| Mock::VerifyAndClearExpectations(&manager_); |
| |
| // Link-up event while down. |
| EXPECT_CALL(manager_, RegisterService(GetService())); |
| EXPECT_CALL(*eap_listener_, Start()); |
| ethernet_->LinkEvent(IFF_LOWER_UP, 0); |
| EXPECT_TRUE(GetLinkUp()); |
| EXPECT_FALSE(GetIsEapDetected()); |
| Mock::VerifyAndClearExpectations(&manager_); |
| |
| // Link-up event while already up. |
| EXPECT_CALL(manager_, RegisterService(GetService())).Times(0); |
| EXPECT_CALL(*eap_listener_, Start()).Times(0); |
| ethernet_->LinkEvent(IFF_LOWER_UP, 0); |
| EXPECT_TRUE(GetLinkUp()); |
| EXPECT_FALSE(GetIsEapDetected()); |
| Mock::VerifyAndClearExpectations(&manager_); |
| |
| // Link-down event while up. |
| SetIsEapDetected(true); |
| // This is done in SetUp, but we have to reestablish this after calling |
| // VerifyAndClearExpectations() above. |
| EXPECT_CALL(manager_, ethernet_eap_provider()) |
| .WillRepeatedly(Return(ðernet_eap_provider_)); |
| EXPECT_CALL(ethernet_eap_provider_, |
| ClearCredentialChangeCallback(ethernet_.get())); |
| EXPECT_CALL(manager_, DeregisterService(GetService())); |
| EXPECT_CALL(*eap_listener_, Stop()); |
| ethernet_->LinkEvent(0, IFF_LOWER_UP); |
| EXPECT_FALSE(GetLinkUp()); |
| EXPECT_FALSE(GetIsEapDetected()); |
| |
| // Restore this expectation during shutdown. |
| EXPECT_CALL(manager_, UpdateEnabledTechnologies()).Times(AnyNumber()); |
| } |
| |
| TEST_F(EthernetTest, ConnectToFailure) { |
| StartEthernet(); |
| SetService(mock_service_); |
| EXPECT_EQ(NULL, GetSelectedService().get()); |
| EXPECT_CALL(dhcp_provider_, CreateConfig(_, _, _, _)). |
| WillOnce(Return(dhcp_config_)); |
| EXPECT_CALL(*dhcp_config_.get(), RequestIP()).WillOnce(Return(false)); |
| EXPECT_CALL(dispatcher_, PostTask(_)); // Posts ConfigureStaticIPTask. |
| // Since we never called SelectService()... |
| EXPECT_CALL(*mock_service_, SetState(_)).Times(0); |
| ethernet_->ConnectTo(mock_service_); |
| EXPECT_EQ(NULL, GetSelectedService().get()); |
| } |
| |
| TEST_F(EthernetTest, ConnectToSuccess) { |
| StartEthernet(); |
| SetService(mock_service_); |
| EXPECT_EQ(NULL, GetSelectedService().get()); |
| EXPECT_CALL(dhcp_provider_, CreateConfig(_, _, _, _)). |
| WillOnce(Return(dhcp_config_)); |
| EXPECT_CALL(*dhcp_config_.get(), RequestIP()).WillOnce(Return(true)); |
| EXPECT_CALL(dispatcher_, PostTask(_)); // Posts ConfigureStaticIPTask. |
| EXPECT_CALL(*mock_service_, SetState(Service::kStateConfiguring)); |
| ethernet_->ConnectTo(mock_service_.get()); |
| EXPECT_EQ(GetService().get(), GetSelectedService().get()); |
| Mock::VerifyAndClearExpectations(mock_service_); |
| |
| EXPECT_CALL(*mock_service_, SetState(Service::kStateIdle)); |
| ethernet_->DisconnectFrom(mock_service_); |
| EXPECT_EQ(NULL, GetSelectedService().get()); |
| } |
| |
| TEST_F(EthernetTest, OnEapDetected) { |
| EXPECT_FALSE(GetIsEapDetected()); |
| EXPECT_CALL(*eap_listener_, Stop()); |
| EXPECT_CALL(ethernet_eap_provider_, |
| SetCredentialChangeCallback(ethernet_.get(), _)); |
| EXPECT_CALL(dispatcher_, PostTask(_)); // Posts TryEapAuthenticationTask. |
| TriggerOnEapDetected(); |
| EXPECT_TRUE(GetIsEapDetected()); |
| } |
| |
| TEST_F(EthernetTest, TryEapAuthenticationNoService) { |
| EXPECT_CALL(*mock_eap_service_, Is8021xConnectable()).Times(0); |
| NiceScopedMockLog log; |
| EXPECT_CALL(log, Log(logging::LOG_INFO, _, |
| EndsWith("Service is missing; " |
| "not doing EAP authentication."))); |
| TriggerTryEapAuthentication(); |
| } |
| |
| TEST_F(EthernetTest, TryEapAuthenticationNotConnectableNotAuthenticated) { |
| SetService(mock_service_); |
| EXPECT_CALL(*mock_eap_service_, Is8021xConnectable()).WillOnce(Return(false)); |
| NiceScopedMockLog log; |
| EXPECT_CALL(log, Log(logging::LOG_INFO, _, |
| EndsWith("EAP Service lacks 802.1X credentials; " |
| "not doing EAP authentication."))); |
| TriggerTryEapAuthentication(); |
| SetService(NULL); |
| } |
| |
| TEST_F(EthernetTest, TryEapAuthenticationNotConnectableAuthenticated) { |
| SetService(mock_service_); |
| SetIsEapAuthenticated(true); |
| EXPECT_CALL(*mock_eap_service_, Is8021xConnectable()).WillOnce(Return(false)); |
| NiceScopedMockLog log; |
| EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); |
| EXPECT_CALL(log, Log(logging::LOG_INFO, _, |
| EndsWith("EAP Service lost 802.1X credentials; " |
| "terminating EAP authentication."))); |
| TriggerTryEapAuthentication(); |
| EXPECT_FALSE(GetIsEapAuthenticated()); |
| } |
| |
| TEST_F(EthernetTest, TryEapAuthenticationEapNotDetected) { |
| SetService(mock_service_); |
| EXPECT_CALL(*mock_eap_service_, Is8021xConnectable()).WillOnce(Return(true)); |
| NiceScopedMockLog log; |
| EXPECT_CALL(log, Log(logging::LOG_WARNING, _, |
| EndsWith("EAP authenticator not detected; " |
| "not doing EAP authentication."))); |
| TriggerTryEapAuthentication(); |
| } |
| |
| TEST_F(EthernetTest, StartSupplicant) { |
| // Save the mock proxy pointers before the Ethernet instance accepts it. |
| MockSupplicantInterfaceProxy *interface_proxy = |
| supplicant_interface_proxy_.get(); |
| MockSupplicantProcessProxy *process_proxy = supplicant_process_proxy_.get(); |
| |
| StartSupplicant(); |
| |
| // Starting it again should not invoke another call to create an interface. |
| Mock::VerifyAndClearExpectations(process_proxy); |
| EXPECT_CALL(*process_proxy, CreateInterface(_)).Times(0); |
| EXPECT_TRUE(InvokeStartSupplicant()); |
| |
| // Also, the mock pointers should remain; if the TestProxyFactory was |
| // invoked again, they would be NULL. |
| EXPECT_EQ(interface_proxy, GetSupplicantInterfaceProxy()); |
| EXPECT_EQ(process_proxy, GetSupplicantProcessProxy()); |
| EXPECT_EQ(kInterfacePath, GetSupplicantInterfacePath()); |
| } |
| |
| TEST_F(EthernetTest, StartSupplicantWithInterfaceExistsException) { |
| MockSupplicantInterfaceProxy *interface_proxy = |
| supplicant_interface_proxy_.get(); |
| MockSupplicantProcessProxy *process_proxy = supplicant_process_proxy_.get(); |
| EXPECT_CALL(*process_proxy, CreateInterface(_)) |
| .WillOnce(Throw(DBus::Error( |
| "fi.w1.wpa_supplicant1.InterfaceExists", |
| "test threw fi.w1.wpa_supplicant1.InterfaceExists"))); |
| EXPECT_CALL(*process_proxy, GetInterface(kDeviceName)) |
| .WillOnce(Return(kInterfacePath)); |
| EXPECT_TRUE(InvokeStartSupplicant()); |
| EXPECT_EQ(interface_proxy, GetSupplicantInterfaceProxy()); |
| EXPECT_EQ(process_proxy, GetSupplicantProcessProxy()); |
| EXPECT_EQ(kInterfacePath, GetSupplicantInterfacePath()); |
| } |
| |
| TEST_F(EthernetTest, StartSupplicantWithUnknownException) { |
| MockSupplicantProcessProxy *process_proxy = supplicant_process_proxy_.get(); |
| EXPECT_CALL(*process_proxy, CreateInterface(_)) |
| .WillOnce(Throw(DBus::Error( |
| "fi.w1.wpa_supplicant1.UnknownError", |
| "test threw fi.w1.wpa_supplicant1.UnknownError"))); |
| EXPECT_CALL(*process_proxy, GetInterface(kDeviceName)).Times(0); |
| EXPECT_FALSE(InvokeStartSupplicant()); |
| EXPECT_EQ(NULL, GetSupplicantInterfaceProxy()); |
| EXPECT_EQ(NULL, GetSupplicantProcessProxy()); |
| EXPECT_EQ("", GetSupplicantInterfacePath()); |
| } |
| |
| TEST_F(EthernetTest, StartEapAuthentication) { |
| MockSupplicantInterfaceProxy *interface_proxy = |
| supplicant_interface_proxy_.get(); |
| |
| StartSupplicant(); |
| SetService(mock_service_); |
| |
| EXPECT_CALL(*mock_service_, ClearEAPCertification()); |
| MockEapCredentials mock_eap_credentials; |
| EXPECT_CALL(*mock_eap_service_, eap()) |
| .WillOnce(Return(&mock_eap_credentials)); |
| EXPECT_CALL(mock_eap_credentials, PopulateSupplicantProperties(_, _, _, _)); |
| EXPECT_CALL(*interface_proxy, RemoveNetwork(_)).Times(0); |
| EXPECT_CALL(*interface_proxy, AddNetwork(_)) |
| .WillOnce(Throw(DBus::Error( |
| "fi.w1.wpa_supplicant1.UnknownError", |
| "test threw fi.w1.wpa_supplicant1.UnknownError"))); |
| EXPECT_CALL(*interface_proxy, SelectNetwork(_)).Times(0); |
| EXPECT_CALL(*interface_proxy, EAPLogon()).Times(0); |
| EXPECT_FALSE(InvokeStartEapAuthentication()); |
| Mock::VerifyAndClearExpectations(mock_service_); |
| Mock::VerifyAndClearExpectations(mock_eap_service_); |
| Mock::VerifyAndClearExpectations(interface_proxy); |
| EXPECT_EQ("", GetSupplicantNetworkPath()); |
| |
| EXPECT_CALL(*mock_service_, ClearEAPCertification()); |
| EXPECT_CALL(*interface_proxy, RemoveNetwork(_)).Times(0); |
| EXPECT_CALL(*mock_eap_service_, eap()) |
| .WillOnce(Return(&mock_eap_credentials)); |
| EXPECT_CALL(mock_eap_credentials, PopulateSupplicantProperties(_, _, _, _)); |
| const char kFirstNetworkPath[] = "/network/first-path"; |
| EXPECT_CALL(*interface_proxy, AddNetwork(_)) |
| .WillOnce(Return(kFirstNetworkPath)); |
| EXPECT_CALL(*interface_proxy, SelectNetwork(StrEq(kFirstNetworkPath))); |
| EXPECT_CALL(*interface_proxy, EAPLogon()); |
| EXPECT_TRUE(InvokeStartEapAuthentication()); |
| Mock::VerifyAndClearExpectations(mock_service_); |
| Mock::VerifyAndClearExpectations(mock_eap_service_); |
| Mock::VerifyAndClearExpectations(&mock_eap_credentials); |
| Mock::VerifyAndClearExpectations(interface_proxy); |
| EXPECT_EQ(kFirstNetworkPath, GetSupplicantNetworkPath()); |
| |
| EXPECT_CALL(*mock_service_, ClearEAPCertification()); |
| EXPECT_CALL(*interface_proxy, RemoveNetwork(StrEq(kFirstNetworkPath))); |
| EXPECT_CALL(*mock_eap_service_, eap()) |
| .WillOnce(Return(&mock_eap_credentials)); |
| EXPECT_CALL(mock_eap_credentials, PopulateSupplicantProperties(_, _, _, _)); |
| const char kSecondNetworkPath[] = "/network/second-path"; |
| EXPECT_CALL(*interface_proxy, AddNetwork(_)) |
| .WillOnce(Return(kSecondNetworkPath)); |
| EXPECT_CALL(*interface_proxy, SelectNetwork(StrEq(kSecondNetworkPath))); |
| EXPECT_CALL(*interface_proxy, EAPLogon()); |
| EXPECT_TRUE(InvokeStartEapAuthentication()); |
| EXPECT_EQ(kSecondNetworkPath, GetSupplicantNetworkPath()); |
| } |
| |
| TEST_F(EthernetTest, StopSupplicant) { |
| MockSupplicantProcessProxy *process_proxy = supplicant_process_proxy_.get(); |
| MockSupplicantInterfaceProxy *interface_proxy = |
| supplicant_interface_proxy_.get(); |
| StartSupplicant(); |
| SetIsEapAuthenticated(true); |
| SetSupplicantNetworkPath("/network/1"); |
| EXPECT_CALL(*interface_proxy, EAPLogoff()); |
| EXPECT_CALL(*process_proxy, RemoveInterface(StrEq(kInterfacePath))); |
| InvokeStopSupplicant(); |
| EXPECT_EQ(NULL, GetSupplicantInterfaceProxy()); |
| EXPECT_EQ(NULL, GetSupplicantProcessProxy()); |
| EXPECT_EQ("", GetSupplicantInterfacePath()); |
| EXPECT_EQ("", GetSupplicantNetworkPath()); |
| EXPECT_FALSE(GetIsEapAuthenticated()); |
| } |
| |
| TEST_F(EthernetTest, Certification) { |
| const string kSubjectName("subject-name"); |
| const uint32 kDepth = 123; |
| // Should not crash due to no service_. |
| TriggerCertification(kSubjectName, kDepth); |
| EXPECT_CALL(*mock_service_, AddEAPCertification(kSubjectName, kDepth)); |
| SetService(mock_service_); |
| TriggerCertification(kSubjectName, kDepth); |
| } |
| |
| } // namespace shill |