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.h"
8 #include "base/timer/timer.h"
9 #include "ui/compositor/test/draw_waiter_for_test.h"
10 #include "ui/compositor/throughput_tracker.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 // Test AnimationDelegateView which has a non-zero expected animation duration
53 // time, which is required for getting smoothness reports.
54 class TestAnimationDelegateViews : public AnimationDelegateViews {
55  public:
TestAnimationDelegateViews(View * view)56   explicit TestAnimationDelegateViews(View* view)
57       : AnimationDelegateViews(view) {}
58   TestAnimationDelegateViews(TestAnimationDelegateViews&) = delete;
59   TestAnimationDelegateViews& operator=(TestAnimationDelegateViews&) = delete;
60   ~TestAnimationDelegateViews() override = default;
61 
62   // AnimationDelegateViews:
GetAnimationDurationForReporting() const63   base::TimeDelta GetAnimationDurationForReporting() const override {
64     return kDuration;
65   }
66 };
67 
68 }  // namespace
69 
70 // Tests that ui::ThroughputTracker will report for gfx::Animation.
TEST_F(CompositorAnimationRunnerTest,ThroughputTracker)71 TEST_F(CompositorAnimationRunnerTest, ThroughputTracker) {
72   WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
73   widget->Show();
74 
75   ui::DrawWaiterForTest::WaitForCompositingStarted(widget->GetCompositor());
76 
77   int report_count = 0;
78   int report_count2 = 0;
79 
80   TestAnimationDelegateViews delegate(widget->GetContentsView());
81 
82   gfx::LinearAnimation animation(
83       kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
84 
85   base::RepeatingTimer interval_timer;
86   base::RunLoop run_loop;
87 
88   ui::ThroughputTracker tracker1 =
89       widget->GetCompositor()->RequestNewThroughputTracker();
90   tracker1.Start(base::BindLambdaForTesting(
91       [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
92         ++report_count;
93         run_loop.Quit();
94       }));
95 
96   animation.Start();
97   EXPECT_TRUE(animation.is_animating());
98   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
99 
100   interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
101                          if (animation.is_animating())
102                            return;
103 
104                          interval_timer.Stop();
105                          tracker1.Stop();
106                        }));
107   run_loop.Run();
108   EXPECT_EQ(1, report_count);
109   EXPECT_EQ(0, report_count2);
110 
111   // Tests that switching metrics reporters for the next animation works as
112   // expected.
113   base::RunLoop run_loop2;
114 
115   ui::ThroughputTracker tracker2 =
116       widget->GetCompositor()->RequestNewThroughputTracker();
117   tracker2.Start(base::BindLambdaForTesting(
118       [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
119         ++report_count2;
120         run_loop2.Quit();
121       }));
122 
123   animation.Start();
124   EXPECT_TRUE(animation.is_animating());
125 
126   interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
127                          if (animation.is_animating())
128                            return;
129 
130                          interval_timer.Stop();
131                          tracker2.Stop();
132                        }));
133   run_loop2.Run();
134   EXPECT_EQ(1, report_count);
135   EXPECT_EQ(1, report_count2);
136 }
137 
138 // No DesktopAura on ChromeOS.
139 // Each widget on MACOSX has its own ui::Compositor.
140 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
141 using CompositorAnimationRunnerDesktopTest = DesktopWidgetTest;
142 
TEST_F(CompositorAnimationRunnerDesktopTest,SwitchCompositor)143 TEST_F(CompositorAnimationRunnerDesktopTest, SwitchCompositor) {
144   WidgetAutoclosePtr widget1(CreateTopLevelNativeWidget());
145   widget1->Show();
146 
147   WidgetAutoclosePtr widget2(CreateTopLevelNativeWidget());
148   widget2->Show();
149 
150   ASSERT_NE(widget1->GetCompositor(), widget2->GetCompositor());
151 
152   Widget* child = CreateChildNativeWidgetWithParent(widget1.get());
153   child->Show();
154   AnimationDelegateViews delegate(child->GetContentsView());
155   gfx::LinearAnimation animation(
156       kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
157 
158   base::RepeatingTimer interval_timer;
159 
160   animation.Start();
161   EXPECT_TRUE(animation.is_animating());
162   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
163   {
164     base::RunLoop run_loop;
165     interval_timer.Start(FROM_HERE, kDuration,
166                          base::BindLambdaForTesting([&]() {
167                            if (animation.is_animating())
168                              return;
169                            interval_timer.Stop();
170                            run_loop.Quit();
171                          }));
172     run_loop.Run();
173   }
174 
175   EXPECT_FALSE(animation.is_animating());
176 
177   Widget::ReparentNativeView(child->GetNativeView(), widget2->GetNativeView());
178   widget1.reset();
179 
180   animation.Start();
181   EXPECT_TRUE(animation.is_animating());
182   EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
183 
184   {
185     base::RunLoop run_loop;
186     interval_timer.Start(FROM_HERE, kDuration,
187                          base::BindLambdaForTesting([&]() {
188                            if (animation.is_animating())
189                              return;
190 
191                            interval_timer.Stop();
192                            run_loop.Quit();
193                          }));
194 
195     run_loop.Run();
196   }
197 }
198 #endif
199 
200 }  // namespace test
201 }  // namespace views
202