blob: 9889b421cf622db1e7a5295bc7dc3251a20eb978 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/settings/about_handler.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/extended_updates/extended_updates_controller.h"
#include "chrome/browser/ash/extended_updates/test/mock_extended_updates_controller.h"
#include "chrome/browser/ash/extended_updates/test/scoped_extended_updates_controller.h"
#include "chrome/browser/ash/ownership/fake_owner_settings_service.h"
#include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
#include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::HasSubstr;
using ::testing::NotNull;
using ::testing::Return;
namespace chromeos {
namespace settings {
namespace {
constexpr std::string_view kExtendedUpdatesSettingChangedMessage =
"extended-updates-setting-changed";
class TestAboutHandler : public ::settings::AboutHandler {
public:
explicit TestAboutHandler(Profile* profile) : AboutHandler(profile) {}
~TestAboutHandler() override = default;
// Make public for testing.
using AboutHandler::set_clock;
// Make public for testing.
using AboutHandler::set_web_ui;
};
class AboutHandlerTest : public testing::Test {
public:
AboutHandlerTest() = default;
~AboutHandlerTest() override = default;
AboutHandlerTest(const AboutHandlerTest&) = delete;
AboutHandlerTest& operator=(const AboutHandlerTest&) = delete;
void SetUp() override {
fake_update_engine_client_ =
ash::UpdateEngineClient::InitializeFakeForTest();
ash::ConciergeClient::InitializeFake(/*fake_cicerone_client=*/nullptr);
subscription_ = ash::FakeOwnerSettingsService::SetUpTestingFactory(
test_cros_settings_.device_settings(),
ash::OwnerSettingsServiceAshFactory::GetInstance()->GetOwnerKeyUtil());
profile_ = std::make_unique<TestingProfile>();
handler_ = std::make_unique<TestAboutHandler>(profile_.get());
handler_->set_web_ui(&web_ui_);
handler_->RegisterMessages();
handler_->AllowJavascriptForTesting();
clock_ = std::make_unique<base::SimpleTestClock>();
handler_->set_clock(clock_.get());
}
void TearDown() override {
handler_.reset();
fake_update_engine_client_ = nullptr;
profile_.reset();
subscription_ = {};
ash::ConciergeClient::Shutdown();
ash::UpdateEngineClient::Shutdown();
}
const base::Value& CallWebUIMessage(const std::string& message,
base::Value::List args = {}) {
size_t initial_call_count = web_ui_.call_data().size();
base::Value::List message_args;
message_args.Append("handlerFunctionName");
for (base::Value& arg : args) {
message_args.Append(std::move(arg));
}
web_ui_.HandleReceivedMessage(message, message_args);
task_environment_.RunUntilIdle();
EXPECT_EQ(initial_call_count + 1u, web_ui_.call_data().size());
const content::TestWebUI::CallData& call_data =
*web_ui_.call_data()[initial_call_count];
EXPECT_EQ("cr.webUIResponse", call_data.function_name());
EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
return *call_data.arg3();
}
std::string CallGetEndOfLifeInfoAndReturnString(bool has_eol_passed) {
const auto& response = CallWebUIMessage("getEndOfLifeInfo").GetDict();
EXPECT_EQ(has_eol_passed, *response.FindBool("hasEndOfLife"));
return *response.FindString("aboutPageEndOfLifeMessage");
}
void SetCurrentTimeToUtc(const char* utc_date_string) {
base::Time utc_time;
ASSERT_TRUE(base::Time::FromUTCString(utc_date_string, &utc_time));
clock_->SetNow(utc_time);
}
void SetEolDateUtc(const char* utc_date_string) {
base::Time utc_date;
ASSERT_TRUE(base::Time::FromUTCString(utc_date_string, &utc_date));
fake_update_engine_client_->set_eol_date(utc_date);
}
void RestartJavascript() {
handler_->DisallowJavascript();
handler_->AllowJavascriptForTesting();
}
protected:
content::BrowserTaskEnvironment task_environment_;
ash::ScopedTestingCrosSettings test_cros_settings_;
ash::ScopedStubInstallAttributes test_install_attributes_;
base::CallbackListSubscription subscription_;
std::unique_ptr<TestingProfile> profile_;
content::TestWebUI web_ui_;
std::unique_ptr<TestAboutHandler> handler_;
raw_ptr<ash::FakeUpdateEngineClient> fake_update_engine_client_;
std::unique_ptr<base::SimpleTestClock> clock_;
};
TEST_F(AboutHandlerTest, EndOfLifeMessageInAboutDetailsSubpage) {
SetCurrentTimeToUtc("15 March 2020");
SetEolDateUtc("15 November 2017");
EXPECT_EQ(
"This device stopped getting automatic software and security "
"updates in November 2017. <a target=\"_blank\" href=\"https:"
"//www.google.com/chromebook/older/\">Learn more</a>",
CallGetEndOfLifeInfoAndReturnString(true /*=has_eol_passed*/));
SetEolDateUtc("15 May 2023");
EXPECT_EQ(
"This device will get automatic software and security updates "
"until May 2023. <a target=\"_blank\" href=\"https://support.google"
".com/chrome/a?p=auto-update-policy\">Learn more</a>",
CallGetEndOfLifeInfoAndReturnString(false /*=has_eol_passed*/));
const base::Time null_time = base::Time();
fake_update_engine_client_->set_eol_date(null_time);
EXPECT_EQ("", CallGetEndOfLifeInfoAndReturnString(false /*=has_eol_passed*/));
}
TEST_F(AboutHandlerTest, DeferredUpdateMessageInAboutPage) {
update_engine::StatusResult status;
status.set_current_operation(update_engine::Operation::UPDATED_BUT_DEFERRED);
fake_update_engine_client_->set_default_status(status);
fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
EXPECT_EQ(0, fake_update_engine_client_->apply_deferred_update_count());
web_ui_.HandleReceivedMessage("applyDeferredUpdate", base::Value::List());
EXPECT_EQ(1, fake_update_engine_client_->apply_deferred_update_count());
}
TEST_F(AboutHandlerTest, GetEndOfLifeInfoWithoutExtendedUpdatesDate) {
SetCurrentTimeToUtc("15 March 2020");
SetEolDateUtc("30 Oct 2023");
const auto& response = CallWebUIMessage("getEndOfLifeInfo").GetDict();
EXPECT_FALSE(*response.FindBool("isExtendedUpdatesDatePassed"));
EXPECT_FALSE(*response.FindBool("isExtendedUpdatesOptInRequired"));
}
TEST_F(AboutHandlerTest, GetEndOfLifeInfoWithExtendedUpdatesDatePassed) {
SetCurrentTimeToUtc("15 March 2020");
base::Time eol_date, extended_date;
ASSERT_TRUE(base::Time::FromUTCString("30 Oct 2023", &eol_date));
ASSERT_TRUE(base::Time::FromUTCString("4 June 2019", &extended_date));
fake_update_engine_client_->set_eol_info({
.eol_date = eol_date,
.extended_date = extended_date,
.extended_opt_in_required = true,
});
const auto& response = CallWebUIMessage("getEndOfLifeInfo").GetDict();
EXPECT_TRUE(*response.FindBool("isExtendedUpdatesDatePassed"));
EXPECT_TRUE(*response.FindBool("isExtendedUpdatesOptInRequired"));
EXPECT_THAT(*response.FindString("aboutPageEndOfLifeMessage"),
HasSubstr("June 2019"));
}
TEST_F(AboutHandlerTest, GetEndOfLifeInfoWithExtendedUpdatesDateNotPassed) {
SetCurrentTimeToUtc("15 March 2020");
base::Time eol_date, extended_date;
ASSERT_TRUE(base::Time::FromUTCString("30 Oct 2023", &eol_date));
ASSERT_TRUE(base::Time::FromUTCString("4 June 2021", &extended_date));
fake_update_engine_client_->set_eol_info({
.eol_date = eol_date,
.extended_date = extended_date,
.extended_opt_in_required = true,
});
const auto& response = CallWebUIMessage("getEndOfLifeInfo").GetDict();
EXPECT_FALSE(*response.FindBool("isExtendedUpdatesDatePassed"));
EXPECT_TRUE(*response.FindBool("isExtendedUpdatesOptInRequired"));
EXPECT_THAT(*response.FindString("aboutPageEndOfLifeMessage"),
HasSubstr("June 2021"));
}
TEST_F(AboutHandlerTest, GetEndOfLifeInfoWithExtendedUpdatesOptedIn) {
SetCurrentTimeToUtc("15 March 2020");
base::Time eol_date, extended_date;
ASSERT_TRUE(base::Time::FromUTCString("30 Oct 2023", &eol_date));
ASSERT_TRUE(base::Time::FromUTCString("4 June 2021", &extended_date));
fake_update_engine_client_->set_eol_info({
.eol_date = eol_date,
.extended_date = extended_date,
.extended_opt_in_required = true,
});
ash::MockExtendedUpdatesController mock_controller;
ash::ScopedExtendedUpdatesController scoped_controller(&mock_controller);
EXPECT_CALL(mock_controller, IsOptedIn()).WillOnce(Return(true));
const auto& response = CallWebUIMessage("getEndOfLifeInfo").GetDict();
EXPECT_FALSE(*response.FindBool("isExtendedUpdatesDatePassed"));
EXPECT_TRUE(*response.FindBool("isExtendedUpdatesOptInRequired"));
EXPECT_THAT(*response.FindString("aboutPageEndOfLifeMessage"),
HasSubstr("October 2023"));
}
TEST_F(AboutHandlerTest, HandleIsExtendedUpdatesOptInEligible) {
ash::ExtendedUpdatesController::Params params{
.eol_passed = false,
.extended_date_passed = true,
.opt_in_required = true,
};
ash::MockExtendedUpdatesController mock_controller;
ash::ScopedExtendedUpdatesController scoped_controller(&mock_controller);
EXPECT_CALL(mock_controller, IsOptInEligible(NotNull(), params))
.WillOnce(Return(true));
bool eligible = CallWebUIMessage("isExtendedUpdatesOptInEligible",
base::Value::List()
.Append(params.eol_passed)
.Append(params.extended_date_passed)
.Append(params.opt_in_required))
.GetBool();
EXPECT_TRUE(eligible);
}
TEST_F(AboutHandlerTest, ObservesExtendedUpdatesSettingChanges) {
ash::MockExtendedUpdatesController mock_controller;
ash::ScopedExtendedUpdatesController scoped_controller(&mock_controller);
EXPECT_CALL(mock_controller, HasOptInAbility(NotNull()))
.WillOnce(Return(true));
EXPECT_EQ(web_ui_.call_data().size(), 0u);
EXPECT_TRUE(mock_controller.OptIn(profile_.get()));
ASSERT_EQ(web_ui_.call_data().size(), 1u);
const auto& call_data = web_ui_.call_data()[0];
ASSERT_EQ(call_data->args().size(), 1u);
EXPECT_EQ(call_data->args()[0].GetString(),
kExtendedUpdatesSettingChangedMessage);
}
TEST_F(AboutHandlerTest, ObservesExtendedUpdatesSettingChangesAfterRefresh) {
// Simulate the disallowing and allowing of Javascript during a page refresh.
RestartJavascript();
ash::MockExtendedUpdatesController mock_controller;
ash::ScopedExtendedUpdatesController scoped_controller(&mock_controller);
EXPECT_CALL(mock_controller, HasOptInAbility(NotNull()))
.WillOnce(Return(true));
EXPECT_EQ(web_ui_.call_data().size(), 0u);
EXPECT_TRUE(mock_controller.OptIn(profile_.get()));
ASSERT_EQ(web_ui_.call_data().size(), 1u);
const auto& call_data = web_ui_.call_data()[0];
ASSERT_EQ(call_data->args().size(), 1u);
EXPECT_EQ(call_data->args()[0].GetString(),
kExtendedUpdatesSettingChangedMessage);
}
} // namespace
} // namespace settings
} // namespace chromeos