bounds_animator_unittest.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Copyright (c) 2012 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. #include "ui/views/animation/bounds_animator.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/run_loop.h"
  9. #include "base/test/icu_test_util.h"
  10. #include "base/test/task_environment.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. #include "ui/gfx/animation/slide_animation.h"
  13. #include "ui/gfx/animation/test_animation_delegate.h"
  14. #include "ui/views/view.h"
  15. using gfx::Animation;
  16. using gfx::SlideAnimation;
  17. using gfx::TestAnimationDelegate;
  18. namespace views {
  19. namespace {
  20. class OwnedDelegate : public gfx::AnimationDelegate {
  21. public:
  22. OwnedDelegate() = default;
  23. OwnedDelegate(const OwnedDelegate&) = delete;
  24. OwnedDelegate& operator=(const OwnedDelegate&) = delete;
  25. ~OwnedDelegate() override { deleted_ = true; }
  26. static bool GetAndClearDeleted() {
  27. bool value = deleted_;
  28. deleted_ = false;
  29. return value;
  30. }
  31. static bool GetAndClearCanceled() {
  32. bool value = canceled_;
  33. canceled_ = false;
  34. return value;
  35. }
  36. // Overridden from gfx::AnimationDelegate:
  37. void AnimationCanceled(const Animation* animation) override {
  38. canceled_ = true;
  39. }
  40. private:
  41. static bool deleted_;
  42. static bool canceled_;
  43. };
  44. // static
  45. bool OwnedDelegate::deleted_ = false;
  46. bool OwnedDelegate::canceled_ = false;
  47. class TestView : public View {
  48. public:
  49. TestView() = default;
  50. TestView(const TestView&) = delete;
  51. TestView& operator=(const TestView&) = delete;
  52. void OnDidSchedulePaint(const gfx::Rect& r) override {
  53. ++repaint_count_;
  54. if (dirty_rect_.IsEmpty())
  55. dirty_rect_ = r;
  56. else
  57. dirty_rect_.Union(r);
  58. }
  59. const gfx::Rect& dirty_rect() const { return dirty_rect_; }
  60. void set_repaint_count(int val) { repaint_count_ = val; }
  61. int repaint_count() const { return repaint_count_; }
  62. private:
  63. gfx::Rect dirty_rect_;
  64. int repaint_count_ = 0;
  65. };
  66. class RTLAnimationTestDelegate : public gfx::AnimationDelegate {
  67. public:
  68. RTLAnimationTestDelegate(const gfx::Rect& start,
  69. const gfx::Rect& target,
  70. View* view,
  71. base::RepeatingClosure quit_closure)
  72. : start_(start),
  73. target_(target),
  74. view_(view),
  75. quit_closure_(std::move(quit_closure)) {}
  76. ~RTLAnimationTestDelegate() override = default;
  77. private:
  78. // gfx::AnimationDelegate:
  79. void AnimationProgressed(const Animation* animation) override {
  80. gfx::Transform transform = view_->GetTransform();
  81. ASSERT_TRUE(!transform.IsIdentity());
  82. // In this test, assume that |parent| is root view.
  83. View* parent = view_->parent();
  84. const gfx::Rect start_rect_in_screen = parent->GetMirroredRect(start_);
  85. const gfx::Rect target_rect_in_screen = parent->GetMirroredRect(target_);
  86. gfx::RectF current_bounds_in_screen(
  87. parent->GetMirroredRect(view_->bounds()));
  88. transform.TransformRect(&current_bounds_in_screen);
  89. // Verify that |view_|'s current bounds in screen are valid.
  90. EXPECT_GE(current_bounds_in_screen.x(),
  91. std::min(start_rect_in_screen.x(), target_rect_in_screen.x()));
  92. EXPECT_LE(
  93. current_bounds_in_screen.right(),
  94. std::max(start_rect_in_screen.right(), target_rect_in_screen.right()));
  95. quit_closure_.Run();
  96. }
  97. // Animation initial bounds.
  98. gfx::Rect start_;
  99. // Animation target bounds.
  100. gfx::Rect target_;
  101. // view to be animated.
  102. raw_ptr<View> view_;
  103. base::RepeatingClosure quit_closure_;
  104. };
  105. } // namespace
  106. class BoundsAnimatorTest : public testing::Test {
  107. public:
  108. BoundsAnimatorTest()
  109. : task_environment_(
  110. base::test::TaskEnvironment::TimeSource::MOCK_TIME,
  111. base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
  112. child_(new TestView()) {
  113. parent_.AddChildView(child_.get());
  114. RecreateAnimator(/*use_transforms=*/false);
  115. }
  116. BoundsAnimatorTest(const BoundsAnimatorTest&) = delete;
  117. BoundsAnimatorTest& operator=(const BoundsAnimatorTest&) = delete;
  118. TestView* parent() { return &parent_; }
  119. TestView* child() { return child_; }
  120. BoundsAnimator* animator() { return animator_.get(); }
  121. protected:
  122. void RecreateAnimator(bool use_transforms) {
  123. animator_ = std::make_unique<BoundsAnimator>(&parent_, use_transforms);
  124. animator_->SetAnimationDuration(base::Milliseconds(10));
  125. }
  126. // Animates |child_| to |target_bounds|. Returns the repaint time.
  127. // |use_long_duration| indicates whether long or short bounds animation is
  128. // created.
  129. int GetRepaintTimeFromBoundsAnimation(const gfx::Rect& target_bounds,
  130. bool use_long_duration) {
  131. child()->set_repaint_count(0);
  132. const base::TimeDelta animation_duration =
  133. base::Milliseconds(use_long_duration ? 2000 : 10);
  134. animator()->SetAnimationDuration(animation_duration);
  135. animator()->AnimateViewTo(child(), target_bounds);
  136. animator()->SetAnimationDelegate(child(),
  137. std::make_unique<TestAnimationDelegate>());
  138. // The animator should be animating now.
  139. EXPECT_TRUE(animator()->IsAnimating());
  140. EXPECT_TRUE(animator()->IsAnimating(child()));
  141. // Run the message loop; the delegate exits the loop when the animation is
  142. // done.
  143. if (use_long_duration)
  144. task_environment_.FastForwardBy(animation_duration);
  145. base::RunLoop().Run();
  146. // Make sure the bounds match of the view that was animated match and the
  147. // layer is destroyed.
  148. EXPECT_EQ(target_bounds, child()->bounds());
  149. EXPECT_FALSE(child()->layer());
  150. // |child| shouldn't be animating anymore.
  151. EXPECT_FALSE(animator()->IsAnimating(child()));
  152. return child()->repaint_count();
  153. }
  154. base::test::SingleThreadTaskEnvironment task_environment_;
  155. private:
  156. TestView parent_;
  157. // TODO(crbug.com/1298696): views_unittests breaks with MTECheckedPtr
  158. // enabled. Triage.
  159. raw_ptr<TestView, DegradeToNoOpWhenMTE> child_; // Owned by |parent_|.
  160. std::unique_ptr<BoundsAnimator> animator_;
  161. };
  162. // Checks animate view to.
  163. TEST_F(BoundsAnimatorTest, AnimateViewTo) {
  164. gfx::Rect initial_bounds(0, 0, 10, 10);
  165. child()->SetBoundsRect(initial_bounds);
  166. gfx::Rect target_bounds(10, 10, 20, 20);
  167. animator()->AnimateViewTo(child(), target_bounds);
  168. animator()->SetAnimationDelegate(child(),
  169. std::make_unique<TestAnimationDelegate>());
  170. // The animator should be animating now.
  171. EXPECT_TRUE(animator()->IsAnimating());
  172. EXPECT_TRUE(animator()->IsAnimating(child()));
  173. // Run the message loop; the delegate exits the loop when the animation is
  174. // done.
  175. base::RunLoop().Run();
  176. // Make sure the bounds match of the view that was animated match.
  177. EXPECT_EQ(target_bounds, child()->bounds());
  178. // |child| shouldn't be animating anymore.
  179. EXPECT_FALSE(animator()->IsAnimating(child()));
  180. // The parent should have been told to repaint as the animation progressed.
  181. // The resulting rect is the union of the original and target bounds.
  182. EXPECT_EQ(gfx::UnionRects(target_bounds, initial_bounds),
  183. parent()->dirty_rect());
  184. }
  185. // Make sure that removing/deleting a child view while animating stops the
  186. // view's animation and will not result in a crash.
  187. TEST_F(BoundsAnimatorTest, DeleteWhileAnimating) {
  188. animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10));
  189. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  190. EXPECT_TRUE(animator()->IsAnimating(child()));
  191. // Make sure that animation is removed upon deletion.
  192. delete child();
  193. EXPECT_FALSE(animator()->GetAnimationForView(child()));
  194. EXPECT_FALSE(animator()->IsAnimating(child()));
  195. }
  196. // Make sure an AnimationDelegate is deleted when canceled.
  197. TEST_F(BoundsAnimatorTest, DeleteDelegateOnCancel) {
  198. animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10));
  199. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  200. animator()->Cancel();
  201. // The animator should no longer be animating.
  202. EXPECT_FALSE(animator()->IsAnimating());
  203. EXPECT_FALSE(animator()->IsAnimating(child()));
  204. // The cancel should both cancel the delegate and delete it.
  205. EXPECT_TRUE(OwnedDelegate::GetAndClearCanceled());
  206. EXPECT_TRUE(OwnedDelegate::GetAndClearDeleted());
  207. }
  208. // Make sure that the AnimationDelegate of the running animation is deleted when
  209. // a new animation is scheduled.
  210. TEST_F(BoundsAnimatorTest, DeleteDelegateOnNewAnimate) {
  211. const gfx::Rect target_bounds_first(0, 0, 10, 10);
  212. animator()->AnimateViewTo(child(), target_bounds_first);
  213. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  214. // Start an animation on the same view with different target bounds.
  215. const gfx::Rect target_bounds_second(0, 5, 10, 10);
  216. animator()->AnimateViewTo(child(), target_bounds_second);
  217. // Starting a new animation should both cancel the delegate and delete it.
  218. EXPECT_TRUE(OwnedDelegate::GetAndClearDeleted());
  219. EXPECT_TRUE(OwnedDelegate::GetAndClearCanceled());
  220. }
  221. // Make sure that the duplicate animation request does not interrupt the running
  222. // animation.
  223. TEST_F(BoundsAnimatorTest, HandleDuplicateAnimation) {
  224. const gfx::Rect target_bounds(0, 0, 10, 10);
  225. animator()->AnimateViewTo(child(), target_bounds);
  226. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  227. // Request the animation with the same view/target bounds.
  228. animator()->AnimateViewTo(child(), target_bounds);
  229. // Verify that the existing animation is not interrupted.
  230. EXPECT_FALSE(OwnedDelegate::GetAndClearDeleted());
  231. EXPECT_FALSE(OwnedDelegate::GetAndClearCanceled());
  232. }
  233. // Make sure that a duplicate animation request that specifies a different
  234. // delegate swaps out that delegate.
  235. TEST_F(BoundsAnimatorTest, DuplicateAnimationsCanReplaceDelegate) {
  236. const gfx::Rect target_bounds(0, 0, 10, 10);
  237. animator()->AnimateViewTo(child(), target_bounds);
  238. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  239. // Request the animation with the same view/target bounds but a different
  240. // delegate.
  241. animator()->AnimateViewTo(child(), target_bounds,
  242. std::make_unique<OwnedDelegate>());
  243. // Verify that the delegate was replaced.
  244. EXPECT_TRUE(OwnedDelegate::GetAndClearDeleted());
  245. // The animation still should not have been canceled.
  246. EXPECT_FALSE(OwnedDelegate::GetAndClearCanceled());
  247. }
  248. // Makes sure StopAnimating works.
  249. TEST_F(BoundsAnimatorTest, StopAnimating) {
  250. std::unique_ptr<OwnedDelegate> delegate(std::make_unique<OwnedDelegate>());
  251. animator()->AnimateViewTo(child(), gfx::Rect(0, 0, 10, 10));
  252. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  253. animator()->StopAnimatingView(child());
  254. // Shouldn't be animating now.
  255. EXPECT_FALSE(animator()->IsAnimating());
  256. EXPECT_FALSE(animator()->IsAnimating(child()));
  257. // Stopping should both cancel the delegate and delete it.
  258. EXPECT_TRUE(OwnedDelegate::GetAndClearDeleted());
  259. EXPECT_TRUE(OwnedDelegate::GetAndClearCanceled());
  260. }
  261. // Make sure Complete completes in-progress animations.
  262. TEST_F(BoundsAnimatorTest, CompleteAnimation) {
  263. std::unique_ptr<OwnedDelegate> delegate(std::make_unique<OwnedDelegate>());
  264. const gfx::Rect target_bounds = gfx::Rect(0, 0, 10, 10);
  265. animator()->AnimateViewTo(child(), target_bounds);
  266. animator()->SetAnimationDelegate(child(), std::make_unique<OwnedDelegate>());
  267. animator()->Complete();
  268. // Shouldn't be animating now.
  269. EXPECT_FALSE(animator()->IsAnimating());
  270. EXPECT_FALSE(animator()->IsAnimating(child()));
  271. // Child should have been moved to the animation's target.
  272. EXPECT_EQ(target_bounds, child()->bounds());
  273. // Completing should delete the delegate.
  274. EXPECT_TRUE(OwnedDelegate::GetAndClearDeleted());
  275. EXPECT_FALSE(OwnedDelegate::GetAndClearCanceled());
  276. }
  277. // Verify that transform is used when the animation target bounds have the
  278. // same size with the current bounds' meanwhile having the transform option
  279. // enabled.
  280. TEST_F(BoundsAnimatorTest, UseTransformsAnimateViewTo) {
  281. RecreateAnimator(/*use_transforms=*/true);
  282. const gfx::Rect initial_bounds(0, 0, 10, 10);
  283. child()->SetBoundsRect(initial_bounds);
  284. // Ensure that the target bounds have the same size with the initial bounds'
  285. // to apply transform to bounds animation.
  286. const gfx::Rect target_bounds_without_resize(gfx::Point(10, 10),
  287. initial_bounds.size());
  288. const int repaint_time_from_short_animation =
  289. GetRepaintTimeFromBoundsAnimation(target_bounds_without_resize,
  290. /*use_long_duration=*/false);
  291. const int repaint_time_from_long_animation =
  292. GetRepaintTimeFromBoundsAnimation(initial_bounds,
  293. /*use_long_duration=*/true);
  294. // The number of repaints in long animation should be the same as with the
  295. // short animation.
  296. EXPECT_EQ(repaint_time_from_short_animation,
  297. repaint_time_from_long_animation);
  298. }
  299. // Verify that transform is not used when the animation target bounds have the
  300. // different size from the current bounds' even if transform is preferred.
  301. TEST_F(BoundsAnimatorTest, NoTransformForScalingAnimation) {
  302. RecreateAnimator(/*use_transforms=*/true);
  303. const gfx::Rect initial_bounds(0, 0, 10, 10);
  304. child()->SetBoundsRect(initial_bounds);
  305. // Ensure that the target bounds have the different size with the initial
  306. // bounds' to repaint bounds in each animation tick.
  307. const gfx::Rect target_bounds_with_reize(gfx::Point(10, 10),
  308. gfx::Size(20, 20));
  309. const int repaint_time_from_short_animation =
  310. GetRepaintTimeFromBoundsAnimation(target_bounds_with_reize,
  311. /*use_long_duration=*/false);
  312. const int repaint_time_from_long_animation =
  313. GetRepaintTimeFromBoundsAnimation(initial_bounds,
  314. /*use_long_duration=*/true);
  315. // When creating bounds animation with repaint, the longer bounds animation
  316. // should have more repaint counts.
  317. EXPECT_GT(repaint_time_from_long_animation,
  318. repaint_time_from_short_animation);
  319. }
  320. // Tests that the transforms option does not crash when a view's bounds start
  321. // off empty.
  322. TEST_F(BoundsAnimatorTest, UseTransformsAnimateViewToEmptySrc) {
  323. RecreateAnimator(/*use_transforms=*/true);
  324. gfx::Rect initial_bounds(0, 0, 0, 0);
  325. child()->SetBoundsRect(initial_bounds);
  326. gfx::Rect target_bounds(10, 10, 20, 20);
  327. child()->set_repaint_count(0);
  328. animator()->AnimateViewTo(child(), target_bounds);
  329. animator()->SetAnimationDelegate(child(),
  330. std::make_unique<TestAnimationDelegate>());
  331. // The animator should be animating now.
  332. EXPECT_TRUE(animator()->IsAnimating());
  333. EXPECT_TRUE(animator()->IsAnimating(child()));
  334. // Run the message loop; the delegate exits the loop when the animation is
  335. // done.
  336. base::RunLoop().Run();
  337. EXPECT_EQ(target_bounds, child()->bounds());
  338. }
  339. // Tests that when using the transform option on the bounds animator, cancelling
  340. // the animation part way results in the correct bounds applied.
  341. TEST_F(BoundsAnimatorTest, UseTransformsCancelAnimation) {
  342. RecreateAnimator(/*use_transforms=*/true);
  343. // Ensure that |initial_bounds| has the same size with |target_bounds| to
  344. // create bounds animation via the transform.
  345. const gfx::Rect initial_bounds(0, 0, 10, 10);
  346. const gfx::Rect target_bounds(10, 10, 10, 10);
  347. child()->SetBoundsRect(initial_bounds);
  348. const base::TimeDelta duration = base::Milliseconds(200);
  349. animator()->SetAnimationDuration(duration);
  350. // Use a linear tween so we can estimate the expected bounds.
  351. animator()->set_tween_type(gfx::Tween::LINEAR);
  352. animator()->AnimateViewTo(child(), target_bounds);
  353. animator()->SetAnimationDelegate(child(),
  354. std::make_unique<TestAnimationDelegate>());
  355. EXPECT_TRUE(animator()->IsAnimating());
  356. EXPECT_TRUE(animator()->IsAnimating(child()));
  357. // Stop halfway and cancel. The child should have its bounds updated to
  358. // exactly halfway between |initial_bounds| and |target_bounds|.
  359. const gfx::Rect expected_bounds(5, 5, 10, 10);
  360. task_environment_.FastForwardBy(base::Milliseconds(100));
  361. EXPECT_EQ(initial_bounds, child()->bounds());
  362. animator()->Cancel();
  363. EXPECT_EQ(expected_bounds, child()->bounds());
  364. }
  365. // Test that when using the transform option on the bounds animator, cancelling
  366. // the animation part way under RTL results in the correct bounds applied.
  367. TEST_F(BoundsAnimatorTest, UseTransformsCancelAnimationRTL) {
  368. // Enable RTL.
  369. base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
  370. RecreateAnimator(/*use_transforms=*/true);
  371. // Ensure that |initial_bounds| has the same size with |target_bounds| to
  372. // create bounds animation via the transform.
  373. const gfx::Rect initial_bounds(0, 0, 10, 10);
  374. const gfx::Rect target_bounds(10, 10, 10, 10);
  375. child()->SetBoundsRect(initial_bounds);
  376. const base::TimeDelta duration = base::Milliseconds(200);
  377. animator()->SetAnimationDuration(duration);
  378. // Use a linear tween so we can estimate the expected bounds.
  379. animator()->set_tween_type(gfx::Tween::LINEAR);
  380. animator()->AnimateViewTo(child(), target_bounds);
  381. EXPECT_TRUE(animator()->IsAnimating());
  382. EXPECT_TRUE(animator()->IsAnimating(child()));
  383. // Stop halfway and cancel. The child should have its bounds updated to
  384. // exactly halfway between |initial_bounds| and |target_bounds|.
  385. const gfx::Rect expected_bounds(5, 5, 10, 10);
  386. task_environment_.FastForwardBy(base::Milliseconds(100));
  387. EXPECT_EQ(initial_bounds, child()->bounds());
  388. animator()->Cancel();
  389. EXPECT_EQ(expected_bounds, child()->bounds());
  390. }
  391. // Verify that the bounds animation which updates the transform of views work
  392. // as expected under RTL (https://crbug.com/1067033).
  393. TEST_F(BoundsAnimatorTest, VerifyBoundsAnimatorUnderRTL) {
  394. // Enable RTL.
  395. base::test::ScopedRestoreICUDefaultLocale scoped_locale("he");
  396. RecreateAnimator(/*use_transforms=*/true);
  397. parent()->SetBounds(0, 0, 40, 40);
  398. const gfx::Rect initial_bounds(0, 0, 10, 10);
  399. child()->SetBoundsRect(initial_bounds);
  400. const gfx::Rect target_bounds(10, 10, 10, 10);
  401. const base::TimeDelta animation_duration = base::Milliseconds(10);
  402. animator()->SetAnimationDuration(animation_duration);
  403. child()->set_repaint_count(0);
  404. animator()->AnimateViewTo(child(), target_bounds);
  405. base::RunLoop run_loop;
  406. animator()->SetAnimationDelegate(
  407. child(),
  408. std::make_unique<RTLAnimationTestDelegate>(
  409. initial_bounds, target_bounds, child(), run_loop.QuitClosure()));
  410. // The animator should be animating now.
  411. EXPECT_TRUE(animator()->IsAnimating());
  412. EXPECT_TRUE(animator()->IsAnimating(child()));
  413. run_loop.Run();
  414. EXPECT_FALSE(animator()->IsAnimating(child()));
  415. }
  416. } // namespace views