#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/camera/autozoom_controller_impl.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/system/video_conference/video_conference_tray_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_view_util.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-shared.h"
#include "media/capture/video/chromeos/mojom/effects_pipeline.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/jpeg_codec.h"
namespace ash {
using ::testing::ElementsAre;
using BackgroundImageInfo = CameraEffectsController::BackgroundImageInfo;
constexpr char kMetadataSuffix[] = ".metadata";
std::string SkBitmapToString(const SkBitmap& bitmap) {
std::optional<std::vector<uint8_t>> data =
gfx::JPEGCodec::Encode(bitmap, 100);
CHECK(data);
return std::string(base::as_string_view(data.value()));
}
std::string CreateJpgBytes(SkColor color) {
SkBitmap bitmap;
bitmap.allocN32Pixels(CameraEffectsController::kImageAsIconWidth,
CameraEffectsController::kImageAsIconWidth);
bitmap.eraseColor(color);
return SkBitmapToString(bitmap);
}
auto BackgroundImageInfoMatcher(const base::FilePath& basename,
const std::string& jpeg_bytes,
const std::string& metadata) {
return testing::AllOf(
testing::Field("basename", &BackgroundImageInfo::basename,
testing::Eq(basename)),
testing::ResultOf(
[](BackgroundImageInfo info) {
info.image.SetReadOnly();
return SkBitmapToString(*info.image.bitmap());
},
testing::Eq(jpeg_bytes)),
testing::Field("metadata", &BackgroundImageInfo::metadata,
testing::Eq(metadata)));
}
constexpr char kTestAccount[] = "testuser@gmail.com";
class CameraEffectsControllerTest : public NoSessionAshTestBase {
public:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
features::kFeatureManagementVideoConference);
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);
ASSERT_TRUE(file_tmp_dir_.CreateUniqueTempDir());
camera_background_img_dir_ =
file_tmp_dir_.GetPath().AppendASCII("camera_background_img_dir_");
camera_background_run_dir_ =
file_tmp_dir_.GetPath().AppendASCII("camera_background_run_dir_");
ASSERT_TRUE(base::CreateDirectory(camera_background_img_dir_));
ASSERT_TRUE(base::CreateDirectory(camera_background_run_dir_));
}
void TearDown() override {
NoSessionAshTestBase::TearDown();
controller_.reset();
}
void SetBackgroundBlurEffectState(std::optional<int> state) {
camera_effects_controller_->OnEffectControlActivated(
VcEffectId::kBackgroundBlur, state);
}
int GetBackgroundBlurEffectState() {
std::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);
}
std::pair<bool, std::string> GetBackgroundReplacePref() {
const auto* prefs =
Shell::Get()->session_controller()->GetActivePrefService();
return {prefs->GetBoolean(prefs::kBackgroundReplace),
prefs->GetFilePath(prefs::kBackgroundImagePath).value()};
}
void TogglePortraitRelightingEffectState() {
camera_effects_controller_->OnEffectControlActivated(
VcEffectId::kPortraitRelighting, std::nullopt);
}
void ToggleCameraFramingEffectState() {
camera_effects_controller_->OnEffectControlActivated(
VcEffectId::kCameraFraming, std::nullopt);
}
bool GetPortraitRelightingEffectState() {
std::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);
}
void SetAutozoomSupportState(bool autozoom_supported) {
auto* autozoom_controller = Shell::Get()->autozoom_controller();
autozoom_controller->SetAutozoomSupported(autozoom_supported);
if (!autozoom_supported) {
return;
}
autozoom_controller->OnActiveClientChange(
cros::mojom::CameraClientType::ASH_CHROME,
true,
{"fake_id"});
}
CameraEffectsController* camera_effects_controller() {
return camera_effects_controller_;
}
FakeVideoConferenceTrayController* tray_controller() {
return controller_.get();
}
base::FilePath GetFileInBackgroundRunDir() {
base::FileEnumerator enumerator(camera_background_run_dir_,
false,
base::FileEnumerator::FILES);
std::vector<base::FilePath> files;
for (auto path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
files.push_back(enumerator.GetInfo().GetName());
}
CHECK_EQ(files.size(), 1u);
return files[0];
}
protected:
const SeaPenImage content1_ =
SeaPenImage(CreateJpgBytes(SK_ColorBLACK), 12345);
const SeaPenImage content2_ = SeaPenImage(CreateJpgBytes(SK_ColorWHITE), 888);
const std::string metadata1_ = "metadata1_";
const std::string metadata2_ = "metadata2_";
const base::FilePath filename1_ = base::FilePath("12345.jpg");
const base::FilePath filename2_ = base::FilePath("888.jpg");
const base::FilePath metadata_filename1_ =
filename1_.AddExtensionASCII(kMetadataSuffix);
base::FilePath metadata_filename2_ =
filename2_.AddExtensionASCII(kMetadataSuffix);
base::ScopedTempDir file_tmp_dir_;
base::FilePath camera_background_img_dir_;
base::FilePath camera_background_run_dir_;
raw_ptr<CameraEffectsController, DanglingUntriaged>
camera_effects_controller_ = nullptr;
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
base::test::ScopedFeatureList scoped_feature_list_;
base::WeakPtrFactory<CameraEffectsControllerTest> weak_factory_{this};
};
TEST_F(CameraEffectsControllerTest, IsEffectControlAvailable) {
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{}, {features::kFeatureManagementVideoConference});
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::kFeatureManagementVideoConference}, {});
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundBlur));
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kPortraitRelight));
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundReplace));
}
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kFeatureManagementVideoConference},
{features::kVcPortraitRelight});
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundBlur));
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kPortraitRelight));
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
cros::mojom::CameraEffect::kBackgroundReplace));
}
{
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kFeatureManagementVideoConference},
{features::kVcBackgroundReplace});
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));
}
}
TEST_F(CameraEffectsControllerTest, BackgroundBlurOnEffectControlActivated) {
SimulateUserLogin({kTestAccount});
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(100);
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
SetBackgroundBlurEffectState(
CameraEffectsController::BackgroundBlurPrefValue::kMaximum);
SetBackgroundBlurEffectState(std::nullopt);
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
EXPECT_EQ(GetBackgroundBlurEffectState(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
}
TEST_F(CameraEffectsControllerTest,
PortraitRelightingOnEffectControlActivated) {
SimulateUserLogin({kTestAccount});
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({kTestAccount});
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({kTestAccount});
auto* background_blur =
camera_effects_controller()->GetEffectById(VcEffectId::kBackgroundBlur);
EXPECT_EQ(VcHostedEffect::ResourceDependency::kCamera,
background_blur->dependency_flags());
if (features::IsVcStudioLookEnabled()) {
auto* studio_look =
camera_effects_controller()->GetEffectById(VcEffectId::kStudioLook);
EXPECT_EQ(VcHostedEffect::ResourceDependency::kCamera,
studio_look->dependency_flags());
} else {
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({kTestAccount});
VideoConferenceMediaState state;
state.has_media_app = true;
state.has_camera_permission = true;
state.has_microphone_permission = true;
state.is_capturing_screen = true;
tray_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);
}
TEST_F(CameraEffectsControllerTest, CameraFramingSupportState) {
SimulateUserLogin({kTestAccount});
EXPECT_FALSE(
camera_effects_controller()->GetEffectById(VcEffectId::kCameraFraming));
SetAutozoomSupportState(true);
EXPECT_TRUE(
camera_effects_controller()->GetEffectById(VcEffectId::kCameraFraming));
SetAutozoomSupportState(false);
EXPECT_FALSE(
camera_effects_controller()->GetEffectById(VcEffectId::kCameraFraming));
}
TEST_F(CameraEffectsControllerTest, CameraFramingToggle) {
SetAutozoomSupportState(true);
SimulateUserLogin({kTestAccount});
ASSERT_EQ(Shell::Get()->autozoom_controller()->GetState(),
cros::mojom::CameraAutoFramingState::OFF);
ToggleCameraFramingEffectState();
EXPECT_EQ(Shell::Get()->autozoom_controller()->GetState(),
cros::mojom::CameraAutoFramingState::ON_SINGLE);
ToggleCameraFramingEffectState();
EXPECT_EQ(Shell::Get()->autozoom_controller()->GetState(),
cros::mojom::CameraAutoFramingState::OFF);
}
TEST_F(CameraEffectsControllerTest, SetBackgroundImageWithFileExists) {
base::test::ScopedFeatureList scoped_feature_list{
features::kVcBackgroundReplace};
SimulateUserLogin({kTestAccount});
camera_effects_controller()->set_camera_background_img_dir_for_testing(
camera_background_img_dir_);
camera_effects_controller()->set_camera_background_run_dir_for_testing(
camera_background_run_dir_);
const auto state = CameraEffectsController::BackgroundBlurPrefValue::kLowest;
SetBackgroundBlurEffectState(state);
EXPECT_EQ(GetBackgroundBlurPref(), state);
const std::string relative_path = "background/test.png";
base::FilePath file_fullpath =
camera_background_img_dir_.Append(relative_path);
ASSERT_TRUE(base::CreateDirectory(file_fullpath.DirName()));
ASSERT_TRUE(base::WriteFile(file_fullpath, ""));
camera_effects_controller()->SetBackgroundImage(
base::FilePath(relative_path),
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(true, relative_path));
EXPECT_EQ(GetBackgroundBlurPref(),
CameraEffectsController::BackgroundBlurPrefValue::kOff);
SetBackgroundBlurEffectState(state);
EXPECT_EQ(GetBackgroundBlurPref(), state);
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(false, ""));
EXPECT_FALSE(camera_effects_controller()
->GetCameraEffects()
->background_filepath.has_value());
camera_effects_controller()->SetBackgroundImage(
base::FilePath(relative_path),
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(true, relative_path));
const auto off_state = CameraEffectsController::BackgroundBlurPrefValue::kOff;
SetBackgroundBlurEffectState(off_state);
EXPECT_EQ(GetBackgroundBlurPref(), off_state);
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(false, ""));
}
TEST_F(CameraEffectsControllerTest, SetBackgroundImageWithFileDoesNotExist) {
base::test::ScopedFeatureList scoped_feature_list{
features::kVcBackgroundReplace};
SimulateUserLogin({kTestAccount});
camera_effects_controller()->set_camera_background_img_dir_for_testing(
camera_background_img_dir_);
camera_effects_controller()->set_camera_background_run_dir_for_testing(
camera_background_run_dir_);
const auto state = CameraEffectsController::BackgroundBlurPrefValue::kLowest;
SetBackgroundBlurEffectState(state);
EXPECT_EQ(GetBackgroundBlurPref(), state);
camera_effects_controller()->SetBackgroundImage(
filename1_, base::BindOnce([](bool call_succeeded) {
EXPECT_FALSE(call_succeeded);
}));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(false, ""));
EXPECT_EQ(GetBackgroundBlurPref(), state);
}
TEST_F(CameraEffectsControllerTest, SetBackgroundImageFromContent) {
base::test::ScopedFeatureList scoped_feature_list{
features::kVcBackgroundReplace};
SimulateUserLogin({kTestAccount});
camera_effects_controller()->set_camera_background_img_dir_for_testing(
camera_background_img_dir_);
camera_effects_controller()->set_camera_background_run_dir_for_testing(
camera_background_run_dir_);
camera_effects_controller()->SetBackgroundImageFromContent(
content1_, metadata1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(),
testing::Pair(true, filename1_.value()));
camera_effects_controller()->GetRecentlyUsedBackgroundImages(
3, base::BindLambdaForTesting(
[&](const std::vector<BackgroundImageInfo>& info) {
EXPECT_THAT(info,
ElementsAre(BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, metadata1_)));
EXPECT_EQ(GetFileInBackgroundRunDir(), filename1_);
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->SetBackgroundImageFromContent(
content2_, metadata2_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(),
testing::Pair(true, filename2_.value()));
camera_effects_controller()->GetRecentlyUsedBackgroundImages(
3, base::BindLambdaForTesting(
[&](const std::vector<BackgroundImageInfo>& info) {
EXPECT_THAT(
info, ElementsAre(
BackgroundImageInfoMatcher(
filename2_, content2_.jpg_bytes, metadata2_),
BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, metadata1_)));
EXPECT_EQ(GetFileInBackgroundRunDir(), filename2_);
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->SetBackgroundImage(
filename1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(),
testing::Pair(true, filename1_.value()));
camera_effects_controller()->GetRecentlyUsedBackgroundImages(
3, base::BindLambdaForTesting(
[&](const std::vector<BackgroundImageInfo>& info) {
EXPECT_THAT(
info, ElementsAre(
BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, metadata1_),
BackgroundImageInfoMatcher(
filename2_, content2_.jpg_bytes, metadata2_)));
EXPECT_EQ(GetFileInBackgroundRunDir(), filename1_);
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->RemoveBackgroundImage(
filename2_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(),
testing::Pair(true, filename1_.value()));
camera_effects_controller()->GetRecentlyUsedBackgroundImages(
3, base::BindLambdaForTesting(
[&](const std::vector<BackgroundImageInfo>& info) {
EXPECT_THAT(info,
ElementsAre(BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, metadata1_)));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->RemoveBackgroundImage(
filename1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
EXPECT_THAT(GetBackgroundReplacePref(), testing::Pair(false, ""));
camera_effects_controller()->GetRecentlyUsedBackgroundImages(
3, base::BindLambdaForTesting(
[&](const std::vector<BackgroundImageInfo>& info) {
EXPECT_TRUE(info.empty());
}));
task_environment()->RunUntilIdle();
}
TEST_F(CameraEffectsControllerTest, GetBackgroundImageFileNames) {
base::test::ScopedFeatureList scoped_feature_list{
features::kVcBackgroundReplace};
SimulateUserLogin({kTestAccount});
camera_effects_controller()->set_camera_background_img_dir_for_testing(
camera_background_img_dir_);
camera_effects_controller()->set_camera_background_run_dir_for_testing(
camera_background_run_dir_);
camera_effects_controller()->SetBackgroundImageFromContent(
content1_, metadata1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageFileNames(
base::BindLambdaForTesting([&](const std::vector<base::FilePath>& files) {
EXPECT_THAT(files, ElementsAre(filename1_));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->SetBackgroundImageFromContent(
content2_, metadata2_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageFileNames(
base::BindLambdaForTesting([&](const std::vector<base::FilePath>& files) {
EXPECT_THAT(files, ElementsAre(filename2_, filename1_));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->SetBackgroundImage(
filename1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageFileNames(
base::BindLambdaForTesting([&](const std::vector<base::FilePath>& files) {
EXPECT_THAT(files, ElementsAre(filename1_, filename2_));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->RemoveBackgroundImage(
filename1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageFileNames(
base::BindLambdaForTesting([&](const std::vector<base::FilePath>& files) {
EXPECT_THAT(files, ElementsAre(filename2_));
}));
task_environment()->RunUntilIdle();
}
TEST_F(CameraEffectsControllerTest, GetBackgroundImageInfo) {
base::test::ScopedFeatureList scoped_feature_list{
features::kVcBackgroundReplace};
SimulateUserLogin({kTestAccount});
camera_effects_controller()->set_camera_background_img_dir_for_testing(
camera_background_img_dir_);
camera_effects_controller()->set_camera_background_run_dir_for_testing(
camera_background_run_dir_);
camera_effects_controller()->SetBackgroundImageFromContent(
content1_, metadata1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageInfo(
filename1_, base::BindLambdaForTesting(
[&](const std::optional<BackgroundImageInfo>& info) {
EXPECT_THAT(
info.value(),
BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, metadata1_));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->RemoveBackgroundImage(
metadata_filename1_,
base::BindOnce([](bool call_succeeded) { EXPECT_TRUE(call_succeeded); }));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageInfo(
filename1_, base::BindLambdaForTesting(
[&](const std::optional<BackgroundImageInfo>& info) {
EXPECT_THAT(info.value(),
BackgroundImageInfoMatcher(
filename1_, content1_.jpg_bytes, ""));
}));
task_environment()->RunUntilIdle();
camera_effects_controller()->GetBackgroundImageInfo(
filename2_,
base::BindOnce([](const std::optional<BackgroundImageInfo>& info) {
EXPECT_FALSE(info.has_value());
}));
task_environment()->RunUntilIdle();
}
TEST_F(CameraEffectsControllerTest, NotEligibleForSeaPen) {
GetSessionControllerClient()->set_is_eligible_for_background_replace(
{false, false});
SimulateUserLogin({kTestAccount});
VideoConferenceMediaState state;
state.has_media_app = true;
state.has_camera_permission = true;
state.has_microphone_permission = true;
state.is_capturing_screen = true;
tray_controller()->UpdateWithMediaState(state);
auto effects = VideoConferenceTrayController::Get()
->GetEffectsManager()
.GetSetValueEffects();
EXPECT_EQ(effects.size(), 1u);
EXPECT_EQ(effects[0]->label_text(), u"Background");
EXPECT_EQ(effects[0]->GetNumStates(), 3);
}
TEST_F(CameraEffectsControllerTest, UpdateBackgroundBlurImageState) {
GetSessionControllerClient()->set_is_eligible_for_background_replace(
{false, false});
SimulateUserLogin({kTestAccount});
VideoConferenceMediaState state;
state.has_media_app = true;
state.has_camera_permission = true;
state.has_microphone_permission = true;
state.is_capturing_screen = true;
tray_controller()->UpdateWithMediaState(state);
auto effects = VideoConferenceTrayController::Get()
->GetEffectsManager()
.GetSetValueEffects();
EXPECT_EQ(effects.size(), 1u);
EXPECT_EQ(effects[0]->label_text(), u"Background");
EXPECT_EQ(effects[0]->GetNumStates(), 3);
GetSessionControllerClient()->set_is_eligible_for_background_replace(
{true, false});
auto* vc_tray = StatusAreaWidgetTestHelper::GetStatusAreaWidget()
->video_conference_tray();
LeftClickOn(vc_tray->toggle_bubble_button());
effects = VideoConferenceTrayController::Get()
->GetEffectsManager()
.GetSetValueEffects();
EXPECT_EQ(effects[0]->GetNumStates(), 4) << " four states are constructed";
const VcEffectState* imageState = effects[0]->GetState(3);
EXPECT_EQ(imageState->view_id(),
video_conference::BubbleViewID::kBackgroundBlurImageButton);
EXPECT_TRUE(imageState->is_disabled_by_enterprise());
GetSessionControllerClient()->set_is_eligible_for_background_replace(
{true, true});
LeftClickOn(vc_tray->toggle_bubble_button());
LeftClickOn(vc_tray->toggle_bubble_button());
effects = VideoConferenceTrayController::Get()
->GetEffectsManager()
.GetSetValueEffects();
EXPECT_EQ(effects[0]->GetNumStates(), 4)
<< "still four states for Background Blur effect";
const VcEffectState* newImageState = effects[0]->GetState(3);
EXPECT_EQ(newImageState->view_id(),
video_conference::BubbleViewID::kBackgroundBlurImageButton);
EXPECT_FALSE(newImageState->is_disabled_by_enterprise());
}
}