// 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.
#ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_TEST_UTIL_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_TEST_UTIL_H_
#include <string>
#include "ash/annotator/annotator_test_util.h"
#include "ash/capture_mode/capture_mode_camera_controller.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/test_capture_mode_delegate.h"
#include "ash/capture_mode/user_nudge_controller.h"
#include "ash/public/cpp/test/mock_projector_client.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "ui/events/event_constants.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
namespace base {
class FilePath;
} // namespace base
namespace gfx {
class Image;
} // namespace gfx
namespace ui::test {
class EventGenerator;
} // namespace ui::test
namespace views {
class View;
} // namespace views
// Functions that are used by capture mode related unit tests and only meant to
// be used in ash_unittests.
namespace ash {
class PillButton;
class IconButton;
class CaptureModeController;
class CaptureModeBarView;
class TabSliderButton;
// Fake camera info used for testing.
constexpr char kDefaultCameraDeviceId[] = "/dev/videoX";
constexpr char kDefaultCameraModelId[] = "0def:c000";
// Starts the capture mode session with given `source` and `type`.
CaptureModeController* StartCaptureSession(CaptureModeSource source,
CaptureModeType type);
// Returns the test delegate of `CaptureModeController`.
TestCaptureModeDelegate* GetTestDelegate();
void ClickOnView(const views::View* view,
ui::test::EventGenerator* event_generator);
// Waits until the recording is in progress.
void WaitForRecordingToStart();
// Starts recording immediately without the 3-seconds count down.
void StartVideoRecordingImmediately();
// Returns the whole file path where the screen capture file is saved to. The
// returned file path could be either under the default downloads folder or the
// custom folder.
base::FilePath WaitForCaptureFileToBeSaved();
// Creates and returns the custom folder path. The custom folder is created in
// the default downloads folder with given `custom_folder_name`.
base::FilePath CreateCustomFolderInUserDownloadsPath(
const std::string& custom_folder_name);
// Creates and returns the custom folder path on driveFS. The custom folder is
// created in the root folder with given `custom_folder_name`.
base::FilePath CreateFolderOnDriveFS(const std::string& custom_folder_name);
// Wait for a specific `seconds`.
void WaitForSeconds(int seconds);
// To avoid flaky failures due to mouse devices blocking entering tablet mode,
// we detach all mouse devices. This shouldn't affect testing the cursor
// status.
void SwitchToTabletMode();
// Leaves the tablet mode.
void LeaveTabletMode();
// Open the `view` by touch.
void TouchOnView(const views::View* view,
ui::test::EventGenerator* event_generator);
// Clicks or taps on the `view` based on whether the user is in clamshell or
// tablet mode.
void ClickOrTapView(const views::View* view,
bool in_table_mode,
ui::test::EventGenerator* event_generator);
views::Widget* GetCaptureModeBarWidget();
CaptureModeBarView* GetCaptureModeBarView();
UserNudgeController* GetUserNudgeController();
bool IsLayerStackedRightBelow(ui::Layer* layer, ui::Layer* sibling);
// Sets the device scale factor for only the first available display.
void SetDeviceScaleFactor(float dsf);
// Enables the auto click accessibility feature, and returns the auto click
// bubble widget.
views::Widget* EnableAndGetAutoClickBubbleWidget();
// Functions to simulate triggering key events from the virtual keyboard.
void PressKeyOnVK(ui::test::EventGenerator* event_generator,
ui::KeyboardCode key_code,
int flags,
int source_device_id = ui::ED_UNKNOWN_DEVICE);
void ReleaseKeyOnVK(ui::test::EventGenerator* event_generator,
ui::KeyboardCode key_code,
int flags,
int source_device_id = ui::ED_UNKNOWN_DEVICE);
void PressAndReleaseKeyOnVK(ui::test::EventGenerator* event_generator,
ui::KeyboardCode key_code,
int flags = ui::EF_NONE,
int source_device_id = ui::ED_UNKNOWN_DEVICE);
// Reads a PNG image from disk and decodes it. Returns the bitmap image, if the
// bitmap was successfully read from disk or an empty gfx::Image otherwise.
gfx::Image ReadAndDecodeImageFile(const base::FilePath& image_path);
// Gets the buttons inside the capture bar view.
TabSliderButton* GetImageToggleButton();
TabSliderButton* GetVideoToggleButton();
TabSliderButton* GetFullscreenToggleButton();
TabSliderButton* GetRegionToggleButton();
TabSliderButton* GetWindowToggleButton();
PillButton* GetStartRecordingButton();
IconButton* GetSettingsButton();
IconButton* GetCloseButton();
// Returns the capture mode related notifications from the message center.
const message_center::Notification* GetPreviewNotification();
// Clicks on the area in the notification specified by the `button_index`.
void ClickOnNotification(std::optional<int> button_index);
// Test util APIs to simulate the camera adding and removing operations.
void AddFakeCamera(
const std::string& device_id,
const std::string& display_name,
const std::string& model_id,
media::VideoFacingMode camera_facing_mode = media::MEDIA_VIDEO_FACING_NONE);
void RemoveFakeCamera(const std::string& device_id);
void AddDefaultCamera();
void RemoveDefaultCamera();
// Waits until at least one camera becomes available, up to the specified
// `time_out`. Returns the number of available cameras, or 0 if none are
// found within the time limit.
size_t WaitForCameraAvailabilityWithTimeout(base::TimeDelta time_out);
// Select a region by pressing and dragging the mouse.
void SelectCaptureModeRegion(ui::test::EventGenerator* event_generator,
const gfx::Rect& region_in_screen,
bool release_mouse = true);
// Defines a helper class to allow setting up and testing the Projector feature
// in multiple test fixtures. Note that this helper initializes the Projector-
// related features in its constructor, so test fixtures that use this should
// also initialize their `ScopedFeatureList` in their constructors to avoid
// DCHECKing when nested ScopedFeatureLists being destroyed in a different order
// than they are initialized.
class ProjectorCaptureModeIntegrationHelper {
public:
ProjectorCaptureModeIntegrationHelper();
ProjectorCaptureModeIntegrationHelper(
const ProjectorCaptureModeIntegrationHelper&) = delete;
ProjectorCaptureModeIntegrationHelper& operator=(
const ProjectorCaptureModeIntegrationHelper&) = delete;
~ProjectorCaptureModeIntegrationHelper() = default;
MockProjectorClient* projector_client() { return &projector_client_; }
// Sets up the projector feature. Must be called after `AshTestBase::SetUp()`
// has been called.
void SetUp();
bool CanStartProjectorSession() const;
// Starts a new projector capture session.
void StartProjectorModeSession();
private:
MockProjectorClient projector_client_;
AnnotatorIntegrationHelper annotator_helper_;
};
// Defines a waiter to observe the visibility change of the view.
class ViewVisibilityChangeWaiter : public views::ViewObserver {
public:
explicit ViewVisibilityChangeWaiter(views::View* view);
ViewVisibilityChangeWaiter(const ViewVisibilityChangeWaiter&) = delete;
ViewVisibilityChangeWaiter& operator=(const ViewVisibilityChangeWaiter&) =
delete;
~ViewVisibilityChangeWaiter() override;
void Wait();
// views::ViewObserver:
void OnViewVisibilityChanged(views::View* observed_view,
views::View* starting_view) override;
private:
const raw_ptr<views::View> view_;
base::RunLoop wait_loop_;
};
// Defines a waiter to observe the notification changes.
class CaptureNotificationWaiter : public message_center::MessageCenterObserver {
public:
CaptureNotificationWaiter();
~CaptureNotificationWaiter() override;
void Wait();
// message_center::MessageCenterObserver:
void OnNotificationAdded(const std::string& notification_id) override;
private:
base::RunLoop run_loop_;
};
// Defines a waiter for the camera devices change notifications.
class CameraDevicesChangeWaiter : public CaptureModeCameraController::Observer {
public:
CameraDevicesChangeWaiter();
CameraDevicesChangeWaiter(const CameraDevicesChangeWaiter&) = delete;
CameraDevicesChangeWaiter& operator=(const CameraDevicesChangeWaiter&) =
delete;
~CameraDevicesChangeWaiter() override;
int camera_change_event_count() const { return camera_change_event_count_; }
int selected_camera_change_event_count() const {
return selected_camera_change_event_count_;
}
void Wait();
// CaptureModeCameraController::Observer:
void OnAvailableCamerasChanged(const CameraInfoList& cameras) override;
void OnSelectedCameraChanged(const CameraId& camera_id) override;
private:
base::RunLoop loop_;
// Tracks the number of times the observer call `OnAvailableCamerasChanged()`
// was triggered.
int camera_change_event_count_ = 0;
// Tracks the number of times `OnSelectedCameraChanged()` was triggered.
int selected_camera_change_event_count_ = 0;
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_TEST_UTIL_H_