| // 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 <memory> |
| #include <vector> |
| |
| #include "ash/accelerators/keyboard_code_util.h" |
| #include "ash/accessibility/magnifier/docked_magnifier_controller.h" |
| #include "ash/capture_mode/capture_mode_bar_view.h" |
| #include "ash/capture_mode/capture_mode_constants.h" |
| #include "ash/capture_mode/capture_mode_controller.h" |
| #include "ash/capture_mode/capture_mode_demo_tools_controller.h" |
| #include "ash/capture_mode/capture_mode_demo_tools_test_api.h" |
| #include "ash/capture_mode/capture_mode_menu_toggle_button.h" |
| #include "ash/capture_mode/capture_mode_metrics.h" |
| #include "ash/capture_mode/capture_mode_session.h" |
| #include "ash/capture_mode/capture_mode_session_focus_cycler.h" |
| #include "ash/capture_mode/capture_mode_session_test_api.h" |
| #include "ash/capture_mode/capture_mode_settings_test_api.h" |
| #include "ash/capture_mode/capture_mode_test_util.h" |
| #include "ash/capture_mode/capture_mode_types.h" |
| #include "ash/capture_mode/capture_mode_util.h" |
| #include "ash/capture_mode/key_combo_view.h" |
| #include "ash/capture_mode/pointer_highlight_layer.h" |
| #include "ash/capture_mode/recording_overlay_controller.h" |
| #include "ash/capture_mode/video_recording_watcher.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/display/window_tree_host_manager.h" |
| #include "ash/projector/projector_controller_impl.h" |
| #include "ash/public/cpp/capture_mode/capture_mode_test_api.h" |
| #include "ash/public/cpp/shelf_model.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shelf/shelf_app_button.h" |
| #include "ash/shelf/shelf_test_util.h" |
| #include "ash/shelf/shelf_view.h" |
| #include "ash/shelf/shelf_view_test_api.h" |
| #include "ash/shell.h" |
| #include "ash/style/icon_button.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wm/splitview/split_view_controller.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/timer/timer.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/base/ime/fake_text_input_client.h" |
| #include "ui/base/ime/text_input_type.h" |
| #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/events/keycodes/keyboard_codes_posix.h" |
| #include "ui/events/pointer_details.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/point_f.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/vector2d.h" |
| #include "ui/views/controls/image_view.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr ui::KeyboardCode kIconKeyCodes[] = {ui::VKEY_BROWSER_BACK, |
| ui::VKEY_BROWSER_FORWARD, |
| ui::VKEY_BROWSER_REFRESH, |
| ui::VKEY_ZOOM, |
| ui::VKEY_MEDIA_LAUNCH_APP1, |
| ui::VKEY_BRIGHTNESS_DOWN, |
| ui::VKEY_BRIGHTNESS_UP, |
| ui::VKEY_VOLUME_MUTE, |
| ui::VKEY_VOLUME_DOWN, |
| ui::VKEY_VOLUME_UP, |
| ui::VKEY_UP, |
| ui::VKEY_DOWN, |
| ui::VKEY_LEFT, |
| ui::VKEY_RIGHT}; |
| |
| } // namespace |
| |
| class CaptureModeDemoToolsTest : public AshTestBase { |
| public: |
| CaptureModeDemoToolsTest() { |
| scoped_feature_list_.InitAndEnableFeature(features::kCaptureModeDemoTools); |
| } |
| CaptureModeDemoToolsTest(const CaptureModeDemoToolsTest&) = delete; |
| CaptureModeDemoToolsTest& operator=(const CaptureModeDemoToolsTest&) = delete; |
| ~CaptureModeDemoToolsTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| window_ = CreateTestWindow(gfx::Rect(20, 30, 601, 300)); |
| |
| // Focus on non-input-text field at beginning. |
| fake_text_input_client_ = |
| std::make_unique<ui::FakeTextInputClient>(ui::TEXT_INPUT_TYPE_NONE); |
| } |
| |
| void TearDown() override { |
| window_.reset(); |
| AshTestBase::TearDown(); |
| } |
| |
| aura::Window* window() const { return window_.get(); } |
| |
| gfx::Rect GetConfineBoundsInScreenCoordinates() { |
| auto* recording_watcher = |
| CaptureModeController::Get()->video_recording_watcher_for_testing(); |
| gfx::Rect confine_bounds_in_screen = |
| recording_watcher->GetCaptureSurfaceConfineBounds(); |
| wm::ConvertRectToScreen(recording_watcher->window_being_recorded(), |
| &confine_bounds_in_screen); |
| return confine_bounds_in_screen; |
| } |
| |
| // Verifies that the `key_combo_widget` is positioned in the middle |
| // horizontally within the confine bounds and that the distance between the |
| // bottom of the widget and the bottom of the confine bounds is always equal |
| // to `capture_mode::kKeyWidgetDistanceFromBottom`. |
| void VerifyKeyComboWidgetPosition() { |
| CaptureModeDemoToolsTestApi demo_tools_test_api( |
| GetCaptureModeDemoToolsController()); |
| auto* key_combo_widget = demo_tools_test_api.GetKeyComboWidget(); |
| ASSERT_TRUE(key_combo_widget); |
| auto confine_bounds_in_screen = GetConfineBoundsInScreenCoordinates(); |
| const gfx::Rect key_combo_widget_bounds = |
| key_combo_widget->GetWindowBoundsInScreen(); |
| EXPECT_NEAR(confine_bounds_in_screen.CenterPoint().x(), |
| key_combo_widget_bounds.CenterPoint().x(), /*abs_error=*/1); |
| EXPECT_EQ( |
| confine_bounds_in_screen.bottom() - key_combo_widget_bounds.bottom(), |
| capture_mode::kKeyWidgetDistanceFromBottom); |
| } |
| |
| IconButton* GetSettingsButton() const { |
| return GetCaptureModeBarView()->settings_button(); |
| } |
| |
| views::Widget* GetCaptureModeSettingsWidget() const { |
| auto* session = CaptureModeController::Get()->capture_mode_session(); |
| DCHECK(session); |
| return CaptureModeSessionTestApi(session).GetCaptureModeSettingsWidget(); |
| } |
| |
| CaptureModeDemoToolsController* GetCaptureModeDemoToolsController() const { |
| auto* recording_watcher = |
| CaptureModeController::Get()->video_recording_watcher_for_testing(); |
| DCHECK(recording_watcher); |
| return recording_watcher->demo_tools_controller_for_testing(); |
| } |
| |
| void WaitForMouseHighlightAnimationCompleted() { |
| base::RunLoop run_loop; |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| DCHECK(demo_tools_controller); |
| CaptureModeDemoToolsTestApi capture_mode_demo_tools_test_api( |
| demo_tools_controller); |
| capture_mode_demo_tools_test_api.SetOnMouseHighlightAnimationEndedCallback( |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| // Fires the key combo viewer timer and verifies the existence of the widget |
| // after the timer expires. |
| void FireTimerAndVerifyWidget(bool should_hide_view) { |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| DCHECK(demo_tools_controller); |
| CaptureModeDemoToolsTestApi capture_mode_demo_tools_test_api( |
| demo_tools_controller); |
| auto* timer = capture_mode_demo_tools_test_api.GetRefreshKeyComboTimer(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| should_hide_view |
| ? capture_mode::kRefreshKeyComboWidgetLongDelay |
| : capture_mode::kRefreshKeyComboWidgetShortDelay); |
| KeyComboView* key_combo_view = |
| capture_mode_demo_tools_test_api.GetKeyComboView(); |
| ViewVisibilityChangeWaiter waiter(key_combo_view); |
| timer->FireNow(); |
| |
| if (should_hide_view) { |
| waiter.Wait(); |
| EXPECT_FALSE(capture_mode_demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_FALSE(capture_mode_demo_tools_test_api.GetKeyComboView()); |
| } |
| } |
| |
| void EnableTextInputFocus(ui::TextInputType input_type) { |
| fake_text_input_client_->set_text_input_type(input_type); |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->input_method() |
| ->SetFocusedTextInputClient(fake_text_input_client_.get()); |
| } |
| |
| void DisableTextInputFocus() { |
| fake_text_input_client_->set_text_input_type(ui::TEXT_INPUT_TYPE_NONE); |
| Shell::Get() |
| ->window_tree_host_manager() |
| ->input_method() |
| ->SetFocusedTextInputClient(nullptr); |
| } |
| |
| void DragTouchAndVerifyHighlight(const ui::PointerId& touch_id, |
| const gfx::Point& touch_point, |
| const gfx::Vector2d& drag_offset) { |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressTouchId(touch_id, touch_point); |
| CaptureModeDemoToolsTestApi demo_tools_test_api( |
| GetCaptureModeDemoToolsController()); |
| const auto& touch_highlight_map = |
| demo_tools_test_api.GetTouchIdToHighlightLayerMap(); |
| const auto iter = |
| touch_highlight_map.find(static_cast<ui::PointerId>(touch_id)); |
| ASSERT_TRUE(iter != touch_highlight_map.end()); |
| const auto* touch_highlight = iter->second.get(); |
| auto original_touch_highlight_bounds = touch_highlight->layer()->bounds(); |
| auto* recording_watcher = |
| CaptureModeController::Get()->video_recording_watcher_for_testing(); |
| wm::ConvertRectToScreen(recording_watcher->window_being_recorded(), |
| &original_touch_highlight_bounds); |
| event_generator->MoveTouchBy(drag_offset.x(), drag_offset.y()); |
| gfx::PointF updated_event_location{ |
| event_generator->current_screen_location()}; |
| const auto expected_touch_highlight_layer_bounds = |
| capture_mode_util::CalculateHighlightLayerBounds( |
| updated_event_location, |
| capture_mode::kHighlightLayerRadius + |
| capture_mode::kInnerHightlightBorderThickness + |
| capture_mode::kOuterHightlightBorderThickness); |
| auto actual_touch_highlight_layer_bounds = original_touch_highlight_bounds; |
| actual_touch_highlight_layer_bounds.Offset(drag_offset.x(), |
| drag_offset.y()); |
| EXPECT_EQ(expected_touch_highlight_layer_bounds, |
| actual_touch_highlight_layer_bounds); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<aura::Window> window_; |
| std::unique_ptr<ui::FakeTextInputClient> fake_text_input_client_; |
| }; |
| |
| // Tests that the key event is considered to generate the `key_combo_widget_` |
| // or ignored otherwise in a correct way. |
| TEST_F(CaptureModeDemoToolsTest, ConsiderKeyEvent) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(GetSettingsButton(), event_generator); |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| Switch* toggle_button = CaptureModeSettingsTestApi() |
| .GetDemoToolsMenuToggleButton() |
| ->toggle_button(); |
| |
| // The toggle button will be disabled by default, toggle the toggle button to |
| // enable the demo tools feature. |
| EXPECT_FALSE(toggle_button->GetIsOn()); |
| ClickOnView(toggle_button, event_generator); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| |
| // Press the 'A' key and the event will not be considered to generate a |
| // corresponding key widget. |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboWidget()); |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| EXPECT_EQ(demo_tools_test_api.GetCurrentModifiersFlags(), 0); |
| EXPECT_EQ(demo_tools_test_api.GetLastNonModifierKey(), ui::VKEY_UNKNOWN); |
| |
| // Press 'Ctrl' + 'A' and the key event will be considered to generate a |
| // corresponding key widget. |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_EQ(demo_tools_test_api.GetCurrentModifiersFlags(), |
| ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(demo_tools_test_api.GetLastNonModifierKey(), ui::VKEY_A); |
| |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| base::OneShotTimer* timer = demo_tools_test_api.GetRefreshKeyComboTimer(); |
| EXPECT_TRUE(timer->IsRunning()); |
| timer->FireNow(); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_EQ(demo_tools_test_api.GetCurrentModifiersFlags(), 0); |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| |
| event_generator->PressKey(ui::VKEY_TAB, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_EQ(demo_tools_test_api.GetCurrentModifiersFlags(), 0); |
| EXPECT_EQ(demo_tools_test_api.GetLastNonModifierKey(), ui::VKEY_TAB); |
| } |
| |
| // Tests that the capture mode demo tools feature will be enabled if the |
| // toggle button is enabled and disabled otherwise. |
| TEST_F(CaptureModeDemoToolsTest, EntryPointTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(GetSettingsButton(), event_generator); |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| Switch* toggle_button = CaptureModeSettingsTestApi() |
| .GetDemoToolsMenuToggleButton() |
| ->toggle_button(); |
| |
| // The toggle button will be disabled by default. |
| EXPECT_FALSE(toggle_button->GetIsOn()); |
| |
| // Toggle the demo tools toggle button to enable the feature and start the |
| // video recording. The modifier key down event will be handled and the key |
| // combo viewer widget will be displayed. |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| ClickOnView(toggle_button, event_generator); |
| EXPECT_TRUE(toggle_button->GetIsOn()); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| EXPECT_FALSE(controller->IsActive()); |
| |
| // Start another capture mode session and the demo tools toggle button will be |
| // enabled. Toggle the toggle button to disable the feature. The modifier key |
| // down event will not be handled when video recording starts. |
| controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| ClickOnView(GetSettingsButton(), event_generator); |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| toggle_button = CaptureModeSettingsTestApi() |
| .GetDemoToolsMenuToggleButton() |
| ->toggle_button(); |
| EXPECT_TRUE(toggle_button->GetIsOn()); |
| ClickOnView(toggle_button, event_generator); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| EXPECT_FALSE(GetCaptureModeDemoToolsController()); |
| } |
| |
| // Tests that the demo tools button is navigated and toggled correctly with |
| // keyboard in the settings menu. |
| TEST_F(CaptureModeDemoToolsTest, EntryPointFocusCyclerTest) { |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| auto* event_generator = GetEventGenerator(); |
| using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup; |
| CaptureModeSessionTestApi session_test_api( |
| controller->capture_mode_session()); |
| |
| // Check the initial focus of the focus ring. |
| EXPECT_EQ(FocusGroup::kNone, session_test_api.GetCurrentFocusGroup()); |
| |
| // Tab 6 times to reach the settings button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/6); |
| EXPECT_EQ(FocusGroup::kSettingsClose, |
| session_test_api.GetCurrentFocusGroup()); |
| EXPECT_TRUE(CaptureModeSessionFocusCycler::HighlightHelper::Get( |
| session_test_api.GetCaptureModeBarView()->settings_button()) |
| ->has_focus()); |
| |
| // Press the space key and the settings menu will be opened. |
| SendKey(ui::VKEY_SPACE, event_generator, ui::EF_NONE); |
| EXPECT_TRUE(session_test_api.GetCaptureModeSettingsView()); |
| EXPECT_EQ(FocusGroup::kPendingSettings, |
| session_test_api.GetCurrentFocusGroup()); |
| |
| // Tab 4 times to reach the demo tools toggle button. |
| SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/4); |
| EXPECT_EQ(FocusGroup::kSettingsMenu, session_test_api.GetCurrentFocusGroup()); |
| |
| Switch* toggle_button = CaptureModeSettingsTestApi() |
| .GetDemoToolsMenuToggleButton() |
| ->toggle_button(); |
| |
| // The demo tools toggle button will be disabled by default. |
| EXPECT_FALSE(toggle_button->GetIsOn()); |
| |
| // Press the space key to enable the toggle button. |
| SendKey(ui::VKEY_SPACE, event_generator, ui::EF_NONE); |
| EXPECT_TRUE(toggle_button->GetIsOn()); |
| |
| // Press the escape key and the focus will return to the settings button. |
| SendKey(ui::VKEY_ESCAPE, event_generator, ui::EF_NONE); |
| EXPECT_EQ(FocusGroup::kSettingsClose, |
| session_test_api.GetCurrentFocusGroup()); |
| EXPECT_TRUE(CaptureModeSessionFocusCycler::HighlightHelper::Get( |
| session_test_api.GetCaptureModeBarView()->settings_button()) |
| ->has_focus()); |
| } |
| |
| // Tests that the demo tools toggle button will be hidden when starting another |
| // capture mode session during video recording. |
| TEST_F(CaptureModeDemoToolsTest, ToggleButtonHiddenWhileInRecording) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| auto* event_generator = GetEventGenerator(); |
| ClickOnView(GetSettingsButton(), event_generator); |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| EXPECT_TRUE(CaptureModeSettingsTestApi().GetDemoToolsMenuToggleButton()); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| |
| controller->Start(CaptureModeEntryType::kQuickSettings); |
| |
| ClickOnView(GetSettingsButton(), event_generator); |
| EXPECT_TRUE(GetCaptureModeSettingsWidget()); |
| EXPECT_FALSE(CaptureModeSettingsTestApi().GetDemoToolsMenuToggleButton()); |
| } |
| |
| // Tests that the key combo viewer widget displays the expected contents on key |
| // event and the modifier key should always be displayed before the non-modifier |
| // key. With no modifier keys or no non-modifier key that can be displayed |
| // independently, the key combo widget will not be displayed. |
| TEST_F(CaptureModeDemoToolsTest, KeyComboWidgetTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_C, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboView()); |
| std::vector<ui::KeyboardCode> expected_modifier_key_vector = { |
| ui::VKEY_CONTROL}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_C); |
| |
| // Press the key 'Shift' at last, but it will still show before the 'C' key. |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| expected_modifier_key_vector = {ui::VKEY_CONTROL, ui::VKEY_SHIFT}; |
| EXPECT_TRUE(demo_tools_test_api.GetShownModifiersKeyCodes() == |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_C); |
| |
| // Release the modifier keys, and the key combo view will hide after the |
| // refresh timer expires. |
| event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboWidget()); |
| } |
| |
| // Tests the timer behaviors for the key combo view: |
| // 1. The refresh timer will be triggered on key up of the non-modifier key with |
| // no modifier keys pressed, the key combo view will hide after the timer |
| // expires; |
| // 2. The refresh timer will also be triggered on key up of the last modifier |
| // key with no non-modifier key that can be displayed independently pressed. The |
| // key combo view will hide after the timer expires; |
| // 3. If there is another key down event happens before the timer expires, the |
| // refresh timer stops and the key combo view will be updated to match the |
| // current keys pressed; |
| // 4. On key up while the refresh timer is still running, the key combo view |
| // will stay visible even the key states have been updated until the timer |
| // expires. |
| TEST_F(CaptureModeDemoToolsTest, DemoToolsTimerTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| // Press the 'Ctrl' + 'A' and verify the shown key widgets. |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| KeyComboView* key_combo_view = demo_tools_test_api.GetKeyComboView(); |
| EXPECT_TRUE(key_combo_view); |
| std::vector<ui::KeyboardCode> expected_modifier_key_vector = { |
| ui::VKEY_CONTROL}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_A); |
| |
| // Release the non-modifier key and the timer with a delay of |
| // `capture_mode::kRefreshKeyComboWidgetShortDelay` will be triggered, the key |
| // combo view will be updated to show 'Ctrl'. |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/false); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_UNKNOWN); |
| |
| // Release the non-modifier key with no modifier keys pressed and the hide |
| // timer will be triggered. |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| |
| // Press 'Ctrl' + 'A' and release the only modifier key 'Ctrl' and |
| // the refresh timer will be triggered. The entire key combo viewer will hide |
| // after the refresh timer expires. |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| |
| // Press 'Ctrl' + 'Shift' + 'A', then release 'A', the timer with a delay of |
| // `capture_mode::kRefreshKeyComboWidgetShortDelay` will be triggered. Press |
| // 'B' and the key combo view will be updated accordingly, i.e. 'Ctrl' + |
| // 'Shift' + 'B'. |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| expected_modifier_key_vector = {ui::VKEY_CONTROL, ui::VKEY_SHIFT}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_A); |
| event_generator->ReleaseKey(ui::VKEY_A, ui::EF_NONE); |
| base::OneShotTimer* timer = demo_tools_test_api.GetRefreshKeyComboTimer(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetShortDelay); |
| event_generator->PressKey(ui::VKEY_B, ui::EF_NONE); |
| EXPECT_FALSE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetShortDelay); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_B); |
| |
| // Release the 'Ctrl' key, the timer with a delay of |
| // `capture_mode::kRefreshKeyComboWidgetShortDelay` will be triggered. Then |
| // release the 'Shift' key and the refresh timer will be triggered The entire |
| // key combo view will hide after the timer expires. |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/false); |
| expected_modifier_key_vector = {ui::VKEY_SHIFT}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_B); |
| |
| event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetLongDelay); |
| event_generator->ReleaseKey(ui::VKEY_B, ui::EF_NONE); |
| |
| // The contents of the widget remains the same before the timer expires. |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_B); |
| |
| // The state the controller has been updated. |
| EXPECT_EQ(demo_tools_test_api.GetCurrentModifiersFlags(), 0); |
| EXPECT_EQ(demo_tools_test_api.GetLastNonModifierKey(), ui::VKEY_UNKNOWN); |
| |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| } |
| |
| // Tests that all the non-modifier keys with the icon are displayed |
| // independently and correctly. |
| TEST_F(CaptureModeDemoToolsTest, AllIconKeysTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| auto* event_generator = GetEventGenerator(); |
| |
| for (const auto key_code : kIconKeyCodes) { |
| event_generator->PressKey(key_code, ui::EF_NONE); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), key_code); |
| views::ImageView* icon = demo_tools_test_api.GetNonModifierKeyItemIcon(); |
| ASSERT_TRUE(icon); |
| const auto image_model = icon->GetImageModel(); |
| const gfx::VectorIcon* vector_icon = GetVectorIconForKeyboardCode(key_code); |
| EXPECT_EQ(std::string(vector_icon->name), |
| std::string(image_model.GetVectorIcon().vector_icon()->name)); |
| event_generator->ReleaseKey(key_code, ui::EF_NONE); |
| } |
| } |
| |
| // Tests that the key combo viewer widget will not show if the input |
| // field is currently focused and will display in a normal way when the focus is |
| // detached. |
| TEST_F(CaptureModeDemoToolsTest, DoNotShowKeyComboViewerInInputField) { |
| for (const auto input_type : |
| {ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_TYPE_PASSWORD, |
| ui::TEXT_INPUT_TYPE_SEARCH, ui::TEXT_INPUT_TYPE_EMAIL, |
| ui::TEXT_INPUT_TYPE_NUMBER, ui::TEXT_INPUT_TYPE_TELEPHONE, |
| ui::TEXT_INPUT_TYPE_URL, ui::TEXT_INPUT_TYPE_DATE, |
| ui::TEXT_INPUT_TYPE_DATE_TIME, ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL, |
| ui::TEXT_INPUT_TYPE_MONTH, ui::TEXT_INPUT_TYPE_TIME, |
| ui::TEXT_INPUT_TYPE_WEEK, ui::TEXT_INPUT_TYPE_TEXT_AREA, |
| ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE, |
| ui::TEXT_INPUT_TYPE_DATE_TIME_FIELD, ui::TEXT_INPUT_TYPE_NULL}) { |
| EnableTextInputFocus(input_type); |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| auto* event_generator = GetEventGenerator(); |
| |
| // With the input text focus enabled before the video recording, the |
| // key combo viewer will not display when pressing 'Ctrl' and 'T'. |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_T, ui::EF_NONE); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboView()); |
| event_generator->ReleaseKey(ui::VKEY_T, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| |
| // Disable the input text focus, the key combo viewer will show when |
| // pressing 'Ctrl' and 'T' in a non-input-text field. |
| DisableTextInputFocus(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_T, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_TRUE(demo_tools_test_api.GetKeyComboView()); |
| event_generator->ReleaseKey(ui::VKEY_T, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| |
| // Enable the text input focus during the recording, the key combo |
| // viewer will not display when pressing 'Ctrl' and 'T'. |
| EnableTextInputFocus(input_type); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_T, ui::EF_NONE); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboWidget()); |
| EXPECT_FALSE(demo_tools_test_api.GetKeyComboView()); |
| event_generator->ReleaseKey(ui::VKEY_T, ui::EF_NONE); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| } |
| } |
| |
| // verifies that after any key release, if the remaining pressed keys are no |
| // longer displayable, the widget will be scheduled to hide after |
| // `capture_mode::kRefreshKeyComboWidgetLongDelay`. |
| TEST_F(CaptureModeDemoToolsTest, ReleaseAllKeysConsistencyTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| auto* event_generator = GetEventGenerator(); |
| auto key_combo_generator = [&]() { |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_C, ui::EF_NONE); |
| }; |
| |
| key_combo_generator(); |
| KeyComboView* key_combo_view = demo_tools_test_api.GetKeyComboView(); |
| EXPECT_TRUE(key_combo_view); |
| |
| // Release the modifier key 'Ctrl' to trigger the timer with a delay |
| // of `capture_mode::kRefreshKeyComboWidgetShortDelay`. |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| |
| base::OneShotTimer* timer = demo_tools_test_api.GetRefreshKeyComboTimer(); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetShortDelay); |
| |
| std::vector<ui::KeyboardCode> expected_modifier_key_vector = { |
| ui::VKEY_CONTROL, ui::VKEY_SHIFT}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_C); |
| |
| // Release the modifier key 'Shift' and the refresh timer will be triggered. |
| event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetLongDelay); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_C); |
| |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| |
| // Key combo viewer update test. |
| key_combo_generator(); |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| EXPECT_TRUE(timer->IsRunning()); |
| EXPECT_EQ(timer->GetCurrentDelay(), |
| capture_mode::kRefreshKeyComboWidgetShortDelay); |
| timer->FireNow(); |
| expected_modifier_key_vector = {ui::VKEY_SHIFT}; |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_C); |
| } |
| |
| // Tests that when the key combo is 'modifier key' + 'non-modifier key that can |
| // be shown independently', on key up of either key, the key combo viewer should |
| // be updated to show the other key. When both keys are released, the refresh |
| // timer will be triggered. |
| TEST_F(CaptureModeDemoToolsTest, |
| ModifierAndIndependentlyShownNonModifierKeyComboTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(kIconKeyCodes[0], ui::EF_NONE); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| std::vector<ui::KeyboardCode>{ui::VKEY_CONTROL}); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), kIconKeyCodes[0]); |
| |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/false); |
| EXPECT_TRUE(demo_tools_test_api.GetShownModifiersKeyCodes().empty()); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), kIconKeyCodes[0]); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| |
| event_generator->ReleaseKey(kIconKeyCodes[0], ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/false); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| std::vector<ui::KeyboardCode>{ui::VKEY_CONTROL}); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_UNKNOWN); |
| |
| event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| } |
| |
| // Tests that if the width of the confine bounds is smaller than that of the |
| // preferred size of the key combo widget, the key combo widget will be shifted |
| // to the left. But the right edge of the key combo widget will always be to the |
| // left of the right edge of the capture surface confine bounds. |
| TEST_F(CaptureModeDemoToolsTest, |
| ConfineBoundsSizeSmallerThanPreferredSizeTest) { |
| auto* controller = CaptureModeController::Get(); |
| const gfx::Rect capture_region(100, 200, 200, 50); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_C, ui::EF_NONE); |
| |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| DCHECK(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| KeyComboView* key_combo_view = demo_tools_test_api.GetKeyComboView(); |
| EXPECT_TRUE(key_combo_view); |
| const auto confine_bounds = controller->GetCaptureSurfaceConfineBounds(); |
| EXPECT_LT(confine_bounds.width(), |
| key_combo_view->GetBoundsInScreen().width()); |
| EXPECT_GT(confine_bounds.right(), |
| key_combo_view->GetBoundsInScreen().right()); |
| } |
| |
| // Tests that the key combo widget will be re-posisioned correctly on capture |
| // window bounds change. |
| TEST_F(CaptureModeDemoToolsTest, CaptureBoundsChangeTest) { |
| UpdateDisplay("800x700"); |
| const auto window = CreateTestWindow(gfx::Rect(100, 150, 300, 500)); |
| auto* split_view_controller = |
| SplitViewController::Get(Shell::GetPrimaryRootWindow()); |
| EXPECT_EQ(split_view_controller->state(), |
| SplitViewController::State::kNoSnap); |
| |
| auto* capture_mode_controller = |
| StartCaptureSession(CaptureModeSource::kWindow, CaptureModeType::kVideo); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseToCenterOf(window.get()); |
| |
| capture_mode_controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(capture_mode_controller->is_recording_in_progress()); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| DCHECK(demo_tools_controller); |
| |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_C, ui::EF_NONE); |
| VerifyKeyComboWidgetPosition(); |
| |
| // Snap the `window` which will result in window bounds change and the key |
| // combo widget will still be centered horizontally. |
| split_view_controller->SnapWindow( |
| window.get(), SplitViewController::SnapPosition::kPrimary); |
| EXPECT_EQ(split_view_controller->primary_window(), window.get()); |
| VerifyKeyComboWidgetPosition(); |
| } |
| |
| // Tests that there is no crash when work area changed after starting a video |
| // recording with demo tools enabled. Docked mananifier is used as an example to |
| // trigger the work area change. |
| TEST_F(CaptureModeDemoToolsTest, WorkAreaChangeTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| auto* docked_magnifier_controller = |
| Shell::Get()->docked_magnifier_controller(); |
| docked_magnifier_controller->SetEnabled(/*enabled=*/true); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| } |
| |
| // Tests that if a touch down event happens before video recording starts, there |
| // will be no crash and no touch highlight will be generated. |
| TEST_F(CaptureModeDemoToolsTest, TouchDownBeforeVideoRecordingTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| |
| // Press touch before starting the video recording. |
| auto* root_window = Shell::GetPrimaryRootWindow(); |
| const gfx::Rect root_window_bounds_in_screen = |
| root_window->GetBoundsInScreen(); |
| const gfx::Point display_center = root_window_bounds_in_screen.CenterPoint(); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressTouchId(0, display_center); |
| |
| StartVideoRecordingImmediately(); |
| WaitForSeconds(1); |
| |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| const auto& touch_highlight_map = |
| demo_tools_test_api.GetTouchIdToHighlightLayerMap(); |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| event_generator->ReleaseTouchId(0); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| } |
| |
| // Tests that the drag and drop in the shelf during video recording with demo |
| // tools enabled works properly with no crash. |
| TEST_F(CaptureModeDemoToolsTest, DragAndDropIconOnShelfTest) { |
| ShelfItem item = ShelfTestUtil::AddAppShortcut("app_id", TYPE_PINNED_APP); |
| const ShelfID& id = item.id; |
| ShelfView* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); |
| ShelfViewTestAPI test_api(shelf_view); |
| ShelfAppButton* button = |
| test_api.GetButton(ShelfModel::Get()->ItemIndexByID(id)); |
| ASSERT_TRUE(button); |
| const gfx::Point button_center_point = |
| button->GetBoundsInScreen().CenterPoint(); |
| |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressTouch(button_center_point); |
| ASSERT_TRUE(button->FireDragTimerForTest()); |
| button->FireRippleActivationTimerForTest(); |
| |
| ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS); |
| ui::GestureEvent long_press(button_center_point.x(), button_center_point.y(), |
| 0, ui::EventTimeForNow(), event_details); |
| event_generator->Dispatch(&long_press); |
| event_generator->MoveTouchBy(0, -10); |
| |
| EXPECT_TRUE(shelf_view->drag_view()); |
| EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING); |
| event_generator->ReleaseTouch(); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| } |
| |
| // Tests that the order of the currently pressed modifier keys will be preserved |
| // when updating the key combo view by removing released keys or appending new |
| // keys. |
| TEST_F(CaptureModeDemoToolsTest, FollowPreDeterminedOrder) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| |
| std::vector<ui::KeyboardCode> expected_modifier_key_vector = { |
| ui::VKEY_CONTROL, ui::VKEY_MENU, ui::VKEY_SHIFT, ui::VKEY_COMMAND}; |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_COMMAND, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_MENU, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| |
| event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/false); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| std::vector<ui::KeyboardCode>( |
| {ui::VKEY_CONTROL, ui::VKEY_MENU, ui::VKEY_COMMAND})); |
| |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| EXPECT_EQ(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| expected_modifier_key_vector); |
| } |
| |
| // Tests that if a new capture mode session gets triggered by keyboard shortcut |
| // while in video recording with demo tools on, the bounds of the key combo |
| // widget will be updated to avoid collision. |
| TEST_F(CaptureModeDemoToolsTest, KeyComboWidgetDeIntersectsWithCaptureBar) { |
| UpdateDisplay("800x700"); |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| // Start a new capture mode session with keyboard shortcut. |
| PressAndReleaseKey(ui::VKEY_MEDIA_LAUNCH_APP1, |
| ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN); |
| auto* key_combo_widget = demo_tools_test_api.GetKeyComboWidget(); |
| EXPECT_TRUE(key_combo_widget); |
| const gfx::Rect original_bounds = key_combo_widget->GetWindowBoundsInScreen(); |
| |
| const auto* capture_bar_view = GetCaptureModeBarView(); |
| EXPECT_TRUE(capture_bar_view); |
| const auto capture_bar_bounds = capture_bar_view->GetBoundsInScreen(); |
| const int capture_bar_y = capture_bar_bounds.y(); |
| EXPECT_LT(capture_bar_y, original_bounds.bottom()); |
| |
| PressAndReleaseKey(ui::VKEY_MEDIA_LAUNCH_APP1, |
| ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN); |
| key_combo_widget = demo_tools_test_api.GetKeyComboWidget(); |
| const gfx::Rect new_bounds = key_combo_widget->GetWindowBoundsInScreen(); |
| EXPECT_GT(capture_bar_y, new_bounds.bottom()); |
| } |
| |
| // Tests that the auto click bar will be repositioned once there is a collision |
| // with the key combo widget. |
| TEST_F(CaptureModeDemoToolsTest, KeyComboWidgetDeIntersectsWithAutoClickBar) { |
| auto* autoclick_bubble_widget = EnableAndGetAutoClickBubbleWidget(); |
| const gfx::Rect original_auto_click_widget_bounds = |
| autoclick_bubble_widget->GetWindowBoundsInScreen(); |
| |
| CaptureModeController* controller = |
| StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kVideo); |
| |
| // Intentionally create a `capture_region` within which the bounds of the key |
| // combo widget generated will mostly likely to collide with the |
| // `autoclick_bubble_widget`. |
| gfx::Rect capture_region = original_auto_click_widget_bounds; |
| capture_region.Inset(-16); |
| |
| controller->SetUserCaptureRegion(capture_region, |
| /*by_user=*/true); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_C, ui::EF_NONE); |
| |
| CaptureModeDemoToolsTestApi demo_tools_test_api( |
| GetCaptureModeDemoToolsController()); |
| auto* key_combo_widget = demo_tools_test_api.GetKeyComboWidget(); |
| ASSERT_TRUE(key_combo_widget); |
| const gfx::Rect key_combo_widget_bounds = |
| key_combo_widget->GetWindowBoundsInScreen(); |
| EXPECT_TRUE( |
| key_combo_widget_bounds.Intersects(original_auto_click_widget_bounds)); |
| |
| const gfx::Rect new_autoclick_widget_bounds = |
| autoclick_bubble_widget->GetWindowBoundsInScreen(); |
| EXPECT_FALSE(key_combo_widget_bounds.Intersects(new_autoclick_widget_bounds)); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| } |
| |
| // Tests that the key combo viewer widget will display for key event coming from |
| // on-screen keyboard. For such key event, the key combo viewer will show on key |
| // down of a modifier key whose `flags()` is not 0 or non-modifier key that is |
| // allowed to show independently. |
| TEST_F(CaptureModeDemoToolsTest, OnScreenKeyboardKeyEventTest) { |
| CaptureModeController* controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| CaptureModeDemoToolsController* demo_tools_controller = |
| GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| auto* event_generator = GetEventGenerator(); |
| PressAndReleaseKeyOnVK(event_generator, ui::VKEY_A, ui::EF_CONTROL_DOWN); |
| EXPECT_THAT(demo_tools_test_api.GetShownModifiersKeyCodes(), |
| testing::ElementsAre(ui::VKEY_CONTROL)); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_A); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| |
| PressAndReleaseKeyOnVK(event_generator, ui::VKEY_TAB, ui::EF_NONE); |
| EXPECT_TRUE(demo_tools_test_api.GetShownModifiersKeyCodes().empty()); |
| EXPECT_EQ(demo_tools_test_api.GetShownNonModifierKeyCode(), ui::VKEY_TAB); |
| FireTimerAndVerifyWidget(/*should_hide_view=*/true); |
| } |
| |
| // Tests that the metrics that record if a recording starts with demo tools |
| // feature enabled are recorded correctly in a capture session both in clamshell |
| // and tablet mode. |
| TEST_F(CaptureModeDemoToolsTest, |
| DemoToolsEnabledOnRecordingStartHistogramTest) { |
| base::HistogramTester histogram_tester; |
| constexpr char kHistogramNameBase[] = "DemoToolsEnabledOnRecordingStart"; |
| |
| struct { |
| bool enable_tablet_mode; |
| bool enable_demo_tools; |
| } kTestCases[]{ |
| {/*enable_tablet_mode=*/false, /*enable_demo_tools=*/false}, |
| {/*enable_tablet_mode=*/false, /*enable_demo_tools=*/true}, |
| {/*enable_tablet_mode=*/true, /*enable_demo_tools=*/false}, |
| {/*enable_tablet_mode=*/true, /*enable_demo_tools=*/true}, |
| }; |
| |
| for (const auto test_case : kTestCases) { |
| if (test_case.enable_tablet_mode) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| const auto histogram_name = |
| BuildHistogramName(kHistogramNameBase, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true); |
| histogram_tester.ExpectBucketCount(histogram_name, |
| test_case.enable_demo_tools, 0); |
| auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, |
| CaptureModeType::kVideo); |
| controller->EnableDemoTools(test_case.enable_demo_tools); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| histogram_tester.ExpectBucketCount(histogram_name, |
| test_case.enable_demo_tools, 1); |
| } |
| } |
| |
| class CaptureModeDemoToolsTestWithAllSources |
| : public CaptureModeDemoToolsTest, |
| public testing::WithParamInterface<CaptureModeSource> { |
| public: |
| CaptureModeDemoToolsTestWithAllSources() = default; |
| CaptureModeDemoToolsTestWithAllSources( |
| const CaptureModeDemoToolsTestWithAllSources&) = delete; |
| CaptureModeDemoToolsTestWithAllSources& operator=( |
| const CaptureModeDemoToolsTestWithAllSources&) = delete; |
| ~CaptureModeDemoToolsTestWithAllSources() override = default; |
| |
| CaptureModeController* StartDemoToolsEnabledVideoRecordingWithParam() { |
| auto* controller = CaptureModeController::Get(); |
| const gfx::Rect capture_region(100, 200, 300, 400); |
| controller->SetUserCaptureRegion(capture_region, /*by_user=*/true); |
| |
| StartCaptureSession(GetParam(), CaptureModeType::kVideo); |
| controller->EnableDemoTools(true); |
| |
| if (GetParam() == CaptureModeSource::kWindow) |
| GetEventGenerator()->MoveMouseToCenterOf(window()); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| return controller; |
| } |
| }; |
| |
| // Tests that the key combo viewer widget should be centered within its confine |
| // bounds. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, |
| KeyComboViewerShouldBeCenteredTest) { |
| auto* controller = StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| |
| auto* event_generator = GetEventGenerator(); |
| const auto kKeyCodes = {ui::VKEY_CONTROL, ui::VKEY_SHIFT, ui::VKEY_A}; |
| for (const auto key_code : kKeyCodes) { |
| event_generator->PressKey(key_code, ui::EF_NONE); |
| VerifyKeyComboWidgetPosition(); |
| } |
| |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| EXPECT_FALSE(controller->IsActive()); |
| } |
| |
| // Tests that the mouse highlight layer will be created on mouse down and |
| // will disappear after the animation. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, MouseHighlightTest) { |
| ui::ScopedAnimationDurationScaleMode normal_animation( |
| ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); |
| StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| gfx::Rect confine_bounds_in_screen = GetConfineBoundsInScreenCoordinates(); |
| auto* event_generator = GetEventGenerator(); |
| event_generator->MoveMouseTo(confine_bounds_in_screen.CenterPoint()); |
| event_generator->PressLeftButton(); |
| event_generator->ReleaseLeftButton(); |
| const MouseHighlightLayers& highlight_layers = |
| demo_tools_test_api.GetMouseHighlightLayers(); |
| EXPECT_FALSE(highlight_layers.empty()); |
| EXPECT_EQ(highlight_layers.size(), 1u); |
| WaitForMouseHighlightAnimationCompleted(); |
| EXPECT_TRUE(highlight_layers.empty()); |
| } |
| |
| // Tests that multiple mouse highlight layers will be visible on consecutive |
| // mouse press events when the whole duration are within the expiration of the |
| // first animation expiration. It also tests that each mouse highlight layer |
| // will be centered on its mouse event location. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, |
| MouseHighlightShouldBeCenteredWithMouseClick) { |
| ui::ScopedAnimationDurationScaleMode normal_animation( |
| ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); |
| StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* recording_watcher = |
| CaptureModeController::Get()->video_recording_watcher_for_testing(); |
| auto* window_being_recorded = recording_watcher->window_being_recorded(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api = |
| CaptureModeDemoToolsTestApi(demo_tools_controller); |
| |
| gfx::Rect inner_rect = GetConfineBoundsInScreenCoordinates(); |
| inner_rect.Inset(5); |
| |
| const auto& layers_vector = demo_tools_test_api.GetMouseHighlightLayers(); |
| auto* event_generator = GetEventGenerator(); |
| |
| for (const auto point : {inner_rect.CenterPoint(), inner_rect.origin(), |
| inner_rect.bottom_right()}) { |
| event_generator->MoveMouseTo(point); |
| event_generator->PressLeftButton(); |
| event_generator->ReleaseLeftButton(); |
| auto* highlight_layer = layers_vector.back().get(); |
| auto highlight_center_point = |
| highlight_layer->layer()->bounds().CenterPoint(); |
| |
| // Convert the highlight layer center pointer to screen coordinates. |
| wm::ConvertPointToScreen(window_being_recorded, &highlight_center_point); |
| |
| EXPECT_EQ(highlight_center_point, point); |
| } |
| |
| EXPECT_EQ(layers_vector.size(), 3u); |
| } |
| |
| // Tests that the key combo viewer is positioned correctly on device |
| // scale factor change. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, DeviceScaleFactorTest) { |
| StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| |
| auto* event_generator = GetEventGenerator(); |
| event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE); |
| event_generator->PressKey(ui::VKEY_A, ui::EF_NONE); |
| |
| const float kDeviceScaleFactors[] = {0.5f, 1.2f, 2.5f}; |
| for (const float dsf : kDeviceScaleFactors) { |
| SetDeviceScaleFactor(dsf); |
| EXPECT_NEAR(dsf, window()->GetHost()->device_scale_factor(), 0.01); |
| VerifyKeyComboWidgetPosition(); |
| } |
| } |
| |
| // Tests that the touch highlight layer will be created on touch |
| // down and removed on touch up. It also tests that the bounds of the touch |
| // highlight layer will be updated correctly on the touch drag event. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, TouchHighlightTest) { |
| StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| const gfx::Rect confine_bounds_in_screen = |
| GetConfineBoundsInScreenCoordinates(); |
| auto* event_generator = GetEventGenerator(); |
| |
| const auto& touch_highlight_map = |
| demo_tools_test_api.GetTouchIdToHighlightLayerMap(); |
| |
| const auto center_point = confine_bounds_in_screen.CenterPoint(); |
| event_generator->PressTouchId(0, center_point); |
| EXPECT_FALSE(touch_highlight_map.empty()); |
| event_generator->ReleaseTouchId(0); |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| |
| const gfx::Vector2d drag_offset = |
| gfx::Vector2d(confine_bounds_in_screen.width() / 4, |
| confine_bounds_in_screen.height() / 4); |
| DragTouchAndVerifyHighlight(/*touch_id=*/0, /*touch_point=*/center_point, |
| drag_offset); |
| } |
| |
| // Tests the behaviors when multiple touches are performed. |
| // 1. The corresponding touch highlight will be generated on touch down; |
| // 2. The number of touch highlights kept in the demo tools controller is the |
| // same as the number of touch down events; |
| // 3. The bounds of the touch highlights will be updated correctly when dragging |
| // multiple touch events simultaneously; |
| // 4. The corresponding touch highlight will be removed on touch up. The |
| // number of touch highlights kept in the demo tools controller will become zero |
| // when all touches are released or cancelled. |
| TEST_P(CaptureModeDemoToolsTestWithAllSources, MutiTouchHighlightTest) { |
| StartDemoToolsEnabledVideoRecordingWithParam(); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| const auto& touch_highlight_map = |
| demo_tools_test_api.GetTouchIdToHighlightLayerMap(); |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| |
| gfx::Rect inner_rect = GetConfineBoundsInScreenCoordinates(); |
| inner_rect.Inset(20); |
| |
| struct { |
| int touch_id; |
| gfx::Point touch_point; |
| gfx::Vector2d drag_offset; |
| } kTestCases[] = { |
| {/*touch_id=*/1, inner_rect.CenterPoint(), gfx::Vector2d(15, 25)}, |
| {/*touch_id=*/0, inner_rect.origin(), gfx::Vector2d(10, -20)}, |
| {/*touch_id=*/2, inner_rect.bottom_right(), gfx::Vector2d(-30, -20)}}; |
| |
| // Iterate through the kTestCases and perform the touch down. The |
| // corresponding touch highlight will be generated. Drag these touch events |
| // and check if the bounds of the corresponding touch highlight are updated |
| // correctly. |
| for (const auto& test_case : kTestCases) { |
| DragTouchAndVerifyHighlight(test_case.touch_id, test_case.touch_point, |
| test_case.drag_offset); |
| } |
| |
| EXPECT_EQ(touch_highlight_map.size(), 3u); |
| |
| // Release the touch event one by one and the corresponding touch highlight |
| // layer will be removed. The number of highlight layers kept in the demo |
| // tools controller will become zero when all touches are released or |
| // cancelled. |
| for (const auto& test_case : kTestCases) { |
| GetEventGenerator()->ReleaseTouchId(test_case.touch_id); |
| EXPECT_FALSE(touch_highlight_map.contains( |
| static_cast<ui::PointerId>(test_case.touch_id))); |
| } |
| |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| CaptureModeDemoToolsTestWithAllSources, |
| testing::Values(CaptureModeSource::kFullscreen, |
| CaptureModeSource::kRegion, |
| CaptureModeSource::kWindow)); |
| |
| class ProjectorCaptureModeDemoToolsTest : public CaptureModeDemoToolsTest { |
| public: |
| ProjectorCaptureModeDemoToolsTest() = default; |
| ~ProjectorCaptureModeDemoToolsTest() override = default; |
| |
| // CaptureModeDemoToolsTest: |
| void SetUp() override { |
| CaptureModeDemoToolsTest::SetUp(); |
| projector_helper_.SetUp(); |
| } |
| |
| void StartProjectorModeSession() { |
| projector_helper_.StartProjectorModeSession(); |
| } |
| |
| private: |
| ProjectorCaptureModeIntegrationHelper projector_helper_; |
| }; |
| |
| // Tests that the demo tools feature will be enabled by default in a |
| // projector-initiated capture mode session and this overwritten configuration |
| // will not be carried over to a normal capture mode session. |
| TEST_F(ProjectorCaptureModeDemoToolsTest, EnableDemoToolsByDefault) { |
| CaptureModeController* capture_mode_controller = StartCaptureSession( |
| CaptureModeSource::kFullscreen, CaptureModeType::kVideo); |
| EXPECT_TRUE(capture_mode_controller->IsActive()); |
| EXPECT_FALSE(capture_mode_controller->enable_demo_tools()); |
| |
| capture_mode_controller->Stop(); |
| StartProjectorModeSession(); |
| EXPECT_TRUE(capture_mode_controller->IsActive()); |
| EXPECT_TRUE(capture_mode_controller->enable_demo_tools()); |
| |
| capture_mode_controller->Stop(); |
| capture_mode_controller->Start(CaptureModeEntryType::kQuickSettings); |
| EXPECT_FALSE(capture_mode_controller->enable_demo_tools()); |
| } |
| |
| // Tests that the pointer (mouse and touch) highlight will be disabled when |
| // annotating and re-enabled after stopping the annotation in a |
| // projector-initiated capture mode. |
| TEST_F(ProjectorCaptureModeDemoToolsTest, |
| DisablePointerHighlightWithAnnotatorEnabled) { |
| ui::ScopedAnimationDurationScaleMode animation_scale( |
| ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); |
| auto* capture_mode_controller = CaptureModeController::Get(); |
| capture_mode_controller->SetSource(CaptureModeSource::kFullscreen); |
| StartProjectorModeSession(); |
| EXPECT_TRUE(capture_mode_controller->enable_demo_tools()); |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(capture_mode_controller->is_recording_in_progress()); |
| auto* demo_tools_controller = GetCaptureModeDemoToolsController(); |
| EXPECT_TRUE(demo_tools_controller); |
| CaptureModeDemoToolsTestApi demo_tools_test_api(demo_tools_controller); |
| |
| const gfx::Rect confine_bounds_in_screen = |
| GetConfineBoundsInScreenCoordinates(); |
| const gfx::Point center_point = confine_bounds_in_screen.CenterPoint(); |
| auto* event_generator = GetEventGenerator(); |
| |
| auto mouse_highlight_test = [&](bool annotating) { |
| event_generator->MoveMouseTo(center_point); |
| event_generator->PressLeftButton(); |
| event_generator->ReleaseLeftButton(); |
| auto& mouse_highlight_layers = |
| demo_tools_test_api.GetMouseHighlightLayers(); |
| if (annotating) { |
| EXPECT_TRUE(mouse_highlight_layers.empty()); |
| } else { |
| EXPECT_FALSE(mouse_highlight_layers.empty()); |
| } |
| }; |
| |
| auto touch_highlight_test = [&](bool annotating) { |
| event_generator->PressTouchId(0, center_point); |
| auto& touch_highlight_map = |
| demo_tools_test_api.GetTouchIdToHighlightLayerMap(); |
| if (annotating) { |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| } else { |
| EXPECT_FALSE(touch_highlight_map.empty()); |
| } |
| event_generator->ReleaseTouchId(0); |
| EXPECT_TRUE(touch_highlight_map.empty()); |
| }; |
| |
| CaptureModeTestApi test_api; |
| RecordingOverlayController* recording_overlay_controller = |
| test_api.GetRecordingOverlayController(); |
| |
| auto* projector_controller = ProjectorControllerImpl::Get(); |
| projector_controller->EnableAnnotatorTool(); |
| EXPECT_TRUE(recording_overlay_controller->is_enabled()); |
| mouse_highlight_test(/*annotating=*/true); |
| touch_highlight_test(/*annotating=*/true); |
| |
| projector_controller->ResetTools(); |
| EXPECT_TRUE(capture_mode_controller->is_recording_in_progress()); |
| EXPECT_FALSE(recording_overlay_controller->is_enabled()); |
| mouse_highlight_test(/*annotating=*/false); |
| touch_highlight_test(/*annotating=*/false); |
| } |
| |
| // Tests that the metrics that record if a recording starts with demo tools |
| // feature enabled are recorded correctly in a projector-initiated capture |
| // session both in clamshell and tablet mode. |
| TEST_F(ProjectorCaptureModeDemoToolsTest, |
| ProjectorDemoToolsEnabledOnRecordingStartHistogramTest) { |
| base::HistogramTester histogram_tester; |
| constexpr char kHistogramNameBase[] = "DemoToolsEnabledOnRecordingStart"; |
| |
| struct { |
| bool enable_tablet_mode; |
| bool enable_demo_tools; |
| } kTestCases[]{ |
| {/*enable_tablet_mode=*/false, /*enable_demo_tools=*/false}, |
| {/*enable_tablet_mode=*/false, /*enable_demo_tools=*/true}, |
| {/*enable_tablet_mode=*/true, /*enable_demo_tools=*/false}, |
| {/*enable_tablet_mode=*/true, /*enable_demo_tools=*/true}, |
| }; |
| |
| for (const auto test_case : kTestCases) { |
| if (test_case.enable_tablet_mode) { |
| SwitchToTabletMode(); |
| EXPECT_TRUE(Shell::Get()->IsInTabletMode()); |
| } else { |
| EXPECT_FALSE(Shell::Get()->IsInTabletMode()); |
| } |
| |
| const std::string histogram_name = BuildHistogramName( |
| kHistogramNameBase, |
| CaptureModeTestApi().GetBehavior(BehaviorType::kProjector), |
| /*append_ui_mode_suffix=*/true); |
| histogram_tester.ExpectBucketCount(histogram_name, |
| test_case.enable_demo_tools, 0); |
| auto* controller = CaptureModeController::Get(); |
| controller->SetSource(CaptureModeSource::kFullscreen); |
| |
| // Start a projector-initiated capture mode sesession, the demo tools |
| // feature will be enabled by default. `EnableDemoTools` to ensure that the |
| // test coverage includes both enabled and disabled cases. |
| StartProjectorModeSession(); |
| EXPECT_TRUE(controller->enable_demo_tools()); |
| controller->EnableDemoTools(test_case.enable_demo_tools); |
| EXPECT_TRUE(controller->IsActive()); |
| |
| StartVideoRecordingImmediately(); |
| EXPECT_TRUE(controller->is_recording_in_progress()); |
| WaitForSeconds(1); |
| |
| controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton); |
| WaitForCaptureFileToBeSaved(); |
| histogram_tester.ExpectBucketCount( |
| histogram_name, test_case.enable_demo_tools, /*expected_count=*/1); |
| } |
| } |
| |
| } // namespace ash |