普通文本  |  631行  |  22.61 KB

//
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "shill/wifi/mac80211_monitor.h"

#include <vector>

#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "shill/mock_event_dispatcher.h"
#include "shill/mock_log.h"
#include "shill/mock_metrics.h"
#include "shill/net/mock_time.h"

using std::string;
using std::vector;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::HasSubstr;
using ::testing::Return;
using ::testing::SetArgumentPointee;
using ::testing::StrictMock;

namespace shill {

namespace {

const char kTestDeviceName[] = "test-dev";
const char kJunkData[] = "junk data";

}  // namespace

typedef Mac80211Monitor::QueueState QState;

class Mac80211MonitorTest : public testing::Test {
 public:
  Mac80211MonitorTest()
      : metrics_(nullptr),
        mac80211_monitor_(
            &event_dispatcher_,
            kTestDeviceName,
            kQueueLengthLimit,
            base::Bind(&Mac80211MonitorTest::OnRepairHandler,
                       base::Unretained(this)),
            &metrics_) {
    mac80211_monitor_.time_ = &time_;
  }
  virtual ~Mac80211MonitorTest() {}

 protected:
  static const size_t kQueueLengthLimit = 5;
  base::FilePath fake_queue_state_file_path_;  // Call FakeUpSysfs() first.
  base::FilePath fake_wake_queues_file_path_;  // Call FakeUpSysfs() first.

  // Getters for fixture fields.
  MockTime& time() { return time_; }
  MockEventDispatcher& event_dispatcher() { return event_dispatcher_; }
  MockMetrics& metrics() { return metrics_; }

  // Complex fixture methods.
  void AllowWakeQueuesIfNeededCommonCalls() {
    // Allow any number of these calls, as these aspects of
    // WakeQueuesIfNeeded interaction are tested elsewhere.
    EXPECT_CALL(event_dispatcher(), PostDelayedTask(_, _))
        .Times(AnyNumber());
    EXPECT_CALL(metrics(), SendEnumToUMA(_, _, _))
        .Times(AnyNumber());
    EXPECT_CALL(metrics(), SendToUMA(_, _, _, _, _))
        .Times(AnyNumber());
  }
  void FakeUpNotStuckState() {
    FakeUpQueueFiles("00: 0x00000000/10\n");
  }
  void FakeUpStuckByDriverState() {
    FakeUpQueueFiles("00: 0x00000001/10\n");
  }
  void FakeUpStuckByPowerSaveState() {
    FakeUpQueueFiles("00: 0x00000002/10\n");
  }
  void FakeUpSysfs() {
    CHECK(fake_sysfs_tree_.CreateUniqueTempDir());
    CHECK(base::CreateTemporaryFileInDir(
        fake_sysfs_tree_.path(), &fake_queue_state_file_path_));
    CHECK(base::CreateTemporaryFileInDir(
        fake_sysfs_tree_.path(), &fake_wake_queues_file_path_));
    PlumbFakeSysfs();
  }
  void DeleteQueueStateFile() {
    fake_queue_state_file_path_.clear();
    PlumbFakeSysfs();
  }
  bool IsRunning() const {
    return mac80211_monitor_.is_running_ &&
        !mac80211_monitor_.check_queues_callback_.IsCancelled();
  }
  bool IsStopped() const {
    return !mac80211_monitor_.is_running_ &&
        mac80211_monitor_.check_queues_callback_.IsCancelled();
  }
  bool IsWakeQueuesFileModified() const {
    CHECK(fake_sysfs_tree_.IsValid());  // Keep tests hermetic.
    string wake_file_contents;
    base::ReadFileToString(fake_wake_queues_file_path_, &wake_file_contents);
    return wake_file_contents != kJunkData;
  }
  MOCK_METHOD0(OnRepairHandler, void());

