blob: cdeda656f33cf8d68c52335482612ba25d0332c9 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/chromeos/night_light/night_light_client.h"
#include "ash/public/interfaces/night_light_controller.mojom.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/clock.h"
#include "base/time/tick_clock.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
using ScheduleType = ash::mojom::NightLightController::ScheduleType;
// A fake implementation of NightLightController for testing.
class FakeNightLightController : public ash::mojom::NightLightController {
public:
FakeNightLightController() : binding_(this) {}
~FakeNightLightController() override = default;
const ash::mojom::SimpleGeopositionPtr& position() const { return position_; }
int position_pushes_num() const { return position_pushes_num_; }
ash::mojom::NightLightControllerPtr CreateInterfacePtrAndBind() {
ash::mojom::NightLightControllerPtr ptr;
binding_.Bind(mojo::MakeRequest(&ptr));
return ptr;
}
// ash::mojom::NightLightController:
void SetCurrentGeoposition(
ash::mojom::SimpleGeopositionPtr position) override {
position_ = std::move(position);
++position_pushes_num_;
}
void SetClient(ash::mojom::NightLightClientPtr client) override {
client_ = std::move(client);
}
void NotifyScheduleTypeChanged(ScheduleType type) {
client_->OnScheduleTypeChanged(type);
client_.FlushForTesting();
}
private:
ash::mojom::SimpleGeopositionPtr position_;
ash::mojom::NightLightClientPtr client_;
mojo::Binding<ash::mojom::NightLightController> binding_;
// The number of times a new position is pushed to this controller.
int position_pushes_num_ = 0;
DISALLOW_COPY_AND_ASSIGN(FakeNightLightController);
};
// A fake implementation of NightLightClient that doesn't perform any actual
// geoposition requests.
class FakeNightLightClient : public NightLightClient,
public base::Clock,
public base::TickClock {
public:
FakeNightLightClient() : NightLightClient(nullptr /* url_context_getter */) {
SetTimerForTesting(
std::make_unique<base::OneShotTimer>(this /* tick_clock */));
SetClockForTesting(this);
}
~FakeNightLightClient() override = default;
// base::Clock:
base::Time Now() const override { return fake_now_; }
// base::TickClock:
base::TimeTicks NowTicks() const override { return fake_now_ticks_; }
void set_fake_now(base::Time now) { fake_now_ = now; }
void set_fake_now_ticks(base::TimeTicks now_ticks) {
fake_now_ticks_ = now_ticks;
}
void set_position_to_send(const chromeos::Geoposition& position) {
position_to_send_ = position;
}
int geoposition_requests_num() const { return geoposition_requests_num_; }
private:
// night_light::NightLightClient:
void RequestGeoposition() override {
OnGeoposition(position_to_send_, false, base::TimeDelta());
++geoposition_requests_num_;
}
base::Time fake_now_;
base::TimeTicks fake_now_ticks_;
// The position to send to the controller the next time OnGeoposition is
// invoked.
chromeos::Geoposition position_to_send_;
// The number of new geoposition requests that have been triggered.
int geoposition_requests_num_ = 0;
DISALLOW_COPY_AND_ASSIGN(FakeNightLightClient);
};
// Base test fixture.
class NightLightClientTest : public testing::Test {
public:
NightLightClientTest() = default;
~NightLightClientTest() override = default;
void SetUp() override {
// Deterministic fake time that doesn't change for the sake of testing.
client_.set_fake_now(base::Time::Now());
client_.set_fake_now_ticks(base::TimeTicks::Now());
client_.SetNightLightControllerPtrForTesting(
controller_.CreateInterfacePtrAndBind());
client_.Start();
client_.FlushNightLightControllerForTesting();
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
FakeNightLightController controller_;
FakeNightLightClient client_;
private:
DISALLOW_COPY_AND_ASSIGN(NightLightClientTest);
};
// Test that the client is retrieving geoposition periodically only when the
// schedule type is "sunset to sunrise".
TEST_F(NightLightClientTest, TestClientRunningOnlyWhenSunsetToSunriseSchedule) {
EXPECT_FALSE(client_.using_geoposition());
controller_.NotifyScheduleTypeChanged(ScheduleType::kNone);
EXPECT_FALSE(client_.using_geoposition());
controller_.NotifyScheduleTypeChanged(ScheduleType::kCustom);
controller_.NotifyScheduleTypeChanged(ScheduleType::kSunsetToSunrise);
scoped_task_environment_.RunUntilIdle();
client_.FlushNightLightControllerForTesting();
EXPECT_TRUE(client_.using_geoposition());
// Client should stop retrieving geopositions when schedule type changes to
// something else.
controller_.NotifyScheduleTypeChanged(ScheduleType::kNone);
EXPECT_FALSE(client_.using_geoposition());
}
// Test that client only pushes valid positions.
TEST_F(NightLightClientTest, TestInvalidPositions) {
EXPECT_EQ(0, controller_.position_pushes_num());
chromeos::Geoposition position;
position.latitude = 32.0;
position.longitude = 31.0;
position.status = chromeos::Geoposition::STATUS_TIMEOUT;
position.accuracy = 10;
position.timestamp = base::Time::Now();
client_.set_position_to_send(position);
controller_.NotifyScheduleTypeChanged(ScheduleType::kSunsetToSunrise);
scoped_task_environment_.RunUntilIdle();
client_.FlushNightLightControllerForTesting();
EXPECT_EQ(1, client_.geoposition_requests_num());
EXPECT_EQ(0, controller_.position_pushes_num());
}
// Test that successive changes of the schedule type to sunset to sunrise do not
// trigger repeated geoposition requests.
TEST_F(NightLightClientTest, TestRepeatedScheduleTypeChanges) {
// Start with a valid position, and expect it to be delivered to the
// controller.
EXPECT_EQ(0, controller_.position_pushes_num());
chromeos::Geoposition position1;
position1.latitude = 32.0;
position1.longitude = 31.0;
position1.status = chromeos::Geoposition::STATUS_OK;
position1.accuracy = 10;
position1.timestamp = base::Time::Now();
client_.set_position_to_send(position1);
controller_.NotifyScheduleTypeChanged(ScheduleType::kSunsetToSunrise);
scoped_task_environment_.RunUntilIdle();
client_.FlushNightLightControllerForTesting();
EXPECT_EQ(1, client_.geoposition_requests_num());
EXPECT_EQ(1, controller_.position_pushes_num());
EXPECT_EQ(client_.Now(), client_.last_successful_geo_request_time());
// A new different position just for the sake of comparison with position1 to
// make sure that no new requests are triggered and the same old position will
// be resent to the controller.
chromeos::Geoposition position2;
position2.latitude = 100.0;
position2.longitude = 200.0;
position2.status = chromeos::Geoposition::STATUS_OK;
position2.accuracy = 10;
position2.timestamp = base::Time::Now();
client_.set_position_to_send(position2);
controller_.NotifyScheduleTypeChanged(ScheduleType::kSunsetToSunrise);
scoped_task_environment_.RunUntilIdle();
client_.FlushNightLightControllerForTesting();
// No new request has been triggered, however the same old valid position was
// pushed to the controller.
EXPECT_EQ(1, client_.geoposition_requests_num());
EXPECT_EQ(2, controller_.position_pushes_num());
EXPECT_TRUE(ash::mojom::SimpleGeoposition::New(position1.latitude,
position1.longitude)
.Equals(controller_.position()));
// The timer should be running scheduling a next request that is a
// kNextRequestDelayAfterSuccess from the last successful request time.
EXPECT_TRUE(client_.timer().IsRunning());
base::TimeDelta expected_delay =
client_.last_successful_geo_request_time() +
NightLightClient::GetNextRequestDelayAfterSuccessForTesting() -
client_.Now();
EXPECT_EQ(expected_delay, client_.timer().GetCurrentDelay());
}
} // namespace