| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/system/progress_indicator/progress_indicator.h" |
| |
| #include "ash/system/progress_indicator/progress_indicator_animation_registry.h" |
| #include "ash/test/ash_test_base.h" |
| #include "base/test/bind.h" |
| #include "base/test/repeating_test_future.h" |
| |
| namespace ash { |
| namespace { |
| |
| // TestProgressIndicator ------------------------------------------------------- |
| |
| class TestProgressIndicator : public ProgressIndicator { |
| public: |
| TestProgressIndicator() |
| : ProgressIndicator(/*animation_registry=*/nullptr, |
| /*animation_key=*/this) {} |
| |
| void SetProgress(const absl::optional<float>& progress) { |
| progress_ = progress; |
| static_cast<ui::LayerDelegate*>(this)->UpdateVisualState(); |
| } |
| |
| private: |
| // ProgressIndicator: |
| absl::optional<float> CalculateProgress() const override { return progress_; } |
| absl::optional<float> progress_; |
| }; |
| |
| } // namespace |
| |
| // ProgressIndicatorTest ------------------------------------------------------- |
| |
| using ProgressIndicatorTest = AshTestBase; |
| |
| // Verifies that `ProgressIndicator::CreateDefaultInstance()` works as intended. |
| // It should delegate progress calculation to a constructor provided callback |
| // and manage progress animations as needed. |
| TEST_F(ProgressIndicatorTest, CreateDefaultInstance) { |
| absl::optional<float> progress; |
| |
| // Create a default instance of `ProgressIndicator` that paints `progress` |
| // whenever visual state is updated. |
| auto progress_indicator = ProgressIndicator::CreateDefaultInstance( |
| base::BindLambdaForTesting([&]() { return progress; })); |
| |
| // Cache `layer_delegate` associate with `progress_indicator` to manually |
| // trigger update of visual state. |
| auto* layer_delegate = |
| static_cast<ui::LayerDelegate*>(progress_indicator.get()); |
| |
| // Cache animation `key` and `registry` associated with `progress_indicator`. |
| auto* key = progress_indicator.get(); |
| auto* registry = progress_indicator->animation_registry(); |
| |
| // Verify initial progress and animation states. |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| EXPECT_FALSE(registry->GetProgressIconAnimationForKey(key)); |
| EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key)); |
| |
| // Update `progress` to 0%. Verify progress and animation states. |
| progress = 0.f; |
| layer_delegate->UpdateVisualState(); |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| ASSERT_TRUE(registry->GetProgressIconAnimationForKey(key)); |
| EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key)->HasAnimated()); |
| EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key)); |
| |
| // Update `progress` to 50%. Verify progress and animation states. |
| progress = 0.5f; |
| layer_delegate->UpdateVisualState(); |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| ASSERT_TRUE(registry->GetProgressIconAnimationForKey(key)); |
| EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key)->HasAnimated()); |
| EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key)); |
| |
| // Update `progress` to indeterminate. Verify progress and animation states. |
| progress = absl::nullopt; |
| layer_delegate->UpdateVisualState(); |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| ASSERT_TRUE(registry->GetProgressIconAnimationForKey(key)); |
| EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key)->HasAnimated()); |
| ASSERT_TRUE(registry->GetProgressRingAnimationForKey(key)); |
| EXPECT_EQ(registry->GetProgressRingAnimationForKey(key)->type(), |
| ProgressRingAnimation::Type::kIndeterminate); |
| |
| // Update `progress` to 75%. Verify progress and animation states. |
| progress = 0.75f; |
| layer_delegate->UpdateVisualState(); |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| ASSERT_TRUE(registry->GetProgressIconAnimationForKey(key)); |
| EXPECT_TRUE(registry->GetProgressIconAnimationForKey(key)->HasAnimated()); |
| EXPECT_FALSE(registry->GetProgressRingAnimationForKey(key)); |
| |
| // Update `progress` to 100%. Verify progress an animation states. |
| progress = ProgressIndicator::kProgressComplete; |
| layer_delegate->UpdateVisualState(); |
| EXPECT_EQ(progress_indicator->progress(), progress); |
| EXPECT_FALSE(registry->GetProgressIconAnimationForKey(key)); |
| ASSERT_TRUE(registry->GetProgressRingAnimationForKey(key)); |
| EXPECT_EQ(registry->GetProgressRingAnimationForKey(key)->type(), |
| ProgressRingAnimation::Type::kPulse); |
| |
| // The pulse animation that runs on progress completion should be removed |
| // automatically on animation completion. |
| base::test::RepeatingTestFuture<ProgressRingAnimation*> future; |
| auto subscription = registry->AddProgressRingAnimationChangedCallbackForKey( |
| key, future.GetCallback()); |
| EXPECT_EQ(future.Take(), nullptr); |
| } |
| |
| // Verifies that `ProgressIndicator::AddProgressChangedCallback()` works as |
| // intended. |
| TEST_F(ProgressIndicatorTest, AddProgressChangedCallback) { |
| // Create a test `progress_indicator`. |
| TestProgressIndicator progress_indicator; |
| progress_indicator.SetProgress(0.5f); |
| |
| // Add a callback to be notified of progress changed events. The callback |
| // should be invoked on progress changes so long as the returned subscription |
| // continues to exist. |
| int callback_call_count = 0; |
| auto subscription = |
| std::make_unique<base::RepeatingClosureList::Subscription>( |
| progress_indicator.AddProgressChangedCallback( |
| base::BindLambdaForTesting([&]() { ++callback_call_count; }))); |
| |
| // Change the underlying progress. |
| progress_indicator.SetProgress(0.75f); |
| EXPECT_EQ(callback_call_count, 1); |
| |
| // Attempt to change the underlying progress to the same value. |
| progress_indicator.SetProgress(0.75f); |
| EXPECT_EQ(callback_call_count, 1); |
| |
| // Reset the subscription and change the underlying progress. |
| subscription.reset(); |
| progress_indicator.SetProgress(1.f); |
| EXPECT_EQ(callback_call_count, 1); |
| } |
| |
| } // namespace ash |