// 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 "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/geolocation/win7_location_api_win.h"
#include "content/browser/geolocation/win7_location_provider_win.h"
#include "content/public/common/geoposition.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::AtLeast;
using testing::DoDefault;
using testing::Invoke;
using testing::Return;

namespace content {

class MockWin7LocationApi : public Win7LocationApi {
 public:
  static MockWin7LocationApi* CreateMock() {
    return new MockWin7LocationApi();
  }

  // Used to signal when the destructor is called.
  MOCK_METHOD0(Die, void());
  // Win7LocationApi
  MOCK_METHOD1(GetPosition, void(Geoposition*));
  MOCK_METHOD1(SetHighAccuracy, bool(bool));

  virtual ~MockWin7LocationApi() {
    Die();
  }

  void GetPositionValid(Geoposition* position) {
    position->latitude = 4.5;
    position->longitude = -34.1;
    position->accuracy = 0.5;
    position->timestamp = base::Time::FromDoubleT(200);
    position->error_code = Geoposition::ERROR_CODE_NONE;
  }
  void GetPositionInvalid(Geoposition* position) {
    position->latitude = 4.5;
    position->longitude = -340000.1;
    position->accuracy = 0.5;
    position->timestamp = base::Time::FromDoubleT(200);
    position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
  }

 private:
  MockWin7LocationApi() {
    ON_CALL(*this, GetPosition(_))
        .WillByDefault(Invoke(this,
                              &MockWin7LocationApi::GetPositionValid));
    ON_CALL(*this, SetHighAccuracy(true))
        .WillByDefault(Return(true));
    ON_CALL(*this, SetHighAccuracy(false))
        .WillByDefault(Return(false));
  }
};

class LocationProviderListenerLoopQuitter {
 public:
  explicit LocationProviderListenerLoopQuitter(base::MessageLoop* message_loop)
      : message_loop_to_quit_(message_loop) {
    CHECK(message_loop_to_quit_ != NULL);
  }

  void LocationUpdateAvailable(const LocationProvider* provider,
                               const Geoposition& position) {
    EXPECT_EQ(base::MessageLoop::current(), message_loop_to_quit_);
    provider_ = provider;
    message_loop_to_quit_->Quit();
  }

  base::MessageLoop* message_loop_to_quit_;
  const LocationProvider* provider_;
};

class GeolocationProviderWin7Tests : public testing::Test {
 public:
  GeolocationProviderWin7Tests(): location_listener_(&main_message_loop_) {
  }

  virtual void SetUp() {
    api_ = MockWin7LocationApi::CreateMock();
    provider_ = new Win7LocationProvider(api_);
    provider_->SetUpdateCallback(base::Bind(
        &LocationProviderListenerLoopQuitter::LocationUpdateAvailable,
        base::Unretained(&location_listener_)));
  }
  virtual void TearDown() {
    provider_->StopProvider();
    delete provider_;
    main_message_loop_.RunUntilIdle();
  }

 protected:
  Win7LocationProvider* provider() {
    return static_cast<Win7LocationProvider*>(provider_);
  }

  MockWin7LocationApi* api_;
  LocationProviderListenerLoopQuitter location_listener_;
  base::MessageLoop main_message_loop_;
  LocationProvider* provider_;
};

TEST_F(GeolocationProviderWin7Tests, StartStop) {
  EXPECT_CALL(*api_, SetHighAccuracy(true))
      .WillOnce(Return(true));
  EXPECT_TRUE(provider()->StartProvider(true));
  provider()->StopProvider();
  EXPECT_CALL(*api_, SetHighAccuracy(false))
      .WillOnce(Return(true));
  EXPECT_TRUE(provider()->StartProvider(false));
}

TEST_F(GeolocationProviderWin7Tests, GetValidPosition) {
  EXPECT_CALL(*api_, GetPosition(_))
      .Times(AtLeast(1));
  EXPECT_CALL(*api_, SetHighAccuracy(true))
      .WillOnce(Return(true));
  EXPECT_TRUE(provider()->StartProvider(true));
  main_message_loop_.Run();
  Geoposition position;
  provider()->GetPosition(&position);
  EXPECT_TRUE(position.Validate());
}

TEST_F(GeolocationProviderWin7Tests, GetInvalidPosition) {
  EXPECT_CALL(*api_, GetPosition(_))
      .Times(AtLeast(1))
      .WillRepeatedly(Invoke(api_,
      &MockWin7LocationApi::GetPositionInvalid));
  EXPECT_CALL(*api_, SetHighAccuracy(true))
      .WillOnce(Return(true));
  EXPECT_TRUE(provider()->StartProvider(true));
  main_message_loop_.Run();
  Geoposition position;
  provider()->GetPosition(&position);
  EXPECT_FALSE(position.Validate());
}

}  // namespace content