  // Getters for Mac80211Monitor state.
  bool GetIsDeviceConnected() const {
    return mac80211_monitor_.is_device_connected_;
  }
  time_t GetLastWokeQueuesMonotonicSeconds() const {
    return mac80211_monitor_.last_woke_queues_monotonic_seconds_;
  }
  const string& GetLinkName() const {
    return mac80211_monitor_.link_name_;
  }
  time_t GetMinimumTimeBetweenWakesSeconds() const {
    return Mac80211Monitor::kMinimumTimeBetweenWakesSeconds;
  }
  const string& GetPhyName() const {
    return mac80211_monitor_.phy_name_;
  }
  const base::FilePath& GetQueueStateFilePath() const {
    return mac80211_monitor_.queue_state_file_path_;
  }
  const base::FilePath& GetWakeQueuesFilePath() const {
    return mac80211_monitor_.wake_queues_file_path_;
  }

  // Pass-through methods to Mac80211Monitor methods.
  void StartMonitor(const string& phy_name) {
    EXPECT_CALL(
        event_dispatcher_,
        PostDelayedTask(
            _, Mac80211Monitor::kQueueStatePollIntervalSeconds * 1000));
    mac80211_monitor_.Start(phy_name);
    if (fake_sysfs_tree_.IsValid()) {
      PlumbFakeSysfs();  // Re-plumb, since un-plumbed by Start().
    }
  }
  void StopMonitor() {
    mac80211_monitor_.Stop();
  }
  uint32_t CheckAreQueuesStuck(const vector<QState>& queue_states) {
    return mac80211_monitor_.CheckAreQueuesStuck(queue_states);
  }
  void UpdateConnectedState(bool new_state) {
    mac80211_monitor_.UpdateConnectedState(new_state);
  }
  void WakeQueuesIfNeeded() {
    CHECK(fake_sysfs_tree_.IsValid());  // Keep tests hermetic.
    mac80211_monitor_.WakeQueuesIfNeeded();
  }

 private:
  base::ScopedTempDir fake_sysfs_tree_;  // Call FakeUpSysfs() first.
  StrictMock<MockEventDispatcher> event_dispatcher_;
  StrictMock<MockMetrics> metrics_;
  StrictMock<MockTime> time_;
  Mac80211Monitor mac80211_monitor_;

  void FakeUpQueueFiles(const string& queue_state_string) {
    CHECK(fake_sysfs_tree_.IsValid());  // Keep tests hermetic.
    base::WriteFile(fake_queue_state_file_path_,
                    queue_state_string.c_str(),
                    queue_state_string.length());
    ASSERT_TRUE(base::WriteFile(fake_wake_queues_file_path_,
                                kJunkData, strlen(kJunkData)));
  }
  void PlumbFakeSysfs() {
    mac80211_monitor_.queue_state_file_path_ = fake_queue_state_file_path_;
    mac80211_monitor_.wake_queues_file_path_ = fake_wake_queues_file_path_;
  }
};

// Can't be in an anonymous namespace, due to ADL.
// Instead, we use static to constain visibility to this unit.
static bool operator==(const QState& a, const QState& b) {
  return a.queue_number == b.queue_number &&
      a.stop_flags == b.stop_flags &&
      a.queue_length == b.queue_length;
}

TEST_F(Mac80211MonitorTest, Ctor) {
  EXPECT_TRUE(IsStopped());
  EXPECT_EQ(kTestDeviceName, GetLinkName());
}

TEST_F(Mac80211MonitorTest, Start) {
  StartMonitor("test-phy");
  EXPECT_TRUE(IsRunning());
  EXPECT_EQ("test-phy", GetPhyName());
  EXPECT_EQ("/sys/kernel/debug/ieee80211/test-phy/queues",
            GetQueueStateFilePath().value());
  EXPECT_EQ("/sys/kernel/debug/ieee80211/test-phy/wake_queues",
            GetWakeQueuesFilePath().value());
  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());
}

TEST_F(Mac80211MonitorTest, Stop) {
  StartMonitor("dont-care-phy");
  EXPECT_TRUE(IsRunning());
  StopMonitor();
  EXPECT_TRUE(IsStopped());
}

