Chromium Code Reviews| Index: ash/common/wm/overview/cleanup_animation_observer_unittest.cc |
| diff --git a/ash/common/wm/overview/cleanup_animation_observer_unittest.cc b/ash/common/wm/overview/cleanup_animation_observer_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ed839cbf68949afd50b6caadaec4ac37dc7aedad |
| --- /dev/null |
| +++ b/ash/common/wm/overview/cleanup_animation_observer_unittest.cc |
| @@ -0,0 +1,187 @@ |
| +// Copyright 2016 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 <vector> |
| + |
| +#include "ash/common/wm/overview/cleanup_animation_observer.h" |
| + |
| +#include "ash/common/wm/overview/window_selector_delegate.h" |
| +#include "ash/common/wm_lookup.h" |
| +#include "ash/common/wm_window.h" |
| +#include "ash/test/ash_test_base.h" |
| +#include "ui/compositor/layer_animation_observer.h" |
| +#include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| +#include "ui/compositor/scoped_layer_animation_settings.h" |
| +#include "ui/gfx/geometry/rect.h" |
| +#include "ui/views/widget/widget.h" |
| +#include "ui/views/widget/widget_observer.h" |
| + |
| +namespace ash { |
| +namespace { |
| + |
| +class TestWindowSelectorDelegate : public WindowSelectorDelegate { |
| + public: |
| + TestWindowSelectorDelegate() = default; |
| + |
| + ~TestWindowSelectorDelegate() override { |
| + // Destroy widgets that may be still animating if shell shuts down soon |
| + // after exiting overview mode. |
| + for (std::unique_ptr<DelayedAnimationObserver>& observer : observers_) |
| + observer->Shutdown(); |
| + } |
| + |
| + // WindowSelectorDelegate: |
| + void OnSelectionEnded() override {} |
| + |
| + void AddDelayedAnimationObserver( |
| + std::unique_ptr<DelayedAnimationObserver> animation_observer) override { |
| + animation_observer->SetOwner(this); |
| + observers_.push_back(std::move(animation_observer)); |
| + } |
| + |
| + void RemoveAndDestroyAnimationObserver( |
| + DelayedAnimationObserver* animation_observer) override { |
| + class IsEqual { |
|
bruthig
2016/07/18 18:49:09
It would be nice if we didn't duplicate this logic
varkha
2016/07/18 20:57:57
Yeah, templates are frowned upon in client code as
|
| + public: |
| + explicit IsEqual(DelayedAnimationObserver* animation_observer) |
| + : animation_observer_(animation_observer) {} |
| + bool operator()(const std::unique_ptr<DelayedAnimationObserver>& other) { |
| + return (other.get() == animation_observer_); |
| + } |
| + |
| + private: |
| + const DelayedAnimationObserver* animation_observer_; |
| + }; |
| + observers_.erase(std::remove_if(observers_.begin(), observers_.end(), |
| + IsEqual(animation_observer)), |
| + observers_.end()); |
| + } |
| + |
| + private: |
| + std::vector<std::unique_ptr<DelayedAnimationObserver>> observers_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestWindowSelectorDelegate); |
| +}; |
| + |
| +class CleanupAnimationObserverTest : public test::AshTestBase, |
| + public views::WidgetObserver { |
| + public: |
| + CleanupAnimationObserverTest() = default; |
| + |
| + ~CleanupAnimationObserverTest() override { |
| + if (!widget_destroyed_) |
| + widget_->RemoveObserver(this); |
| + } |
| + |
| + // Creates a Widget containing a Window with the given |bounds|. This should |
| + // be used when the test requires a Widget. For example any test that will |
| + // cause a window to be closed via |
| + // views::Widget::GetWidgetForNativeView(window)->Close(). |
| + std::unique_ptr<views::Widget> CreateWindowWidget(const gfx::Rect& bounds) { |
| + std::unique_ptr<views::Widget> widget(new views::Widget); |
| + views::Widget::InitParams params; |
| + params.bounds = bounds; |
| + params.type = views::Widget::InitParams::TYPE_WINDOW; |
| + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| + widget->Init(params); |
| + widget->Show(); |
| + ParentWindowInPrimaryRootWindow(widget->GetNativeWindow()); |
| + widget->AddObserver(this); |
| + widget_ = widget.get(); |
| + return widget; |
| + } |
| + |
| + protected: |
| + bool widget_destroyed() { return widget_destroyed_; } |
| + |
| + private: |
| + void OnWidgetDestroyed(views::Widget* widget) override { |
| + if (widget_ == widget) |
| + widget_destroyed_ = true; |
|
bruthig
2016/07/18 18:49:09
Any reason you are not setting |widget_| = nullptr
varkha
2016/07/18 20:57:57
Done.
|
| + } |
| + |
| + views::Widget* widget_ = nullptr; |
| + bool widget_destroyed_ = false; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(CleanupAnimationObserverTest); |
| +}; |
| + |
| +} // namespace |
| + |
| +// Tests that basic create-destroy sequence does not crash. |
| +TEST_F(CleanupAnimationObserverTest, CreateDestroy) { |
| + TestWindowSelectorDelegate delegate; |
| + std::unique_ptr<views::Widget> widget( |
| + CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| + std::unique_ptr<CleanupAnimationObserver> observer( |
| + new CleanupAnimationObserver(std::move(widget))); |
| + delegate.AddDelayedAnimationObserver(std::move(observer)); |
| +} |
| + |
| +// Tests that completing animation deletes the animation observer and the |
| +// test widget and that deleting the WindowSelectorDelegate instance which |
| +// owns the observer does not crash. |
| +TEST_F(CleanupAnimationObserverTest, CreateAnimateComplete) { |
| + TestWindowSelectorDelegate delegate; |
| + std::unique_ptr<views::Widget> widget( |
| + CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| + WmWindow* widget_window = WmLookup::Get()->GetWindowForWidget(widget.get()); |
| + { |
| + ui::ScopedLayerAnimationSettings animation_settings( |
| + widget_window->GetLayer()->GetAnimator()); |
| + animation_settings.SetTransitionDuration( |
| + base::TimeDelta::FromMilliseconds(1000)); |
| + animation_settings.SetPreemptionStrategy( |
| + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| + |
| + std::unique_ptr<CleanupAnimationObserver> observer( |
| + new CleanupAnimationObserver(std::move(widget))); |
| + animation_settings.AddObserver(observer.get()); |
| + delegate.AddDelayedAnimationObserver(std::move(observer)); |
| + |
| + widget_window->SetBounds(gfx::Rect(50, 50, 60, 60)); |
| + } |
| + // The widget should be destroyed when |animation_settings| gets out of scope |
| + // which in absence of NON_ZERO_DURATION animation duration mode completes |
| + // the animation and calls OnImplicitAnimationsCompleted() on the cleanup |
| + // observer and auto-deletes the owned widget. |
| + EXPECT_TRUE(widget_destroyed()); |
| + // TestWindowSelectorDelegate going out of scope should not crash. |
| +} |
| + |
| +// Tests that starting an animation and exiting doesn't crash. This test would |
| +// have crashed of not for TestWindowSelectorDelegate calling Shutdown() on |
|
bruthig
2016/07/18 18:49:09
sp: of -> if
varkha
2016/07/18 20:57:57
Done.
|
| +// a CleanupAnimationObserver instance. |
| +TEST_F(CleanupAnimationObserverTest, CreateAnimateShutdown) { |
| + TestWindowSelectorDelegate delegate; |
| + std::unique_ptr<views::Widget> widget( |
| + CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| + WmWindow* widget_window = WmLookup::Get()->GetWindowForWidget(widget.get()); |
| + { |
| + // Normal animations for tests have ZERO_DURATION, make sure we are actually |
| + // animating the movement. |
| + ui::ScopedAnimationDurationScaleMode animation_scale_mode( |
| + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| + ui::ScopedLayerAnimationSettings animation_settings( |
| + widget_window->GetLayer()->GetAnimator()); |
| + animation_settings.SetTransitionDuration( |
| + base::TimeDelta::FromMilliseconds(1000)); |
| + animation_settings.SetPreemptionStrategy( |
| + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| + |
| + std::unique_ptr<CleanupAnimationObserver> observer( |
| + new CleanupAnimationObserver(std::move(widget))); |
| + animation_settings.AddObserver(observer.get()); |
| + delegate.AddDelayedAnimationObserver(std::move(observer)); |
| + |
| + widget_window->SetBounds(gfx::Rect(50, 50, 60, 60)); |
| + } |
| + // The widget still exists. |
| + EXPECT_FALSE(widget_destroyed()); |
| + // The test widget is auto-deleted when |delegate| that owns it goes out of |
| + // scope. The animation is still active when this happens which should not |
| + // crash. |
| +} |
| + |
| +} // namespace ash |