// Copyright (c) 2012 The Chromium 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 "content/browser/system_message_window_win.h"

#include <dbt.h>
#include <string>
#include <vector>

#include "base/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/sys_string_conversions.h"
#include "base/system_monitor/system_monitor.h"
#include "base/test/mock_devices_changed_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

LRESULT GetVolumeName(LPCWSTR drive,
                      LPWSTR volume_name,
                      unsigned int volume_name_length) {
  DCHECK(volume_name_length > wcslen(drive) + 2);
  *volume_name = 'V';
  wcscpy(volume_name + 1, drive);
  return TRUE;
}

}  // namespace

class SystemMessageWindowWinTest : public testing::Test {
 public:
  SystemMessageWindowWinTest() : window_(&GetVolumeName) { }
  virtual ~SystemMessageWindowWinTest() { }

 protected:
  virtual void SetUp() OVERRIDE {
    system_monitor_.AddDevicesChangedObserver(&observer_);
  }

  void DoDevicesAttachedTest(const std::vector<int>& deviceIndices);
  void DoDevicesDetachedTest(const std::vector<int>& deviceIndices);

  MessageLoop message_loop_;
  base::SystemMonitor system_monitor_;
  base::MockDevicesChangedObserver observer_;
  SystemMessageWindowWin window_;
};

void SystemMessageWindowWinTest::DoDevicesAttachedTest(
    const std::vector<int>& device_indices) {
  DEV_BROADCAST_VOLUME volume_broadcast;
  volume_broadcast.dbcv_size = sizeof(volume_broadcast);
  volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
  volume_broadcast.dbcv_unitmask = 0x0;
  volume_broadcast.dbcv_flags = 0x0;
  {
    testing::InSequence sequnce;
    for (std::vector<int>::const_iterator it = device_indices.begin();
         it != device_indices.end();
         ++it) {
      volume_broadcast.dbcv_unitmask |= 0x1 << *it;
      std::wstring drive(L"_:\\");
      drive[0] = 'A' + *it;
      std::string name("V");
      name.append(base::SysWideToUTF8(drive));
      EXPECT_CALL(observer_, OnMediaDeviceAttached(*it, name, FilePath(drive)));
    }
  }
  window_.OnDeviceChange(DBT_DEVICEARRIVAL,
                         reinterpret_cast<DWORD>(&volume_broadcast));
  message_loop_.RunAllPending();
};

void SystemMessageWindowWinTest::DoDevicesDetachedTest(
    const std::vector<int>& device_indices) {
  DEV_BROADCAST_VOLUME volume_broadcast;
  volume_broadcast.dbcv_size = sizeof(volume_broadcast);
  volume_broadcast.dbcv_devicetype = DBT_DEVTYP_VOLUME;
  volume_broadcast.dbcv_unitmask = 0x0;
  volume_broadcast.dbcv_flags = 0x0;
  {
    testing::InSequence sequence;
    for (std::vector<int>::const_iterator it = device_indices.begin();
         it != device_indices.end();
         ++it) {
      volume_broadcast.dbcv_unitmask |= 0x1 << *it;
      EXPECT_CALL(observer_, OnMediaDeviceDetached(*it));
    }
  }
  window_.OnDeviceChange(DBT_DEVICEREMOVECOMPLETE,
                         reinterpret_cast<DWORD>(&volume_broadcast));
  message_loop_.RunAllPending();
};

TEST_F(SystemMessageWindowWinTest, DevicesChanged) {
  EXPECT_CALL(observer_, OnDevicesChanged()).Times(1);
  window_.OnDeviceChange(DBT_DEVNODES_CHANGED, NULL);
  message_loop_.RunAllPending();
}

TEST_F(SystemMessageWindowWinTest, RandomMessage) {
  window_.OnDeviceChange(DBT_DEVICEQUERYREMOVE, NULL);
  message_loop_.RunAllPending();
}

TEST_F(SystemMessageWindowWinTest, DevicesAttached) {
  std::vector<int> device_indices;
  device_indices.push_back(1);
  device_indices.push_back(5);
  device_indices.push_back(7);

  DoDevicesAttachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesAttachedHighBoundary) {
  std::vector<int> device_indices;
  device_indices.push_back(25);

  DoDevicesAttachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesAttachedLowBoundary) {
  std::vector<int> device_indices;
  device_indices.push_back(0);

  DoDevicesAttachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesAttachedAdjacentBits) {
  std::vector<int> device_indices;
  device_indices.push_back(0);
  device_indices.push_back(1);
  device_indices.push_back(2);
  device_indices.push_back(3);

  DoDevicesAttachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesDetached) {
  std::vector<int> device_indices;
  device_indices.push_back(1);
  device_indices.push_back(5);
  device_indices.push_back(7);

  DoDevicesDetachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesDetachedHighBoundary) {
  std::vector<int> device_indices;
  device_indices.push_back(25);

  DoDevicesDetachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesDetachedLowBoundary) {
  std::vector<int> device_indices;
  device_indices.push_back(0);

  DoDevicesDetachedTest(device_indices);
}

TEST_F(SystemMessageWindowWinTest, DevicesDetachedAdjacentBits) {
  std::vector<int> device_indices;
  device_indices.push_back(0);
  device_indices.push_back(1);
  device_indices.push_back(2);
  device_indices.push_back(3);

  DoDevicesDetachedTest(device_indices);
}