TEST_F(Mac80211MonitorTest, UpdateConnectedState) {
  UpdateConnectedState(false);
  EXPECT_FALSE(GetIsDeviceConnected());

  UpdateConnectedState(true);
  EXPECT_TRUE(GetIsDeviceConnected());

  // Initial state was unknown. Ensure that we can move from true to false.
  UpdateConnectedState(false);
  EXPECT_FALSE(GetIsDeviceConnected());
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededFullMacDevice) {
  FakeUpSysfs();
  StartMonitor("dont-care-phy");
  UpdateConnectedState(false);
  EXPECT_CALL(event_dispatcher(), PostDelayedTask(_, _));
  ScopedMockLog log;
  EXPECT_CALL(log, Log(_, _, HasSubstr(": incomplete read on "))).Times(0);

  // In case of using device with Full-Mac support,
  // there is no queue state file in debugfs.
  DeleteQueueStateFile();
  WakeQueuesIfNeeded();
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededRearmsTimerWhenDisconnected) {
  FakeUpSysfs();
  StartMonitor("dont-care-phy");
  UpdateConnectedState(false);
  EXPECT_CALL(event_dispatcher(), PostDelayedTask(_, _));
  WakeQueuesIfNeeded();
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededFailToReadQueueState) {
  FakeUpSysfs();
  StartMonitor("dont-care-phy");
  UpdateConnectedState(false);
  AllowWakeQueuesIfNeededCommonCalls();
  WakeQueuesIfNeeded();

  // In case we succeeded reading queue state before, but fail this time.
  ScopedMockLog log;
  EXPECT_CALL(log, Log(_, _, HasSubstr(": incomplete read on "))).Times(1);
  DeleteQueueStateFile();
  WakeQueuesIfNeeded();
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededRearmsTimerWhenConnected) {
  FakeUpSysfs();
  StartMonitor("dont-care-phy");
  UpdateConnectedState(true);
  EXPECT_CALL(event_dispatcher(), PostDelayedTask(_, _));
  WakeQueuesIfNeeded();
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededWakeNeeded) {
  FakeUpSysfs();
  FakeUpStuckByPowerSaveState();
  StartMonitor("dont-care-phy");
  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());

  const time_t kNowMonotonicSeconds = GetMinimumTimeBetweenWakesSeconds();
  EXPECT_CALL(time(), GetSecondsMonotonic(_))
      .WillOnce(DoAll(SetArgumentPointee<0>(kNowMonotonicSeconds),
                      Return(true)));
  EXPECT_CALL(*this, OnRepairHandler());
  AllowWakeQueuesIfNeededCommonCalls();
  WakeQueuesIfNeeded();

  EXPECT_EQ(kNowMonotonicSeconds, GetLastWokeQueuesMonotonicSeconds());
  EXPECT_TRUE(IsWakeQueuesFileModified());
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededRateLimiting) {
  FakeUpSysfs();
  FakeUpStuckByPowerSaveState();
  StartMonitor("dont-care-phy");
  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());

  EXPECT_CALL(time(), GetSecondsMonotonic(_))
      .WillOnce(DoAll(
          SetArgumentPointee<0>(GetMinimumTimeBetweenWakesSeconds() - 1),
          Return(true)));
  EXPECT_CALL(*this, OnRepairHandler()).Times(0);
  AllowWakeQueuesIfNeededCommonCalls();
  WakeQueuesIfNeeded();

  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());
  EXPECT_FALSE(IsWakeQueuesFileModified());
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededNotStuck) {
  FakeUpSysfs();
  FakeUpNotStuckState();
  StartMonitor("dont-care-phy");
  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());

  EXPECT_CALL(*this, OnRepairHandler()).Times(0);
  AllowWakeQueuesIfNeededCommonCalls();
  WakeQueuesIfNeeded();

  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());
  EXPECT_FALSE(IsWakeQueuesFileModified());
}

TEST_F(Mac80211MonitorTest, WakeQueuesIfNeededStuckByDriver) {
  FakeUpSysfs();
  FakeUpStuckByDriverState();
  StartMonitor("dont-care-phy");
  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());

  EXPECT_CALL(*this, OnRepairHandler()).Times(0);
  AllowWakeQueuesIfNeededCommonCalls();
  WakeQueuesIfNeeded();

  EXPECT_EQ(0, GetLastWokeQueuesMonotonicSeconds());
  EXPECT_FALSE(IsWakeQueuesFileModified());
}

