1 // Copyright 2019 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 "ui/views/animation/compositor_animation_runner.h"
6 
7 #include "base/test/bind_test_util.h"
8 #include "base/timer/timer.h"
9 #include "ui/compositor/animation_metrics_reporter.h"
10 #include "ui/compositor/test/draw_waiter_for_test.h"
11 #include "ui/gfx/animation/linear_animation.h"
12 #include "ui/views/animation/animation_delegate_views.h"
13 #include "ui/views/buildflags.h"
14 #include "ui/views/test/widget_test.h"
15 
16 namespace views {
17 namespace test {
18 namespace {
19 constexpr base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(100);
20 }
21 
22 using CompositorAnimationRunnerTest = WidgetTest;
23 
TEST_F(CompositorAnimationRunnerTest,BasicCoverageTest)24 TEST_F(CompositorAnimationRunnerTest, BasicCoverageTest) {
25   WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
26   widget->Show();
27 
28   AnimationDelegateViews delegate(widget->GetContentsView());
29   gfx::LinearAnimation animation(
30       kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
31 
32   base::RepeatingTimer interval_timer;
33   base::RunLoop run_loop;
34 
35   animation.Start();
36   EXPECT_TRUE(animation.is_animating());
37   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
38 
39   interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
40                          if (animation.is_animating())
41                            return;
42 
43                          interval_timer.Stop();
44                          run_loop.Quit();
45                        }));
46 
47   run_loop.Run();
48 }
49 
50 namespace {
51 
52 class TestAnimationMetricsReporter : public ui::AnimationMetricsReporter {
53  public:
54   TestAnimationMetricsReporter() = default;
55   TestAnimationMetricsReporter(TestAnimationMetricsReporter&) = delete;
56   TestAnimationMetricsReporter& operator=(TestAnimationMetricsReporter&) =
57       delete;
58   ~TestAnimationMetricsReporter() override = default;
59 
60   // ui::AnimationMetricsReporter:
Report(int value)61   void Report(int value) override { ++report_count_; }
62 
report_count() const63   int report_count() const { return report_count_; }
64 
65  private:
66   int report_count_ = 0;
67 };
68 
69 // Test AnimationDelegateView which has a non-zero expected animation duration
70 // time, which is required for getting smoothness reports.
71 class TestAnimationDelegateViews : public AnimationDelegateViews {
72  public:
TestAnimationDelegateViews(View * view)73   explicit TestAnimationDelegateViews(View* view)
74       : AnimationDelegateViews(view) {}
75   TestAnimationDelegateViews(TestAnimationDelegateViews&) = delete;
76   TestAnimationDelegateViews& operator=(TestAnimationDelegateViews&) = delete;
77   ~TestAnimationDelegateViews() override = default;
78 
79   // AnimationDelegateViews:
GetAnimationDurationForReporting() const80   base::TimeDelta GetAnimationDurationForReporting() const override {
81     return kDuration;
82   }
83 };
84 
85 }  // namespace
86 
87 // Tests that an attached animation metrics reporter will get smoothness
88 // reports.
TEST_F(CompositorAnimationRunnerTest,AnimationMetricsReporter)89 TEST_F(CompositorAnimationRunnerTest, AnimationMetricsReporter) {
90   WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
91   widget->Show();
92 
93   ui::DrawWaiterForTest::WaitForCompositingStarted(widget->GetCompositor());
94 
95   TestAnimationMetricsReporter metrics_reporter;
96   TestAnimationMetricsReporter metrics_reporter2;
97   TestAnimationDelegateViews delegate(widget->GetContentsView());
98   delegate.SetAnimationMetricsReporter(&metrics_reporter);
99   gfx::LinearAnimation animation(
100       kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
101 
102   base::RepeatingTimer interval_timer;
103   base::RunLoop run_loop;
104 
105   animation.Start();
106   EXPECT_TRUE(animation.is_animating());
107   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
108 
109   interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
110                          if (animation.is_animating())
111                            return;
112 
113                          interval_timer.Stop();
114                          run_loop.Quit();
115                        }));
116   run_loop.Run();
117   EXPECT_EQ(1, metrics_reporter.report_count());
118   EXPECT_EQ(0, metrics_reporter2.report_count());
119 
120   // Tests that switching metrics reporters for the next animation works as
121   // expected.
122   base::RunLoop run_loop2;
123   delegate.SetAnimationMetricsReporter(&metrics_reporter2);
124   animation.Start();
125   EXPECT_TRUE(animation.is_animating());
126 
127   interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
128                          if (animation.is_animating())
129                            return;
130 
131                          interval_timer.Stop();
132                          run_loop2.Quit();
133                        }));
134   run_loop2.Run();
135   EXPECT_EQ(1, metrics_reporter.report_count());
136   EXPECT_EQ(1, metrics_reporter2.report_count());
137 }
138 
139 // No DesktopAura on ChromeOS.
140 // Each widget on MACOSX has its own ui::Compositor.
141 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
142 using CompositorAnimationRunnerDesktopTest = DesktopWidgetTest;
143 
TEST_F(CompositorAnimationRunnerDesktopTest,SwitchCompositor)144 TEST_F(CompositorAnimationRunnerDesktopTest, SwitchCompositor) {
145   WidgetAutoclosePtr widget1(CreateTopLevelNativeWidget());
146   widget1->Show();
147 
148   WidgetAutoclosePtr widget2(CreateTopLevelNativeWidget());
149   widget2->Show();
150 
151   ASSERT_NE(widget1->GetCompositor(), widget2->GetCompositor());
152 
153   Widget* child = CreateChildNativeWidgetWithParent(widget1.get());
154   child->Show();
155   AnimationDelegateViews delegate(child->GetContentsView());
156   gfx::LinearAnimation animation(
157       kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
158 
159   base::RepeatingTimer interval_timer;
160 
161   animation.Start();
162   EXPECT_TRUE(animation.is_animating());
163   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
164   {
165     base::RunLoop run_loop;
166     interval_timer.Start(FROM_HERE, kDuration,
167                          base::BindLambdaForTesting([&]() {
168                            if (animation.is_animating())
169                              return;
170                            interval_timer.Stop();
171                            run_loop.Quit();
172                          }));
173     run_loop.Run();
174   }
175 
176   EXPECT_FALSE(animation.is_animating());
177 
178   Widget::ReparentNativeView(child->GetNativeView(), widget2->GetNativeView());
179   widget1.reset();
180 
181   animation.Start();
182   EXPECT_TRUE(animation.is_animating());
183   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
184 
185   {
186     base::RunLoop run_loop;
187     interval_timer.Start(FROM_HERE, kDuration,
188                          base::BindLambdaForTesting([&]() {
189                            if (animation.is_animating())
190                              return;
191 
192                            interval_timer.Stop();
193                            run_loop.Quit();
194                          }));
195 
196     run_loop.Run();
197   }
198 }
199 #endif
200 
201 }  // namespace test
202 }  // namespace views
203