| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/gpu/android/surface_chooser_helper.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "media/gpu/android/mock_android_video_surface_chooser.h" |
| #include "media/gpu/android/mock_promotion_hint_aggregator.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| using testing::AtLeast; |
| |
| namespace media { |
| |
| // Unit tests for PromotionHintAggregatorImplTest |
| class SurfaceChooserHelperTest : public testing::Test { |
| public: |
| ~SurfaceChooserHelperTest() override {} |
| |
| void SetUp() override { |
| // Create a default helper. |
| ReplaceHelper(false, false); |
| } |
| |
| void TearDown() override {} |
| |
| void ReplaceHelper(bool is_overlay_required, |
| bool promote_secure_only, |
| bool always_use_texture_owner = false) { |
| // Advance the clock so that time 0 isn't recent. |
| tick_clock_.Advance(base::Seconds(10000)); |
| |
| std::unique_ptr<MockAndroidVideoSurfaceChooser> chooser = |
| std::make_unique<MockAndroidVideoSurfaceChooser>(); |
| chooser_ = chooser.get(); |
| std::unique_ptr<MockPromotionHintAggregator> aggregator = |
| std::make_unique<MockPromotionHintAggregator>(); |
| aggregator_ = aggregator.get(); |
| helper_ = std::make_unique<SurfaceChooserHelper>( |
| std::move(chooser), is_overlay_required, promote_secure_only, |
| always_use_texture_owner, std::move(aggregator), &tick_clock_); |
| } |
| |
| // Convenience function. |
| void UpdateChooserState() { |
| EXPECT_CALL(*chooser_, MockUpdateState()); |
| helper_->UpdateChooserState(absl::optional<AndroidOverlayFactoryCB>()); |
| } |
| |
| base::SimpleTestTickClock tick_clock_; |
| |
| MockPromotionHintAggregator* aggregator_ = nullptr; |
| |
| MockAndroidVideoSurfaceChooser* chooser_ = nullptr; |
| |
| std::unique_ptr<SurfaceChooserHelper> helper_; |
| }; |
| |
| TEST_F(SurfaceChooserHelperTest, SetIsFullscreen) { |
| // Entering fullscreen should expect relayout. |
| helper_->SetIsFullscreen(true); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_fullscreen); |
| ASSERT_TRUE(chooser_->current_state_.is_expecting_relayout); |
| |
| // Exiting fullscreen should not reset the expecting layout flag. |
| helper_->SetIsFullscreen(false); |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.is_fullscreen); |
| // We don't really care if it sets expecting_relayout, clears it, or not. |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetVideoRotation) { |
| // VideoRotation should be forwarded to the chooser. |
| helper_->SetVideoRotation(VIDEO_ROTATION_90); |
| UpdateChooserState(); |
| ASSERT_EQ(chooser_->current_state_.video_rotation, VIDEO_ROTATION_90); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetIsPersistentVideo) { |
| helper_->SetIsPersistentVideo(true); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_persistent_video); |
| |
| helper_->SetIsPersistentVideo(false); |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.is_persistent_video); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetIsOverlayRequired) { |
| // The default helper was created without |is_required|, so verify that. |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.is_required); |
| |
| ReplaceHelper(true, false); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_required); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetInsecureSurface) { |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kInsecure); |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.is_secure); |
| ASSERT_FALSE(chooser_->current_state_.is_required); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetRequestedSecureSurface) { |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kRequested); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_secure); |
| ASSERT_FALSE(chooser_->current_state_.is_required); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetRequiredSecureSurface) { |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kRequired); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_secure); |
| ASSERT_TRUE(chooser_->current_state_.is_required); |
| |
| // Also check that removing kRequired puts |is_required| back, since that has |
| // special processing for "always required". |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kInsecure); |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.is_required); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, StillRequiredAfterClearingSecure) { |
| // Verify that setting then clearing kRequired doesn't make |is_required| |
| // false if overlays were required during construction. |
| ReplaceHelper(true, false); |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kRequired); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_required); |
| |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kInsecure); |
| UpdateChooserState(); |
| // Should still be true. |
| ASSERT_TRUE(chooser_->current_state_.is_required); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetPromoteSecureOnly) { |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.promote_secure_only); |
| |
| ReplaceHelper(false, true); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.promote_secure_only); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, SetAlwaysUseTextureOwner) { |
| UpdateChooserState(); |
| ASSERT_FALSE(chooser_->current_state_.always_use_texture_owner); |
| |
| ReplaceHelper(false, true, true); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.always_use_texture_owner); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, PromotionHintsForwardsHint) { |
| // Make sure that NotifyPromotionHint relays the hint to the aggregator. |
| PromotionHintAggregator::Hint hint(gfx::Rect(1, 2, 3, 4), false); |
| EXPECT_CALL(*aggregator_, NotifyPromotionHint(hint)); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, PromotionHintsRelayPosition) { |
| // Make sure that the overlay position is sent to the chooser. |
| gfx::Rect rect(0, 1, 2, 3); |
| |
| // Send an unpromotable hint and verify that the state reflects it. We set |
| // it to be promotable so that it notifies the chooser. |
| helper_->NotifyPromotionHintAndUpdateChooser( |
| PromotionHintAggregator::Hint(rect, true), false); |
| ASSERT_EQ(chooser_->current_state_.initial_position, rect); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, PromotionHintsRelayPromotable) { |
| // Make sure that the promotability state is forwarded to the chooser. |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(AtLeast(1)); |
| PromotionHintAggregator::Hint hint(gfx::Rect(), false); |
| |
| // Send a hint while the aggregator says it's unpromotable, and verify that |
| // the state reflects it. |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| ASSERT_FALSE(chooser_->current_state_.is_compositor_promotable); |
| |
| // Send a promotable hint and check the state. Note that the hint has nothing |
| // to do with it; it's the aggregator's state. |
| aggregator_->SetIsSafeToPromote(true); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| ASSERT_TRUE(chooser_->current_state_.is_compositor_promotable); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, PromotionHintsClearRelayoutFlag) { |
| // Set fullscreen to enable relayout. |
| helper_->SetIsFullscreen(true); |
| UpdateChooserState(); |
| ASSERT_TRUE(chooser_->current_state_.is_expecting_relayout); |
| |
| // Send a bunch of hints. |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(AtLeast(1)); |
| for (int i = 0; i < 15; i++) { |
| PromotionHintAggregator::Hint hint(gfx::Rect(), false); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| } |
| |
| // It should no longer be expecting fs. |
| ASSERT_FALSE(chooser_->current_state_.is_expecting_relayout); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, PromotionHintsUpdateChooserStatePeriodically) { |
| // Verify that, if enough time passes, we'll get chooser updates if we want |
| // and overlay but don't have one. |
| PromotionHintAggregator::Hint hint(gfx::Rect(), false); |
| |
| // Sending the first hint should update the chooser, since we're becoming |
| // safe to promote. |
| aggregator_->SetIsSafeToPromote(true); |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(1); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| |
| // Sending an additional hint should not, whether or not we're using an |
| // overlay currently. |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(0); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, true); |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(0); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| |
| // Advancing the time and using an overlay should not send a hint. |
| tick_clock_.Advance(base::Seconds(10)); |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(0); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, true); |
| |
| // If we're not using an overlay, then it should update the chooser to see |
| // if it's willing to try for one now. |
| EXPECT_CALL(*chooser_, MockUpdateState()).Times(1); |
| helper_->NotifyPromotionHintAndUpdateChooser(hint, false); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, FrameInformationIsCorrectForL1) { |
| // Verify L1 cases. |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kRequired); |
| |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation::OVERLAY_L1, |
| helper_->ComputeFrameInformation(true)); |
| // We don't check the "not using overlay" case; it's unclear what we should be |
| // doing in this case anyway. |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, FrameInformationIsCorrectForL3) { |
| // Verify L3 cases. |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kRequested); |
| |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation::OVERLAY_L3, |
| helper_->ComputeFrameInformation(true)); |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation::NON_OVERLAY_L3, |
| helper_->ComputeFrameInformation(false)); |
| } |
| |
| TEST_F(SurfaceChooserHelperTest, FrameInformationIsCorrectForInsecure) { |
| // Verify insecure cases. |
| helper_->SetSecureSurfaceMode( |
| SurfaceChooserHelper::SecureSurfaceMode::kInsecure); |
| |
| // Not using an overlay should be NON_OVERLAY_INSECURE |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation::NON_OVERLAY_INSECURE, |
| helper_->ComputeFrameInformation(false)); |
| |
| // Fullscreen state should affect the result, so that we can tell the |
| // difference between player-element-fs and div-fs (custom controls). |
| helper_->SetIsFullscreen(true); |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation:: |
| OVERLAY_INSECURE_PLAYER_ELEMENT_FULLSCREEN, |
| helper_->ComputeFrameInformation(true)); |
| helper_->SetIsFullscreen(false); |
| ASSERT_EQ(SurfaceChooserHelper::FrameInformation:: |
| OVERLAY_INSECURE_NON_PLAYER_ELEMENT_FULLSCREEN, |
| helper_->ComputeFrameInformation(true)); |
| } |
| |
| } // namespace media |