TEST_F(Mac80211MonitorTest, ParseQueueStateSimple) {
  // Single queue.
  EXPECT_THAT(Mac80211Monitor::ParseQueueState("00: 0x00000000/0\n"),
              ElementsAre(QState(0, 0, 0)));

  // Multiple queues, non-empty.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "00: 0x00000000/10\n"
          "01: 0x00000000/20\n"),
      ElementsAre(QState(0, 0, 10), QState(1, 0, 20)));
}

TEST_F(Mac80211MonitorTest, ParseQueueStateStopped) {
  // Single queue, stopped for various reasons.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x00000001/10\n"),
      ElementsAre(QState(0, Mac80211Monitor::kStopFlagDriver, 10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x00000003/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave,
                         10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x00000007/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave |
                         Mac80211Monitor::kStopFlagChannelSwitch,
                         10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x0000000f/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave |
                         Mac80211Monitor::kStopFlagChannelSwitch |
                         Mac80211Monitor::kStopFlagAggregation,
                         10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x0000001f/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave |
                         Mac80211Monitor::kStopFlagChannelSwitch |
                         Mac80211Monitor::kStopFlagAggregation |
                         Mac80211Monitor::kStopFlagSuspend,
                         10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x0000003f/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave |
                         Mac80211Monitor::kStopFlagChannelSwitch |
                         Mac80211Monitor::kStopFlagAggregation |
                         Mac80211Monitor::kStopFlagSuspend |
                         Mac80211Monitor::kStopFlagBufferAdd,
                         10)));
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState("00: 0x0000007f/10\n"),
      ElementsAre(QState(0,
                         Mac80211Monitor::kStopFlagDriver |
                         Mac80211Monitor::kStopFlagPowerSave |
                         Mac80211Monitor::kStopFlagChannelSwitch |
                         Mac80211Monitor::kStopFlagAggregation |
                         Mac80211Monitor::kStopFlagSuspend |
                         Mac80211Monitor::kStopFlagBufferAdd |
                         Mac80211Monitor::kStopFlagChannelTypeChange,
                         10)));
}

