blob: c57fe972ec00effa94b414532fbdf7bbc1a3d88e [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/rgb_keyboard/rgb_keyboard_manager.h"
#include <stdint.h>
#include <memory>
#include <optional>
#include "ash/constants/ash_features.h"
#include "ash/ime/ime_controller_impl.h"
#include "ash/rgb_keyboard/histogram_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ash/components/dbus/rgbkbd/fake_rgbkbd_client.h"
#include "chromeos/ash/components/dbus/rgbkbd/rgbkbd_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
class RgbKeyboardManagerTest : public testing::Test {
public:
RgbKeyboardManagerTest() {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{features::kExperimentalRgbKeyboardPatterns},
/*disabled_features=*/{});
// ImeControllerImpl must be initialized before RgbKeyboardManager.
ime_controller_ = std::make_unique<ImeControllerImpl>();
// This is instantiating a global instance that will be deallocated in
// the destructor of RgbKeyboardManagerTest.
RgbkbdClient::InitializeFake();
client_ = static_cast<FakeRgbkbdClient*>(RgbkbdClient::Get());
// Default capabilities to 'RgbKeyboardCapabilities::kIndividualKey'
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
}
RgbKeyboardManagerTest(const RgbKeyboardManagerTest&) = delete;
RgbKeyboardManagerTest& operator=(const RgbKeyboardManagerTest&) = delete;
~RgbKeyboardManagerTest() override {
// Ordering for deletion is Manger -> Client -> IME Controller
manager_.reset();
RgbkbdClient::Shutdown();
ime_controller_.reset();
}
protected:
void InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities capability) {
client_->set_rgb_keyboard_capabilities(capability);
// |ime_controller_| is initialized in RgbKeyboardManagerTest's ctor.
DCHECK(ime_controller_);
manager_.reset();
manager_ = std::make_unique<RgbKeyboardManager>(ime_controller_.get());
}
// ImeControllerImpl must be destroyed after RgbKeyboardManager.
std::unique_ptr<ImeControllerImpl> ime_controller_;
std::unique_ptr<RgbKeyboardManager> manager_;
raw_ptr<FakeRgbkbdClient, DanglingUntriaged> client_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(RgbKeyboardManagerTest, GetKeyboardCapabilities) {
// kIndividualKey is the default for this test suite.
EXPECT_EQ(rgbkbd::RgbKeyboardCapabilities::kIndividualKey,
client_->get_rgb_keyboard_capabilities());
}
class KeyboardCapabilityHistogramEmittedTest
: public RgbKeyboardManagerTest,
public testing::WithParamInterface<
std::pair<rgbkbd::RgbKeyboardCapabilities,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType>> {
public:
KeyboardCapabilityHistogramEmittedTest() {
std::tie(capability_, metric_) = GetParam();
}
protected:
rgbkbd::RgbKeyboardCapabilities capability_;
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType metric_;
};
INSTANTIATE_TEST_SUITE_P(
All,
KeyboardCapabilityHistogramEmittedTest,
testing::Values(
std::make_pair(
rgbkbd::RgbKeyboardCapabilities::kNone,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType::kNone),
std::make_pair(rgbkbd::RgbKeyboardCapabilities::kIndividualKey,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType::
kIndividualKey),
std::make_pair(rgbkbd::RgbKeyboardCapabilities::kFourZoneFortyLed,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType::
kFourZoneFortyLed),
std::make_pair(rgbkbd::RgbKeyboardCapabilities::kFourZoneTwelveLed,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType::
kFourZoneTwelveLed),
std::make_pair(rgbkbd::RgbKeyboardCapabilities::kFourZoneFourLed,
ash::rgb_keyboard::metrics::RgbKeyboardCapabilityType::
kFourZoneFourLed)));
TEST_P(KeyboardCapabilityHistogramEmittedTest,
KeyboardCapabilityHistogramEmitted) {
base::HistogramTester histogram_tester;
InitializeManagerWithCapability(capability_);
EXPECT_EQ(capability_, client_->get_rgb_keyboard_capabilities());
histogram_tester.ExpectBucketCount(
rgb_keyboard::metrics::kRgbKeyboardCapabilityTypeHistogramName, metric_,
1);
}
class RgbChangeTypeHistogramEmittedTest
: public RgbKeyboardManagerTest,
public testing::WithParamInterface<rgbkbd::RgbKeyboardCapabilities> {
public:
RgbChangeTypeHistogramEmittedTest() = default;
};
INSTANTIATE_TEST_SUITE_P(
All,
RgbChangeTypeHistogramEmittedTest,
testing::Values(rgbkbd::RgbKeyboardCapabilities::kIndividualKey,
rgbkbd::RgbKeyboardCapabilities::kFourZoneFortyLed,
rgbkbd::RgbKeyboardCapabilities::kFourZoneTwelveLed,
rgbkbd::RgbKeyboardCapabilities::kFourZoneFourLed));
TEST_P(RgbChangeTypeHistogramEmittedTest, RgbChangeTypeHistogramEmitted) {
base::HistogramTester histogram_tester;
const auto capability = GetParam();
const auto name =
std::string(ash::rgb_keyboard::metrics::kRgbKeyboardHistogramPrefix +
ash::rgb_keyboard::metrics::GetCapabilityTypeStr(capability));
InitializeManagerWithCapability(capability);
manager_->SetStaticBackgroundColor(/*r=*/1, /*g=*/2, /*b=*/3);
histogram_tester.ExpectBucketCount(
name,
ash::rgb_keyboard::metrics::RgbKeyboardBacklightChangeType::
kStaticBackgroundColorChanged,
1);
manager_->SetRainbowMode();
histogram_tester.ExpectBucketCount(
name,
ash::rgb_keyboard::metrics::RgbKeyboardBacklightChangeType::
kRainbowModeSelected,
1);
manager_->SetZoneColor(/*zone=*/0, /*r=*/1, /*g=*/2, /*b=*/3);
histogram_tester.ExpectBucketCount(
name,
ash::rgb_keyboard::metrics::RgbKeyboardBacklightChangeType::
kStaticZoneColorChanged,
1);
}
TEST_F(RgbKeyboardManagerTest, ZoneCountIsCorrect) {
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
EXPECT_EQ(5, manager_->GetZoneCount());
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kFourZoneFortyLed);
EXPECT_EQ(4, manager_->GetZoneCount());
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kFourZoneTwelveLed);
EXPECT_EQ(4, manager_->GetZoneCount());
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kFourZoneFourLed);
EXPECT_EQ(4, manager_->GetZoneCount());
InitializeManagerWithCapability(rgbkbd::RgbKeyboardCapabilities::kNone);
EXPECT_EQ(0, manager_->GetZoneCount());
}
TEST_F(RgbKeyboardManagerTest, SetStaticRgbValues) {
const uint8_t expected_r = 1;
const uint8_t expected_g = 2;
const uint8_t expected_b = 3;
manager_->SetStaticBackgroundColor(expected_r, expected_g, expected_b);
const RgbColor& rgb_values = client_->recently_sent_rgb();
EXPECT_EQ(expected_r, std::get<0>(rgb_values));
EXPECT_EQ(expected_g, std::get<1>(rgb_values));
EXPECT_EQ(expected_b, std::get<2>(rgb_values));
}
TEST_F(RgbKeyboardManagerTest, SetZoneRgbValues) {
const int zone_1 = 0;
const uint8_t expected_r_1 = 1;
const uint8_t expected_g_1 = 2;
const uint8_t expected_b_1 = 3;
const int zone_2 = 3;
const uint8_t expected_r_2 = 4;
const uint8_t expected_g_2 = 5;
const uint8_t expected_b_2 = 6;
manager_->SetZoneColor(zone_1, expected_r_1, expected_g_1, expected_b_1);
manager_->SetZoneColor(zone_2, expected_r_2, expected_g_2, expected_b_2);
auto zone_colors = client_->get_zone_colors();
EXPECT_EQ(2u, zone_colors.size());
EXPECT_EQ(zone_colors[zone_1],
std::make_tuple(expected_r_1, expected_g_1, expected_b_1));
EXPECT_EQ(zone_colors[zone_2],
std::make_tuple(expected_r_2, expected_g_2, expected_b_2));
}
TEST_F(RgbKeyboardManagerTest, SetInvalidZoneId) {
const int invalid_zone = 100;
const uint8_t expected_r = 1;
const uint8_t expected_g = 2;
const uint8_t expected_b = 3;
manager_->SetZoneColor(invalid_zone, expected_r, expected_g, expected_b);
auto zone_colors = client_->get_zone_colors();
EXPECT_EQ(0u, zone_colors.size());
const int valid_zone = 0;
manager_->SetZoneColor(valid_zone, expected_r, expected_g, expected_b);
zone_colors = client_->get_zone_colors();
EXPECT_EQ(1u, zone_colors.size());
EXPECT_EQ(zone_colors[valid_zone],
std::make_tuple(expected_r, expected_g, expected_b));
}
TEST_F(RgbKeyboardManagerTest, SetRainbowMode) {
EXPECT_FALSE(client_->is_rainbow_mode_set());
manager_->SetRainbowMode();
EXPECT_TRUE(client_->is_rainbow_mode_set());
}
TEST_F(RgbKeyboardManagerTest, RainbowModeResetsStatic) {
EXPECT_FALSE(client_->is_rainbow_mode_set());
const uint8_t expected_r = 1;
const uint8_t expected_g = 2;
const uint8_t expected_b = 3;
manager_->SetStaticBackgroundColor(expected_r, expected_g, expected_b);
const RgbColor& rgb_values = client_->recently_sent_rgb();
EXPECT_EQ(expected_r, std::get<0>(rgb_values));
EXPECT_EQ(expected_g, std::get<1>(rgb_values));
EXPECT_EQ(expected_b, std::get<2>(rgb_values));
manager_->SetRainbowMode();
EXPECT_TRUE(client_->is_rainbow_mode_set());
const RgbColor& updated_rgb_values = client_->recently_sent_rgb();
EXPECT_EQ(0u, std::get<0>(updated_rgb_values));
EXPECT_EQ(0u, std::get<1>(updated_rgb_values));
EXPECT_EQ(0u, std::get<2>(updated_rgb_values));
}
TEST_F(RgbKeyboardManagerTest, StaticResetRainbowMode) {
EXPECT_FALSE(client_->is_rainbow_mode_set());
manager_->SetRainbowMode();
EXPECT_TRUE(client_->is_rainbow_mode_set());
const uint8_t expected_r = 1;
const uint8_t expected_g = 2;
const uint8_t expected_b = 3;
manager_->SetStaticBackgroundColor(expected_r, expected_g, expected_b);
const RgbColor& rgb_values = client_->recently_sent_rgb();
EXPECT_FALSE(client_->is_rainbow_mode_set());
EXPECT_EQ(expected_r, std::get<0>(rgb_values));
EXPECT_EQ(expected_g, std::get<1>(rgb_values));
EXPECT_EQ(expected_b, std::get<2>(rgb_values));
}
TEST_F(RgbKeyboardManagerTest, OnCapsLockChanged) {
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
ime_controller_->UpdateCapsLockState(/*caps_enabled=*/true);
EXPECT_TRUE(client_->get_caps_lock_state());
ime_controller_->UpdateCapsLockState(/*caps_enabled=*/false);
EXPECT_FALSE(client_->get_caps_lock_state());
}
TEST_F(RgbKeyboardManagerTest, OnLoginCapsLock) {
// Simulate CapsLock enabled upon login.
ime_controller_->SetCapsLockEnabled(/*caps_enabled=*/true);
// Simulate RgbKeyboardManager starting up on login.
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
EXPECT_TRUE(client_->get_caps_lock_state());
}
// TODO(jimmyxgong): This is just a stub test, there is only one enum available
// so just check num times the function has been called.
TEST_F(RgbKeyboardManagerTest, SetAnimationMode) {
EXPECT_EQ(0, client_->animation_mode_call_count());
manager_->SetAnimationMode(rgbkbd::RgbAnimationMode::kBasicTestPattern);
EXPECT_EQ(1, client_->animation_mode_call_count());
}
TEST_F(RgbKeyboardManagerTest, SetCapsLockStateDisallowedForZonedKeyboards) {
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kFourZoneFortyLed);
EXPECT_FALSE(client_->get_caps_lock_state());
ime_controller_->UpdateCapsLockState(/*caps_enabled=*/true);
// Caps lock state should still be false since RgbKeyboardManager should have
// prevented the call to SetCapsLockState.
EXPECT_FALSE(client_->get_caps_lock_state());
}
TEST_F(RgbKeyboardManagerTest, SetCapsLockStateAllowedForPerKeyKeboards) {
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
EXPECT_FALSE(client_->get_caps_lock_state());
ime_controller_->UpdateCapsLockState(/*caps_enabled=*/true);
EXPECT_TRUE(client_->get_caps_lock_state());
}
// Following `RaceCondition*` tests verify that if a keyboard backlight color is
// set before `RgbKeyboardManager` is ready, that the color is set once
// `RgbKeyboardManager` is finally ready.
TEST_F(RgbKeyboardManagerTest, RaceConditionStaticSingleColor) {
const uint8_t expected_r = 1;
const uint8_t expected_g = 2;
const uint8_t expected_b = 3;
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/false);
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
manager_->SetStaticBackgroundColor(expected_r, expected_g, expected_b);
{
const RgbColor& rgb_values = client_->recently_sent_rgb();
EXPECT_NE(expected_r, std::get<0>(rgb_values));
EXPECT_NE(expected_g, std::get<1>(rgb_values));
EXPECT_NE(expected_b, std::get<2>(rgb_values));
}
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/true);
client_->attempt_run_rgb_keyboard_capabilities_callback();
{
const RgbColor& rgb_values = client_->recently_sent_rgb();
EXPECT_EQ(expected_r, std::get<0>(rgb_values));
EXPECT_EQ(expected_g, std::get<1>(rgb_values));
EXPECT_EQ(expected_b, std::get<2>(rgb_values));
}
}
TEST_F(RgbKeyboardManagerTest, RaceConditionStaticZoneColor) {
const int zone_1 = 0;
const uint8_t expected_r_1 = 1;
const uint8_t expected_g_1 = 2;
const uint8_t expected_b_1 = 3;
const int zone_2 = 3;
const uint8_t expected_r_2 = 4;
const uint8_t expected_g_2 = 5;
const uint8_t expected_b_2 = 6;
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/false);
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
manager_->SetZoneColor(zone_1, expected_r_1, expected_g_1, expected_b_1);
manager_->SetZoneColor(zone_2, expected_r_2, expected_g_2, expected_b_2);
{
auto zone_colors = client_->get_zone_colors();
EXPECT_EQ(0u, zone_colors.size());
}
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/true);
client_->attempt_run_rgb_keyboard_capabilities_callback();
{
auto zone_colors = client_->get_zone_colors();
EXPECT_EQ(2u, zone_colors.size());
EXPECT_EQ(zone_colors[zone_1],
std::make_tuple(expected_r_1, expected_g_1, expected_b_1));
EXPECT_EQ(zone_colors[zone_2],
std::make_tuple(expected_r_2, expected_g_2, expected_b_2));
}
}
TEST_F(RgbKeyboardManagerTest, RaceConditionStaticRainbow) {
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/false);
InitializeManagerWithCapability(
rgbkbd::RgbKeyboardCapabilities::kIndividualKey);
manager_->SetRainbowMode();
EXPECT_FALSE(client_->is_rainbow_mode_set());
client_->set_should_run_rgb_keyboard_capabilities_callback(
/*should_run_callback=*/true);
client_->attempt_run_rgb_keyboard_capabilities_callback();
EXPECT_TRUE(client_->is_rainbow_mode_set());
}
} // namespace ash