#include "ash/system/camera/camera_effects_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/style/tab_slider.h"
#include "ash/style/tab_slider_button.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/system/video_conference/bubble/bubble_view_ids.h"
#include "ash/system/video_conference/bubble/set_value_effects_view.h"
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager_types.h"
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
#include "ash/system/video_conference/video_conference_tray.h"
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "media/capture/video/chromeos/mojom/effects_pipeline.mojom.h"
namespace ash {
class CameraEffectsControllerTest : public NoSessionAshTestBase {
public:
void SetUp() override {
scoped_feature_list_.InitWithFeatures({features::kVideoConference}, {});
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kCameraEffectsSupportedByHardware);
controller_ = std::make_unique<FakeVideoConferenceTrayController>();
AshTestBase::SetUp();
camera_effects_controller_ = Shell::Get()->camera_effects_controller();
camera_effects_controller_->bypass_set_camera_effects_for_testing(true);
}
void TearDown() override {
NoSessionAshTestBase::TearDown();
controller_.reset();
}
void SetBackgroundBlurEffectState(absl::optional<int> state) {
camera_effects_controller_->OnEffectControlActivated(
VcEffectId::kBackgroundBlur, state);
}
int GetBackgroundBlurEffectState() {
absl::optional<int> effect_state =
camera_effects_controller_->GetEffectState(VcEffectId::kBackgroundBlur);
DCHECK(effect_state.has_value());
return effect_state.value();
}
int GetBackgroundBlurPref() {
return Shell::Get()
->session_controller()
->GetActivePrefService()
->GetInteger(prefs::kBackgroundBlur);
}
void TogglePortraitRelightingEffectState() {
camera_effects_controller_->OnEffectControlActivated(
VcEffectId::kPortraitRelighting, absl::nullopt);
}
bool GetPortraitRelightingEffectState() {
absl::optional<int> effect_state =
camera_effects_controller_->GetEffectState(
VcEffectId::kPortraitRelighting);
DCHECK(effect_state.has_value());
return static_cast<bool>(effect_state.value());
}
bool GetPortraitRelightingPref() {
return Shell::Get()
->session_controller()
->GetActivePrefService()
->GetBoolean(prefs::kPortraitRelighting);
}
CameraEffectsController* camera_effects_controller() {
return camera_effects_controller_;
}
FakeVideoConferenceTrayController* controller() { return controller_.get(); }
protected:
raw_ptr<CameraEffectsController, ExperimentalAsh> camera_effects_controller_ =
nullptr;
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(CameraEffectsControllerTest, IsEffectControlAvailable) {
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures({}, {features::kVideoConference});
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundBlur));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kPortraitRelight));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundReplace));
}
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures({features::kVideoConference}, {});
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundBlur));
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kPortraitRelight));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundReplace));
}
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures({features::kVideoConference},
{features::kVcPortraitRelight});
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundBlur));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kPortraitRelight));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundReplace));
}
}
TEST_F(CameraEffectsControllerTest, BackgroundBlurOnEffectControlActivated) {
SimulateUserLogin("testuser@gmail.com");
for (const auto state :
{CameraEffectsController::BackgroundBlurPrefValue::kOff,
CameraEffectsController::BackgroundBlurPrefValue::kLowest,
CameraEffectsController::BackgroundBlurPrefValue::kLight,
CameraEffectsController::BackgroundBlurPrefValue::kMedium,
CameraEffectsController::BackgroundBlurPrefValue::kHeavy,
CameraEffectsController::BackgroundBlurPrefValue::kMaximum}) {
SetBackgroundBlurEffectState(state);
EXPECT_EQ(GetBackgroundBlurPref(), state);
EXPECT_EQ(GetBackgroundBlurEffectState(), state);
}
SetBackgroundBlurEffectState(
static_cast<int>(
CameraEffectsController::BackgroundBlurPrefValue::kMaximum) +
1);
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
SetBackgroundBlurEffectState(
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
SetBackgroundBlurEffectState(absl::nullopt);
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
}
TEST_F(CameraEffectsControllerTest,
PortraitRelightingOnEffectControlActivated) {
SimulateUserLogin("testuser@gmail.com");
EXPECT_FALSE(GetPortraitRelightingEffectState());
EXPECT_FALSE(GetPortraitRelightingPref());
TogglePortraitRelightingEffectState();
EXPECT_TRUE(GetPortraitRelightingEffectState());
EXPECT_TRUE(GetPortraitRelightingPref());
TogglePortraitRelightingEffectState();
EXPECT_FALSE(GetPortraitRelightingEffectState());
EXPECT_FALSE(GetPortraitRelightingPref());
TogglePortraitRelightingEffectState();
EXPECT_TRUE(GetPortraitRelightingEffectState());
EXPECT_TRUE(GetPortraitRelightingPref());
}
TEST_F(CameraEffectsControllerTest, PrefOnCameraEffectChanged) {
SimulateUserLogin("testuser@gmail.com");
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_FALSE(GetPortraitRelightingEffectState());
EXPECT_FALSE(GetPortraitRelightingPref());
cros::mojom::EffectsConfigPtr new_effects = cros::mojom::EffectsConfig::New();
new_effects->blur_enabled = true;
new_effects->blur_level = cros::mojom::BlurLevel::kMaximum;
new_effects->relight_enabled = true;
camera_effects_controller_->OnCameraEffectChanged(std::move(new_effects));
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
EXPECT_TRUE(GetPortraitRelightingEffectState());
EXPECT_TRUE(GetPortraitRelightingPref());
new_effects = cros::mojom::EffectsConfigPtr();
camera_effects_controller_->OnCameraEffectChanged(std::move(new_effects));
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
EXPECT_TRUE(GetPortraitRelightingEffectState());
EXPECT_TRUE(GetPortraitRelightingPref());
new_effects = cros::mojom::EffectsConfig::New();
camera_effects_controller_->OnCameraEffectChanged(std::move(new_effects));
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_FALSE(GetPortraitRelightingEffectState());
EXPECT_FALSE(GetPortraitRelightingPref());
}
TEST_F(CameraEffectsControllerTest, ResourceDependencyFlags) {
SimulateUserLogin("testuser@gmail.com");
auto* background_blur =
camera_effects_controller()->GetEffectById(VcEffectId::kBackgroundBlur);
EXPECT_EQ(VcHostedEffect::ResourceDependency::kCamera,
background_blur->dependency_flags());
auto* portrait_relight = camera_effects_controller()->GetEffectById(
VcEffectId::kPortraitRelighting);
EXPECT_EQ(VcHostedEffect::ResourceDependency::kCamera,
portrait_relight->dependency_flags());
}
TEST_F(CameraEffectsControllerTest, BackgroundBlurEnums) {
EXPECT_EQ(
static_cast<int>(CameraEffectsController::BackgroundBlurState::kMaximum),
CameraEffectsController::BackgroundBlurPrefValue::kMaximum + 1);
}
TEST_F(CameraEffectsControllerTest, BackgroundBlurMetricsRecord) {
base::HistogramTester histogram_tester;
SimulateUserLogin("testuser@gmail.com");
VideoConferenceMediaState state;
state.has_media_app = true;
state.has_camera_permission = true;
state.has_microphone_permission = true;
state.is_capturing_screen = true;
controller()->UpdateWithMediaState(state);
auto* vc_tray = StatusAreaWidgetTestHelper::GetStatusAreaWidget()
->video_conference_tray();
LeftClickOn(vc_tray->toggle_bubble_button());
auto* set_value_effects_view = vc_tray->GetBubbleView()->GetViewByID(
video_conference::BubbleViewID::kSetValueEffectsView);
ASSERT_EQ(1u, set_value_effects_view->children().size());
auto* background_blur_slider =
static_cast<video_conference::SetValueEffectSlider*>(
set_value_effects_view->children()[0]);
EXPECT_EQ(VcEffectId::kBackgroundBlur, background_blur_slider->effect_id());
auto* tab_slider = background_blur_slider->tab_slider();
auto* first_button = tab_slider->GetButtonAtIndex(0);
EXPECT_TRUE(first_button->selected());
histogram_tester.ExpectBucketCount(
"Ash.VideoConferenceTray.BackgroundBlur.Click",
CameraEffectsController::BackgroundBlurState::kOff, 0);
LeftClickOn(tab_slider->GetButtonAtIndex(1));
histogram_tester.ExpectBucketCount(
"Ash.VideoConferenceTray.BackgroundBlur.Click",
CameraEffectsController::BackgroundBlurState::kLight, 1);
LeftClickOn(tab_slider->GetButtonAtIndex(2));
histogram_tester.ExpectBucketCount(
"Ash.VideoConferenceTray.BackgroundBlur.Click",
CameraEffectsController::BackgroundBlurState::kMaximum, 1);
LeftClickOn(first_button);
histogram_tester.ExpectBucketCount(
"Ash.VideoConferenceTray.BackgroundBlur.Click",
CameraEffectsController::BackgroundBlurState::kOff, 1);
}
}