TEST_F(Mac80211MonitorTest, ParseQueueStateBadInput) {
  // Empty input -> Empty output.
  EXPECT_TRUE(Mac80211Monitor::ParseQueueState("").empty());

  // Missing queue length for queue 0.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "00: 0x00000000\n"
          "01: 0xffffffff/10\n"),
      ElementsAre(QState(1, 0xffffffff, 10)));

  // Missing flags for queue 0.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "00: 0\n"
          "01: 0xffffffff/10\n"),
      ElementsAre(QState(1, 0xffffffff, 10)));

  // Bad number for queue 0.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "aa: 0xabcdefgh/0\n"
          "01: 0xffffffff/10\n"),
      ElementsAre(QState(1, 0xffffffff, 10)));

  // Bad flags for queue 0.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "00: 0xabcdefgh/0\n"
          "01: 0xffffffff/10\n"),
      ElementsAre(QState(1, 0xffffffff, 10)));

  // Bad length for queue 0.
  EXPECT_THAT(
      Mac80211Monitor::ParseQueueState(
          "00: 0x00000000/-1\n"
          "01: 0xffffffff/10\n"),
      ElementsAre(QState(1, 0xffffffff, 10)));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckNotStuck) {
  EXPECT_FALSE(CheckAreQueuesStuck({}));
  EXPECT_FALSE(CheckAreQueuesStuck({QState(0, 0, 0)}));
  // Not stuck when queue length is below limit.
  EXPECT_FALSE(CheckAreQueuesStuck({
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-1)}));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckSingleReason) {
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonDriver,
      Mac80211Monitor::kStopReasonMax));
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonPowerSave,
      Mac80211Monitor::kStopReasonMax));
  EXPECT_CALL(metrics(), SendToUMA(
      Metrics::kMetricWifiStoppedTxQueueLength,
      kQueueLengthLimit,
      Metrics::kMetricWifiStoppedTxQueueLengthMin,
      Metrics::kMetricWifiStoppedTxQueueLengthMax,
      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(2);
  EXPECT_EQ(Mac80211Monitor::kStopFlagDriver,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagDriver,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit)}));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckMultipleReasons) {
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonPowerSave,
      Mac80211Monitor::kStopReasonMax)).Times(2);
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonDriver,
      Mac80211Monitor::kStopReasonMax)).Times(2);
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonChannelSwitch,
      Mac80211Monitor::kStopReasonMax)).Times(2);
  EXPECT_CALL(metrics(), SendToUMA(
      Metrics::kMetricWifiStoppedTxQueueLength,
      kQueueLengthLimit,
      Metrics::kMetricWifiStoppedTxQueueLengthMin,
      Metrics::kMetricWifiStoppedTxQueueLengthMax,
      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(3);
  EXPECT_EQ(Mac80211Monitor::kStopFlagDriver |
            Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagDriver |
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave |
            Mac80211Monitor::kStopFlagChannelSwitch,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave |
                       Mac80211Monitor::kStopFlagChannelSwitch,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagDriver |
            Mac80211Monitor::kStopFlagChannelSwitch,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagDriver |
                       Mac80211Monitor::kStopFlagChannelSwitch,
                       kQueueLengthLimit)}));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckMultipleQueues) {
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonPowerSave,
      Mac80211Monitor::kStopReasonMax)).Times(5);
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonDriver,
      Mac80211Monitor::kStopReasonMax)).Times(2);
  EXPECT_CALL(metrics(), SendToUMA(
      Metrics::kMetricWifiStoppedTxQueueLength,
      kQueueLengthLimit,
      Metrics::kMetricWifiStoppedTxQueueLengthMin,
      Metrics::kMetricWifiStoppedTxQueueLengthMax,
      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(5);
  EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0, 0, 0),
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit),
                QState(0, 0, 0)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit),
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagDriver |
            Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit),
                QState(0,
                       Mac80211Monitor::kStopFlagDriver,
                       kQueueLengthLimit)}));
  EXPECT_EQ(Mac80211Monitor::kStopFlagDriver |
            Mac80211Monitor::kStopFlagPowerSave,
            CheckAreQueuesStuck({
                QState(0, Mac80211Monitor::kStopFlagDriver, kQueueLengthLimit),
                QState(0,
                       Mac80211Monitor::kStopFlagPowerSave,
                       kQueueLengthLimit)}));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckQueueLength) {
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonPowerSave,
      Mac80211Monitor::kStopReasonMax)).Times(4);
  EXPECT_CALL(metrics(), SendToUMA(
      Metrics::kMetricWifiStoppedTxQueueLength,
      kQueueLengthLimit,
      Metrics::kMetricWifiStoppedTxQueueLengthMin,
      Metrics::kMetricWifiStoppedTxQueueLengthMax,
      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(4);
  EXPECT_TRUE(CheckAreQueuesStuck({
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit)}));
  EXPECT_TRUE(CheckAreQueuesStuck({
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-2),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-1),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit)}));
  EXPECT_TRUE(CheckAreQueuesStuck({
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-1),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-2)}));
  EXPECT_TRUE(CheckAreQueuesStuck({
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-1),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-2)}));
}

TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckQueueLengthIgnoresUnstopped) {
  EXPECT_CALL(metrics(), SendEnumToUMA(
      Metrics::kMetricWifiStoppedTxQueueReason,
      Mac80211Monitor::kStopReasonPowerSave,
      Mac80211Monitor::kStopReasonMax));
  EXPECT_CALL(metrics(), SendToUMA(
      Metrics::kMetricWifiStoppedTxQueueLength,
      kQueueLengthLimit,
      Metrics::kMetricWifiStoppedTxQueueLengthMin,
      Metrics::kMetricWifiStoppedTxQueueLengthMax,
      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets));
  EXPECT_TRUE(CheckAreQueuesStuck({
        QState(0, 0, kQueueLengthLimit * 10),
        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit)}));
}

}  // namespace shill