OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <vector> |
| 6 |
| 7 #include "ash/common/wm/overview/cleanup_animation_observer.h" |
| 8 |
| 9 #include "ash/common/wm/overview/window_selector_delegate.h" |
| 10 #include "ash/common/wm_lookup.h" |
| 11 #include "ash/common/wm_window.h" |
| 12 #include "ash/test/ash_test_base.h" |
| 13 #include "ui/compositor/layer_animation_observer.h" |
| 14 #include "ui/compositor/scoped_animation_duration_scale_mode.h" |
| 15 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 16 #include "ui/gfx/geometry/rect.h" |
| 17 #include "ui/views/widget/widget.h" |
| 18 #include "ui/views/widget/widget_observer.h" |
| 19 |
| 20 namespace ash { |
| 21 namespace { |
| 22 |
| 23 class TestWindowSelectorDelegate : public WindowSelectorDelegate { |
| 24 public: |
| 25 TestWindowSelectorDelegate() = default; |
| 26 |
| 27 ~TestWindowSelectorDelegate() override { |
| 28 // Destroy widgets that may be still animating if shell shuts down soon |
| 29 // after exiting overview mode. |
| 30 for (std::unique_ptr<DelayedAnimationObserver>& observer : observers_) |
| 31 observer->Shutdown(); |
| 32 } |
| 33 |
| 34 // WindowSelectorDelegate: |
| 35 void OnSelectionEnded() override {} |
| 36 |
| 37 void AddDelayedAnimationObserver( |
| 38 std::unique_ptr<DelayedAnimationObserver> animation_observer) override { |
| 39 animation_observer->SetOwner(this); |
| 40 observers_.push_back(std::move(animation_observer)); |
| 41 } |
| 42 |
| 43 void RemoveAndDestroyAnimationObserver( |
| 44 DelayedAnimationObserver* animation_observer) override { |
| 45 class IsEqual { |
| 46 public: |
| 47 explicit IsEqual(DelayedAnimationObserver* animation_observer) |
| 48 : animation_observer_(animation_observer) {} |
| 49 bool operator()(const std::unique_ptr<DelayedAnimationObserver>& other) { |
| 50 return (other.get() == animation_observer_); |
| 51 } |
| 52 |
| 53 private: |
| 54 const DelayedAnimationObserver* animation_observer_; |
| 55 }; |
| 56 observers_.erase(std::remove_if(observers_.begin(), observers_.end(), |
| 57 IsEqual(animation_observer)), |
| 58 observers_.end()); |
| 59 } |
| 60 |
| 61 private: |
| 62 std::vector<std::unique_ptr<DelayedAnimationObserver>> observers_; |
| 63 |
| 64 DISALLOW_COPY_AND_ASSIGN(TestWindowSelectorDelegate); |
| 65 }; |
| 66 |
| 67 class CleanupAnimationObserverTest : public test::AshTestBase, |
| 68 public views::WidgetObserver { |
| 69 public: |
| 70 CleanupAnimationObserverTest() = default; |
| 71 |
| 72 ~CleanupAnimationObserverTest() override { |
| 73 if (widget_) |
| 74 widget_->RemoveObserver(this); |
| 75 } |
| 76 |
| 77 // Creates a Widget containing a Window with the given |bounds|. This should |
| 78 // be used when the test requires a Widget. For example any test that will |
| 79 // cause a window to be closed via |
| 80 // views::Widget::GetWidgetForNativeView(window)->Close(). |
| 81 std::unique_ptr<views::Widget> CreateWindowWidget(const gfx::Rect& bounds) { |
| 82 std::unique_ptr<views::Widget> widget(new views::Widget); |
| 83 views::Widget::InitParams params; |
| 84 params.bounds = bounds; |
| 85 params.type = views::Widget::InitParams::TYPE_WINDOW; |
| 86 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 87 widget->Init(params); |
| 88 widget->Show(); |
| 89 ParentWindowInPrimaryRootWindow(widget->GetNativeWindow()); |
| 90 widget->AddObserver(this); |
| 91 widget_ = widget.get(); |
| 92 return widget; |
| 93 } |
| 94 |
| 95 protected: |
| 96 bool widget_destroyed() { return !widget_; } |
| 97 |
| 98 private: |
| 99 void OnWidgetDestroyed(views::Widget* widget) override { |
| 100 if (widget_ == widget) |
| 101 widget_ = nullptr; |
| 102 } |
| 103 |
| 104 views::Widget* widget_ = nullptr; |
| 105 |
| 106 DISALLOW_COPY_AND_ASSIGN(CleanupAnimationObserverTest); |
| 107 }; |
| 108 |
| 109 } // namespace |
| 110 |
| 111 // Tests that basic create-destroy sequence does not crash. |
| 112 TEST_F(CleanupAnimationObserverTest, CreateDestroy) { |
| 113 TestWindowSelectorDelegate delegate; |
| 114 std::unique_ptr<views::Widget> widget( |
| 115 CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| 116 std::unique_ptr<CleanupAnimationObserver> observer( |
| 117 new CleanupAnimationObserver(std::move(widget))); |
| 118 delegate.AddDelayedAnimationObserver(std::move(observer)); |
| 119 } |
| 120 |
| 121 // Tests that completing animation deletes the animation observer and the |
| 122 // test widget and that deleting the WindowSelectorDelegate instance which |
| 123 // owns the observer does not crash. |
| 124 TEST_F(CleanupAnimationObserverTest, CreateAnimateComplete) { |
| 125 TestWindowSelectorDelegate delegate; |
| 126 std::unique_ptr<views::Widget> widget( |
| 127 CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| 128 WmWindow* widget_window = WmLookup::Get()->GetWindowForWidget(widget.get()); |
| 129 { |
| 130 ui::ScopedLayerAnimationSettings animation_settings( |
| 131 widget_window->GetLayer()->GetAnimator()); |
| 132 animation_settings.SetTransitionDuration( |
| 133 base::TimeDelta::FromMilliseconds(1000)); |
| 134 animation_settings.SetPreemptionStrategy( |
| 135 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 136 |
| 137 std::unique_ptr<CleanupAnimationObserver> observer( |
| 138 new CleanupAnimationObserver(std::move(widget))); |
| 139 animation_settings.AddObserver(observer.get()); |
| 140 delegate.AddDelayedAnimationObserver(std::move(observer)); |
| 141 |
| 142 widget_window->SetBounds(gfx::Rect(50, 50, 60, 60)); |
| 143 } |
| 144 // The widget should be destroyed when |animation_settings| gets out of scope |
| 145 // which in absence of NON_ZERO_DURATION animation duration mode completes |
| 146 // the animation and calls OnImplicitAnimationsCompleted() on the cleanup |
| 147 // observer and auto-deletes the owned widget. |
| 148 EXPECT_TRUE(widget_destroyed()); |
| 149 // TestWindowSelectorDelegate going out of scope should not crash. |
| 150 } |
| 151 |
| 152 // Tests that starting an animation and exiting doesn't crash. If not for |
| 153 // TestWindowSelectorDelegate calling Shutdown() on a CleanupAnimationObserver |
| 154 // instance in destructor, this test would have crashed. |
| 155 TEST_F(CleanupAnimationObserverTest, CreateAnimateShutdown) { |
| 156 TestWindowSelectorDelegate delegate; |
| 157 std::unique_ptr<views::Widget> widget( |
| 158 CreateWindowWidget(gfx::Rect(0, 0, 40, 40))); |
| 159 WmWindow* widget_window = WmLookup::Get()->GetWindowForWidget(widget.get()); |
| 160 { |
| 161 // Normal animations for tests have ZERO_DURATION, make sure we are actually |
| 162 // animating the movement. |
| 163 ui::ScopedAnimationDurationScaleMode animation_scale_mode( |
| 164 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); |
| 165 ui::ScopedLayerAnimationSettings animation_settings( |
| 166 widget_window->GetLayer()->GetAnimator()); |
| 167 animation_settings.SetTransitionDuration( |
| 168 base::TimeDelta::FromMilliseconds(1000)); |
| 169 animation_settings.SetPreemptionStrategy( |
| 170 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 171 |
| 172 std::unique_ptr<CleanupAnimationObserver> observer( |
| 173 new CleanupAnimationObserver(std::move(widget))); |
| 174 animation_settings.AddObserver(observer.get()); |
| 175 delegate.AddDelayedAnimationObserver(std::move(observer)); |
| 176 |
| 177 widget_window->SetBounds(gfx::Rect(50, 50, 60, 60)); |
| 178 } |
| 179 // The widget still exists. |
| 180 EXPECT_FALSE(widget_destroyed()); |
| 181 // The test widget is auto-deleted when |delegate| that owns it goes out of |
| 182 // scope. The animation is still active when this happens which should not |
| 183 // crash. |
| 184 } |
| 185 |
| 186 } // namespace ash |
OLD | NEW |