// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/diagnostics/networking_log.h"
#include <vector>
#include "ash/system/diagnostics/log_test_helpers.h"
#include "ash/webui/diagnostics_ui/mojom/network_health_provider.mojom.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace diagnostics {
namespace {
constexpr char kNetworkInfoHeader[] = "--- Network Info ---";
mojom::NetworkPtr CreateWiFiNetworkPtr(uint32_t signal_strength,
uint32_t frequency,
const std::string& ssid,
const std::string& bssid,
uint32_t routing_prefix,
const std::string& gateway,
const std::string& ip_address,
std::vector<std::string>&& name_servers,
const std::string& guid,
const std::string& name,
const std::string& mac_address,
mojom::SecurityType security) {
auto wifi_props = mojom::WiFiStateProperties::New(signal_strength, frequency,
ssid, bssid, security);
auto type_props =
mojom::NetworkTypeProperties::NewWifi(std::move(wifi_props));
auto ip_config = mojom::IPConfigProperties::New(
std::move(name_servers), routing_prefix, gateway, ip_address);
return mojom::Network::New(mojom::NetworkState::kOnline,
mojom::NetworkType::kWiFi, std::move(type_props),
guid, name, mac_address, std::move(ip_config));
}
mojom::NetworkPtr CreateEthernetNetworkPtr(
const std::string& guid,
const std::string& name,
const std::string& mac_address,
const mojom::AuthenticationType& authentication) {
auto ethernet_props = mojom::EthernetStateProperties::New(authentication);
auto type_props =
mojom::NetworkTypeProperties::NewEthernet(std::move(ethernet_props));
return mojom::Network::New(mojom::NetworkState::kOnline,
mojom::NetworkType::kEthernet,
std::move(type_props), guid, name, mac_address,
mojom::IPConfigProperties::New());
}
mojom::NetworkPtr CreateCellularNetworkPtr(
const std::string& guid,
const std::string& name,
const std::string& mac_address,
const std::string& iccid,
const std::string& eid,
const std::string& network_technology,
bool roaming,
mojom::RoamingState roaming_state,
const uint32_t signal_strength,
bool sim_locked,
mojom::LockType lock_type) {
auto cellular_props = mojom::CellularStateProperties::New(
iccid, eid, network_technology, roaming, roaming_state, signal_strength,
sim_locked, lock_type);
auto type_props =
mojom::NetworkTypeProperties::NewCellular(std::move(cellular_props));
return mojom::Network::New(mojom::NetworkState::kOnline,
mojom::NetworkType::kCellular,
std::move(type_props), guid, name, mac_address,
mojom::IPConfigProperties::New());
}
// Splits `line` on '-' ignoring the first part which is the date, and verifies
// that the second half of the line equals `expected_message`.
void ExpectCorrectLogLine(const std::string& expected_message,
const std::string& line) {
const std::vector<std::string> event_parts = GetLogLineContents(line);
EXPECT_EQ(2u, event_parts.size());
EXPECT_EQ(expected_message, event_parts[1]);
}
} // namespace
class NetworkingLogTest : public testing::Test {
public:
NetworkingLogTest() { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); }
~NetworkingLogTest() override = default;
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::ScopedTempDir temp_dir_;
};
TEST_F(NetworkingLogTest, DetailedLogContentsWiFi) {
const uint32_t expected_signal_strength = 99;
const uint16_t expected_frequency = 5785;
const std::string expected_ssid = "ssid";
const std::string expected_bssid = "bssid";
const std::string expected_subnet_mask = "128.0.0.0";
const std::string expected_gateway = "192.0.0.1";
const std::string expected_ip_address = "192.168.1.1";
const std::string expected_security_type = "WEP";
const std::string name_server1 = "192.168.1.100";
const std::string name_server2 = "192.168.1.101";
std::vector<std::string> expected_name_servers = {name_server1, name_server2};
const std::string expected_guid = "guid";
const std::string expected_name = "name";
const std::string expected_mac_address = "84:C5:A6:30:3F:31";
mojom::NetworkPtr test_info = CreateWiFiNetworkPtr(
expected_signal_strength, expected_frequency, expected_ssid,
expected_bssid, /*routing_prefix=*/1, expected_gateway,
expected_ip_address, std::move(expected_name_servers), expected_guid,
expected_name, expected_mac_address, mojom::SecurityType::kWepPsk);
NetworkingLog log(temp_dir_.GetPath());
log.UpdateNetworkList({expected_guid}, expected_guid);
log.UpdateNetworkState(test_info.Clone());
task_environment_.RunUntilIdle();
const std::string log_as_string = log.GetNetworkInfo();
const std::vector<std::string> log_lines = GetLogLines(log_as_string);
// Expect one title line and 14 content lines.
EXPECT_EQ(15u, log_lines.size());
EXPECT_EQ(kNetworkInfoHeader, log_lines[0]);
EXPECT_EQ("Name: " + expected_name, log_lines[1]);
EXPECT_EQ("Type: WiFi", log_lines[2]);
EXPECT_EQ("State: Online", log_lines[3]);
EXPECT_EQ("Active: True", log_lines[4]);
EXPECT_EQ("MAC Address: " + expected_mac_address, log_lines[5]);
EXPECT_EQ(
"Signal Strength: " + base::NumberToString(expected_signal_strength),
log_lines[6]);
EXPECT_EQ("Frequency: " + base::NumberToString(expected_frequency),
log_lines[7]);
EXPECT_EQ("SSID: " + expected_ssid, log_lines[8]);
EXPECT_EQ("BSSID: " + expected_bssid, log_lines[9]);
EXPECT_EQ("Security: " + expected_security_type, log_lines[10]);
EXPECT_EQ("Gateway: " + expected_gateway, log_lines[11]);
EXPECT_EQ("IP Address: " + expected_ip_address, log_lines[12]);
EXPECT_EQ("Name Servers: " + name_server1 + ", " + name_server2,
log_lines[13]);
EXPECT_EQ("Subnet Mask: " + expected_subnet_mask, log_lines[14]);
// Expect one title and one event for adding the network.
const std::string events_log = log.GetNetworkEvents();
const std::vector<std::string> events_lines = GetLogLines(events_log);
EXPECT_EQ(2u, events_lines.size());
EXPECT_EQ("--- Network Events ---", events_lines[0]);
const std::string expected_line =
"WiFi network [" + expected_mac_address + "] started in state Online";
ExpectCorrectLogLine(expected_line, events_lines[1]);
}
TEST_F(NetworkingLogTest, DetailedLogContentsEthernet) {
const std::string expected_guid = "guid";
const std::string expected_name = "name";
const std::string expected_mac_address = "84:C5:A6:30:3F:31";
const std::string expected_authentication = "EAP";
mojom::NetworkPtr test_info = CreateEthernetNetworkPtr(
expected_guid, expected_name, expected_mac_address,
mojom::AuthenticationType::k8021x);
NetworkingLog log(temp_dir_.GetPath());
log.UpdateNetworkList({expected_guid}, expected_guid);
log.UpdateNetworkState(test_info.Clone());
task_environment_.RunUntilIdle();
const std::string log_as_string = log.GetNetworkInfo();
const std::vector<std::string> log_lines = GetLogLines(log_as_string);
// Expect one title line and 10 content lines.
EXPECT_EQ(11u, log_lines.size());
EXPECT_EQ(kNetworkInfoHeader, log_lines[0]);
EXPECT_EQ("Name: " + expected_name, log_lines[1]);
EXPECT_EQ("Type: Ethernet", log_lines[2]);
EXPECT_EQ("State: Online", log_lines[3]);
EXPECT_EQ("Active: True", log_lines[4]);
EXPECT_EQ("MAC Address: " + expected_mac_address, log_lines[5]);
EXPECT_EQ("Authentication: " + expected_authentication, log_lines[6]);
// Expect one title and one event for adding the network.
const std::string events_log = log.GetNetworkEvents();
const std::vector<std::string> events_lines = GetLogLines(events_log);
EXPECT_EQ(2u, events_lines.size());
EXPECT_EQ("--- Network Events ---", events_lines[0]);
const std::string expected_line =
"Ethernet network [" + expected_mac_address + "] started in state Online";
ExpectCorrectLogLine(expected_line, events_lines[1]);
}
TEST_F(NetworkingLogTest, DetailedLogContentsCellular) {
const std::string expected_guid = "guid";
const std::string expected_name = "name";
const std::string expected_mac_address = "84:C5:A6:30:3F:31";
const std::string expected_iccid = "83948080007483825411";
const std::string expected_eid = "82099038007008862600508229159883";
const std::string expected_network_technology = "LTE";
const std::string expected_roaming = "False";
const std::string expected_roaming_state = "None";
const uint32_t expected_signal_strength = 89;
const std::string expected_sim_locked = "True";
const std::string expected_lock_type = "sim-pin";
mojom::NetworkPtr test_info = CreateCellularNetworkPtr(
expected_guid, expected_name, expected_mac_address, expected_iccid,
expected_eid, expected_network_technology, false,
mojom::RoamingState::kNone, expected_signal_strength, true,
mojom::LockType::kSimPin);
NetworkingLog log(temp_dir_.GetPath());
log.UpdateNetworkList({expected_guid}, expected_guid);
log.UpdateNetworkState(test_info.Clone());
task_environment_.RunUntilIdle();
const std::string log_as_string = log.GetNetworkInfo();
const std::vector<std::string> log_lines = GetLogLines(log_as_string);
// Expect one title line and 17 content lines.
EXPECT_EQ(18u, log_lines.size());
EXPECT_EQ(kNetworkInfoHeader, log_lines[0]);
EXPECT_EQ("Name: " + expected_name, log_lines[1]);
EXPECT_EQ("Type: Cellular", log_lines[2]);
EXPECT_EQ("State: Online", log_lines[3]);
EXPECT_EQ("Active: True", log_lines[4]);
EXPECT_EQ("MAC Address: " + expected_mac_address, log_lines[5]);
EXPECT_EQ("ICCID: " + expected_iccid, log_lines[6]);
EXPECT_EQ("EID: " + expected_eid, log_lines[7]);
EXPECT_EQ("Technology: " + expected_network_technology, log_lines[8]);
EXPECT_EQ("Roaming: " + expected_roaming, log_lines[9]);
EXPECT_EQ("Roaming State: " + expected_roaming_state, log_lines[10]);
EXPECT_EQ(
"Signal Strength: " + base::NumberToString(expected_signal_strength),
log_lines[11]);
EXPECT_EQ("SIM Locked: " + expected_sim_locked, log_lines[12]);
EXPECT_EQ("SIM Lock Type: " + expected_lock_type, log_lines[13]);
// Expect one title and one event for adding the network.
const std::string events_log = log.GetNetworkEvents();
const std::vector<std::string> events_lines = GetLogLines(events_log);
EXPECT_EQ(2u, events_lines.size());
EXPECT_EQ("--- Network Events ---", events_lines[0]);
const std::string expected_line =
"Cellular network [" + expected_mac_address + "] started in state Online";
ExpectCorrectLogLine(expected_line, events_lines[1]);
}
TEST_F(NetworkingLogTest, NetworkEvents) {
const std::string expected_guid = "guid";
const std::string expected_name = "name";
const std::string expected_mac_address = "84:C5:A6:30:3F:31";
mojom::NetworkPtr test_info = CreateEthernetNetworkPtr(
expected_guid, expected_name, expected_mac_address,
mojom::AuthenticationType::k8021x);
NetworkingLog log(temp_dir_.GetPath());
// Add the network.
log.UpdateNetworkList({expected_guid}, expected_guid);
log.UpdateNetworkState(test_info.Clone());
// Change the state of the network from Online to Disabled.
mojom::NetworkPtr new_state = test_info.Clone();
new_state->state = mojom::NetworkState::kDisabled;
log.UpdateNetworkState(std::move(new_state));
// Remove the network.
log.UpdateNetworkList({}, "expected_guid");
// Make sure all the updates are committed.
task_environment_.RunUntilIdle();
// Split the log for verification.
const std::string events_log = log.GetNetworkEvents();
const std::vector<std::string> events_lines = GetLogLines(events_log);
EXPECT_EQ(4u, events_lines.size());
// Verify section header.
size_t upto_line = 0;
EXPECT_EQ("--- Network Events ---", events_lines[upto_line++]);
// Verify add event.
std::string expected_line =
"Ethernet network [" + expected_mac_address + "] started in state Online";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify state change event.
expected_line = "Ethernet network [" + expected_mac_address +
"] changed state from Online to Disabled";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify remove event.
expected_line = "Ethernet network [" + expected_mac_address + "] removed";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
}
TEST_F(NetworkingLogTest, WiFiNetworkEvents) {
const uint32_t expected_signal_strength = 99;
const uint16_t expected_frequency = 5785;
const std::string expected_ssid = "ssid";
const std::string expected_bssid = "bssid";
const std::string expected_bssid_roamed = "bssid_roamed";
const std::string expected_subnet_mask = "128.0.0.0";
const std::string expected_gateway = "192.0.0.1";
const std::string expected_ip_address = "192.168.1.1";
const std::string expected_security_type = "WEP";
const std::string name_server1 = "192.168.1.100";
const std::string name_server2 = "192.168.1.101";
std::vector<std::string> expected_name_servers = {name_server1, name_server2};
const std::string expected_guid = "guid";
const std::string expected_name = "name";
const std::string expected_mac_address = "84:C5:A6:30:3F:31";
mojom::NetworkPtr test_info = CreateWiFiNetworkPtr(
expected_signal_strength, expected_frequency, expected_ssid,
expected_bssid, /*routing_prefix=*/1, expected_gateway,
expected_ip_address, std::move(expected_name_servers), expected_guid,
expected_name, expected_mac_address, mojom::SecurityType::kWepPsk);
NetworkingLog log(temp_dir_.GetPath());
// Add the network.
log.UpdateNetworkList({expected_guid}, expected_guid);
log.UpdateNetworkState(test_info.Clone());
// Leave the WiFi network.
mojom::NetworkPtr new_state = test_info.Clone();
new_state->state = mojom::NetworkState::kNotConnected;
new_state->type_properties->get_wifi()->ssid = "";
log.UpdateNetworkState(std::move(new_state));
// Rejoin the WiFi network.
new_state = test_info.Clone();
new_state->state = mojom::NetworkState::kOnline;
new_state->type_properties->get_wifi()->ssid = expected_ssid;
log.UpdateNetworkState(std::move(new_state));
// Roam to a new access point.
new_state = test_info.Clone();
new_state->state = mojom::NetworkState::kOnline;
new_state->type_properties->get_wifi()->bssid = expected_bssid_roamed;
log.UpdateNetworkState(std::move(new_state));
// Remove the network.
log.UpdateNetworkList({}, "expected_guid");
// Make sure all the updates are committed.
task_environment_.RunUntilIdle();
// Split the log for verification.
const std::string events_log = log.GetNetworkEvents();
const std::vector<std::string> events_lines = GetLogLines(events_log);
EXPECT_EQ(8u, events_lines.size());
// Verify section header.
size_t upto_line = 0;
EXPECT_EQ("--- Network Events ---", events_lines[upto_line++]);
// Verify add event.
std::string expected_line =
"WiFi network [" + expected_mac_address + "] started in state Online";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify network leave event.
expected_line = "WiFi network [" + expected_mac_address + "] left SSID '" +
expected_ssid + "'";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify state change event.
expected_line = "WiFi network [" + expected_mac_address +
"] changed state from Online to Not Connected";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify network join event.
expected_line = "WiFi network [" + expected_mac_address + "] joined SSID '" +
expected_ssid + "' on access point [" + expected_bssid + "]";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify state change event.
expected_line = "WiFi network [" + expected_mac_address +
"] changed state from Not Connected to Online";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify access point roam event.
expected_line = "WiFi network [" + expected_mac_address + "] on SSID '" +
expected_ssid + "' roamed from access point [" +
expected_bssid + "] to [" + expected_bssid_roamed + "]";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
// Verify remove event.
expected_line = "WiFi network [" + expected_mac_address + "] removed";
ExpectCorrectLogLine(expected_line, events_lines[upto_line++]);
}
TEST_F(NetworkingLogTest, NetworkPtrInvalidDoesNotCrash) {
mojom::NetworkPtr null_network;
NetworkingLog log(temp_dir_.GetPath());
const std::vector<std::string> observer_guids;
EXPECT_NO_FATAL_FAILURE(
log.UpdateNetworkList(observer_guids, /**active_guid=*/""));
EXPECT_TRUE(null_network.is_null());
EXPECT_NO_FATAL_FAILURE(log.UpdateNetworkState(std::move(null_network)));
// Ensure AsyncLog tasks complete.
task_environment_.RunUntilIdle();
// No networks, only header should be logged.
const std::vector<std::string> logged_network_info_1 =
ash::diagnostics::GetLogLines(log.GetNetworkInfo());
EXPECT_EQ(1u, logged_network_info_1.size());
EXPECT_EQ(kNetworkInfoHeader, logged_network_info_1[0]);
// LogRemoveNetwork should not crash if NetworkPtr null.
mojom::NetworkPtr removed_network =
CreateEthernetNetworkPtr("fake_guid", "eth0", "00:AA:11:BB:22:CC",
mojom::AuthenticationType::kNone);
EXPECT_NO_FATAL_FAILURE(log.UpdateNetworkState(std::move(removed_network)));
EXPECT_NO_FATAL_FAILURE(
log.UpdateNetworkList(observer_guids, /**active_guid=*/""));
// Ensure AsyncLog tasks complete.
task_environment_.RunUntilIdle();
// No networks, only header should be logged.
const std::vector<std::string> logged_network_info_2 =
ash::diagnostics::GetLogLines(log.GetNetworkInfo());
EXPECT_EQ(1u, logged_network_info_2.size());
EXPECT_EQ(kNetworkInfoHeader, logged_network_info_2[0]);
}
} // namespace diagnostics
} // namespace ash