blob: a94585e34374db2e5db77168b565f04632905d0b [file] [log] [blame]
// Copyright 2021 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/wifi/wifi_cqm.h"
#include <memory>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "shill/metrics.h"
#include "shill/mock_control.h"
#include "shill/mock_log.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
#include "shill/net/netlink_packet.h"
#include "shill/net/nl80211_attribute.h"
#include "shill/net/nl80211_message.h"
#include "shill/refptr_types.h"
#include "shill/test_event_dispatcher.h"
#include "shill/wifi/mock_wake_on_wifi.h"
#include "shill/wifi/mock_wifi.h"
#include "shill/wifi/wifi.h"
using ::testing::_;
using ::testing::HasSubstr;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
namespace shill {
namespace {
// Fake MAC address.
constexpr char kDeviceAddress[] = "aabbccddeeff";
const uint16_t kNl80211FamilyId = 0x13;
// Bytes representing NL80211 CQM message to pass to unit tests.
const uint8_t kCQMBeaconLossNLMsg[] = {
0x2c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x08, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x02,
0x00, 0x00, 0x00, 0x08, 0x00, 0x5e, 0x00, 0x04, 0x00, 0x08, 0x00};
const uint8_t kCQMRssiLowNLMsg[] = {
0x38, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x14, 0x00, 0x5e, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x09, 0x00, 0xba, 0xff, 0xff, 0xff};
const uint8_t kCQMRssiHighNLMsg[] = {
0x38, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x14, 0x00, 0x5e, 0x00, 0x08, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
0x08, 0x00, 0x09, 0x00, 0xd8, 0xff, 0xff, 0xff};
const uint8_t kCQMPacketLossNLMsg[] = {
0x30, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x5e, 0x00, 0x08, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00};
} // namespace
class WiFiCQMTest : public ::testing::Test {
public:
WiFiCQMTest()
: manager_(&control_interface_, &dispatcher_, &metrics_),
wifi_(new MockWiFi(
&manager_, "wifi", kDeviceAddress, 0, new MockWakeOnWiFi())),
wifi_cqm_(new WiFiCQM(&metrics_, wifi().get())) {
Nl80211Message::SetMessageType(kNl80211FamilyId);
}
void SetUp() override {
CHECK(temp_dir_.CreateUniqueTempDir());
base::FilePath fw_dump_path =
temp_dir_.GetPath()
.Append("sys/kernel/debug/ieee80211/phy0/iwlwifi/iwlmvm")
.Append("fw_dbg_collect");
EXPECT_TRUE(base::CreateDirectory(fw_dump_path.DirName()));
EXPECT_TRUE(base::WriteFile(fw_dump_path, "1", 1));
EXPECT_TRUE(base::PathExists(fw_dump_path));
wifi_cqm_->fw_dump_path_ = fw_dump_path;
}
void TearDown() override { EXPECT_TRUE(temp_dir_.Delete()); }
~WiFiCQMTest() override = default;
protected:
// Used to create a virtual sysfs path.
base::ScopedTempDir temp_dir_;
MockMetrics* metrics() { return &metrics_; }
void OnCQMNotify(const Nl80211Message& nl80211_message) {
wifi_cqm_->OnCQMNotify(nl80211_message);
}
void TriggerFwDump() { return wifi_cqm_->TriggerFwDump(); }
int FwDumpCount() { return wifi_cqm_->fw_dump_count_; }
scoped_refptr<MockWiFi> wifi() { return wifi_; }
private:
MockMetrics metrics_;
MockControl control_interface_;
EventDispatcherForTest dispatcher_;
MockManager manager_;
scoped_refptr<MockWiFi> wifi_;
std::unique_ptr<WiFiCQM> wifi_cqm_;
};
TEST_F(WiFiCQMTest, TriggerFwDump) {
ScopedMockLog log;
ScopeLogger::GetInstance()->EnableScopesByName("wifi");
ScopeLogger::GetInstance()->set_verbose_level(3);
EXPECT_CALL(log, Log(_, _, HasSubstr("Triggering FW dump")));
EXPECT_CALL(log, Log(_, _, HasSubstr("FW dump trigger succeeded")));
TriggerFwDump();
EXPECT_EQ(FwDumpCount(), 1);
Mock::VerifyAndClearExpectations(&log);
EXPECT_CALL(log, Log(_, _, HasSubstr("In FW dump cool down period")));
TriggerFwDump();
// No new FW dump should be triggered in cool down period, so this should
// still be one.
EXPECT_EQ(FwDumpCount(), 1);
ScopeLogger::GetInstance()->set_verbose_level(0);
ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
}
TEST_F(WiFiCQMTest, OnCQMNotificationBeaconLoss) {
NotifyCqmMessage msg;
ScopedMockLog log;
ScopeLogger::GetInstance()->EnableScopesByName("wifi");
ScopeLogger::GetInstance()->set_verbose_level(3);
NetlinkPacket packet(kCQMBeaconLossNLMsg, sizeof(kCQMBeaconLossNLMsg));
msg.InitFromPacket(&packet, NetlinkMessage::MessageContext());
const Nl80211Message& nl80211_msg =
*reinterpret_cast<const Nl80211Message*>(&msg);
EXPECT_CALL(log, Log(_, _, HasSubstr("Beacon loss observed")));
EXPECT_CALL(log, Log(_, _, HasSubstr("Triggering FW dump")));
EXPECT_CALL(log, Log(_, _, HasSubstr("FW dump trigger succeeded")));
EXPECT_CALL(*wifi(), GetSignalLevelForActiveService()).WillOnce(Return(-50));
EXPECT_CALL(*metrics(), SendEnumToUMA(Metrics::kMetricWiFiCQMNotification,
Metrics::kWiFiCQMBeaconLoss, _));
OnCQMNotify(nl80211_msg);
EXPECT_EQ(FwDumpCount(), 1);
Mock::VerifyAndClearExpectations(wifi().get());
Mock::VerifyAndClearExpectations(&log);
// No Fw dump triggered for signal strength less than -80dBm.
EXPECT_CALL(
log,
Log(_, _,
HasSubstr("CQM notification for signal strength less than -80 dBm")));
EXPECT_CALL(*wifi(), GetSignalLevelForActiveService()).WillOnce(Return(-90));
OnCQMNotify(nl80211_msg);
// FW dump count should not increase.
EXPECT_EQ(FwDumpCount(), 1);
ScopeLogger::GetInstance()->set_verbose_level(0);
ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
}
TEST_F(WiFiCQMTest, OnCQMNotificationLowRssiLevelBreach) {
NotifyCqmMessage msg;
ScopedMockLog log;
ScopeLogger::GetInstance()->EnableScopesByName("wifi");
ScopeLogger::GetInstance()->set_verbose_level(3);
NetlinkPacket packet(kCQMRssiLowNLMsg, sizeof(kCQMRssiLowNLMsg));
msg.InitFromPacket(&packet, NetlinkMessage::MessageContext());
const Nl80211Message& nl80211_msg =
*reinterpret_cast<const Nl80211Message*>(&msg);
EXPECT_CALL(
log, Log(_, _,
HasSubstr("NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT event found")));
OnCQMNotify(nl80211_msg);
EXPECT_EQ(FwDumpCount(), 0);
ScopeLogger::GetInstance()->set_verbose_level(0);
ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
}
TEST_F(WiFiCQMTest, OnCQMNotificationHighRssiLevelBreach) {
NotifyCqmMessage msg;
ScopedMockLog log;
ScopeLogger::GetInstance()->EnableScopesByName("wifi");
ScopeLogger::GetInstance()->set_verbose_level(3);
NetlinkPacket packet(kCQMRssiHighNLMsg, sizeof(kCQMRssiHighNLMsg));
msg.InitFromPacket(&packet, NetlinkMessage::MessageContext());
const Nl80211Message& nl80211_msg =
*reinterpret_cast<const Nl80211Message*>(&msg);
EXPECT_CALL(
log, Log(_, _,
HasSubstr("NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT event found")));
OnCQMNotify(nl80211_msg);
EXPECT_EQ(FwDumpCount(), 0);
ScopeLogger::GetInstance()->set_verbose_level(0);
ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
}
TEST_F(WiFiCQMTest, OnCQMNotificationPacketLoss) {
NotifyCqmMessage msg;
ScopedMockLog log;
ScopeLogger::GetInstance()->EnableScopesByName("wifi");
ScopeLogger::GetInstance()->set_verbose_level(3);
NetlinkPacket packet(kCQMPacketLossNLMsg, sizeof(kCQMPacketLossNLMsg));
msg.InitFromPacket(&packet, NetlinkMessage::MessageContext());
const Nl80211Message& nl80211_msg =
*reinterpret_cast<const Nl80211Message*>(&msg);
EXPECT_CALL(log, Log(_, _, HasSubstr("Packet loss event received")));
EXPECT_CALL(log, Log(_, _, HasSubstr("Triggering FW dump")));
EXPECT_CALL(log, Log(_, _, HasSubstr("FW dump trigger succeeded")));
EXPECT_CALL(*wifi(), GetSignalLevelForActiveService()).WillOnce(Return(-50));
EXPECT_CALL(*metrics(), SendEnumToUMA(Metrics::kMetricWiFiCQMNotification,
Metrics::kWiFiCQMPacketLoss, _));
OnCQMNotify(nl80211_msg);
EXPECT_EQ(FwDumpCount(), 1);
Mock::VerifyAndClearExpectations(wifi().get());
Mock::VerifyAndClearExpectations(&log);
// No Fw dump triggered for signal strength less than -80dBm.
EXPECT_CALL(
log,
Log(_, _,
HasSubstr("CQM notification for signal strength less than -80 dBm")));
EXPECT_CALL(*wifi(), GetSignalLevelForActiveService()).WillOnce(Return(-90));
OnCQMNotify(nl80211_msg);
// FW dump count should not increase.
EXPECT_EQ(FwDumpCount(), 1);
ScopeLogger::GetInstance()->set_verbose_level(0);
ScopeLogger::GetInstance()->EnableScopesByName("-wifi");
}
} // namespace shill