desktop_viewport_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // Copyright 2017 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 "remoting/client/ui/desktop_viewport.h"
  5. #include "base/bind.h"
  6. #include "base/location.h"
  7. #include "base/strings/stringprintf.h"
  8. #include "testing/gtest/include/gtest/gtest.h"
  9. namespace remoting {
  10. namespace {
  11. const float EPSILON = 0.001f;
  12. } // namespace
  13. class DesktopViewportTest : public testing::Test {
  14. public:
  15. void SetUp() override;
  16. void TearDown() override;
  17. protected:
  18. void AssertTransformationReceived(const base::Location& from_here,
  19. float scale,
  20. float offset_x,
  21. float offset_y);
  22. ViewMatrix ReleaseReceivedTransformation();
  23. DesktopViewport viewport_;
  24. private:
  25. void OnTransformationChanged(const ViewMatrix& matrix);
  26. ViewMatrix received_transformation_;
  27. };
  28. void DesktopViewportTest::SetUp() {
  29. viewport_.RegisterOnTransformationChangedCallback(
  30. base::BindRepeating(&DesktopViewportTest::OnTransformationChanged,
  31. base::Unretained(this)),
  32. true);
  33. }
  34. void DesktopViewportTest::TearDown() {
  35. ASSERT_TRUE(received_transformation_.IsEmpty());
  36. }
  37. void DesktopViewportTest::AssertTransformationReceived(
  38. const base::Location& from_here,
  39. float scale,
  40. float offset_x,
  41. float offset_y) {
  42. ASSERT_FALSE(received_transformation_.IsEmpty())
  43. << "Matrix has not been received yet."
  44. << "Location: " << from_here.ToString();
  45. ViewMatrix expected(scale, {offset_x, offset_y});
  46. std::array<float, 9> expected_array = expected.ToMatrixArray();
  47. std::array<float, 9> actual_array = received_transformation_.ToMatrixArray();
  48. for (int i = 0; i < 9; i++) {
  49. float diff = expected_array[i] - actual_array[i];
  50. ASSERT_TRUE(diff > -EPSILON && diff < EPSILON)
  51. << "Matrix doesn't match. \n"
  52. << base::StringPrintf("Expected scale: %f, offset: (%f, %f)\n",
  53. expected_array[0], expected_array[2],
  54. expected_array[5])
  55. << base::StringPrintf("Actual scale: %f, offset: (%f, %f)\n",
  56. actual_array[0], actual_array[2], actual_array[5])
  57. << "Location: " << from_here.ToString();
  58. }
  59. received_transformation_ = ViewMatrix();
  60. }
  61. ViewMatrix DesktopViewportTest::ReleaseReceivedTransformation() {
  62. EXPECT_FALSE(received_transformation_.IsEmpty());
  63. ViewMatrix out = received_transformation_;
  64. received_transformation_ = ViewMatrix();
  65. return out;
  66. }
  67. void DesktopViewportTest::OnTransformationChanged(const ViewMatrix& matrix) {
  68. ASSERT_TRUE(received_transformation_.IsEmpty())
  69. << "Previous matrix has not been asserted.";
  70. received_transformation_ = matrix;
  71. }
  72. TEST_F(DesktopViewportTest, TestViewportInitialization1) {
  73. // VP < DP. Desktop shrinks to fit.
  74. // +====+------+
  75. // | VP | DP |
  76. // | | |
  77. // +====+------+
  78. viewport_.SetDesktopSize(8, 6);
  79. viewport_.SetSurfaceSize(2, 3);
  80. AssertTransformationReceived(FROM_HERE, 0.5f, 0.f, 0.f);
  81. }
  82. TEST_F(DesktopViewportTest, TestViewportInitialization2) {
  83. // VP < DP. Desktop shrinks to fit.
  84. // +-----------------+
  85. // | DP |
  86. // | |
  87. // +=================+
  88. // | VP |
  89. // +=================+
  90. viewport_.SetDesktopSize(8, 6);
  91. viewport_.SetSurfaceSize(3, 2);
  92. AssertTransformationReceived(FROM_HERE, 0.375, 0.f, 0.f);
  93. }
  94. TEST_F(DesktopViewportTest, TestViewportInitialization3) {
  95. // VP < DP. Desktop shrinks to fit.
  96. // +========+----+
  97. // | VP | DP |
  98. // +========+----+
  99. viewport_.SetDesktopSize(9, 3);
  100. viewport_.SetSurfaceSize(2, 1);
  101. AssertTransformationReceived(FROM_HERE, 1 / 3.f, 0.f, 0.f);
  102. }
  103. TEST_F(DesktopViewportTest, TestViewportInitialization4) {
  104. // VP > DP. Desktop grows to fit.
  105. // +====+------+
  106. // | VP | DP |
  107. // | | |
  108. // +====+------+
  109. viewport_.SetDesktopSize(2, 1);
  110. viewport_.SetSurfaceSize(3, 4);
  111. AssertTransformationReceived(FROM_HERE, 4.f, 0.f, 0.f);
  112. }
  113. TEST_F(DesktopViewportTest, TestMoveDesktop) {
  114. // +====+------+
  115. // | VP | DP |
  116. // | | |
  117. // +====+------+
  118. viewport_.SetDesktopSize(8, 6);
  119. viewport_.SetSurfaceSize(2, 3);
  120. AssertTransformationReceived(FROM_HERE, 0.5f, 0.f, 0.f);
  121. // <--- DP
  122. // +------+====+
  123. // | DP | VP |
  124. // | | |
  125. // +------+====+
  126. viewport_.MoveDesktop(-2.f, 0.f);
  127. AssertTransformationReceived(FROM_HERE, 0.5f, -2.f, 0.f);
  128. // +====+
  129. // +----| VP |
  130. // | DP | |
  131. // | +====+
  132. // +--------+
  133. // Bounces back.
  134. viewport_.MoveDesktop(-1.f, 1.f);
  135. AssertTransformationReceived(FROM_HERE, 0.5f, -2.f, 0.f);
  136. }
  137. TEST_F(DesktopViewportTest, TestMoveAndScaleDesktop) {
  138. // Number in surface coordinate.
  139. //
  140. // +====+------+
  141. // | VP | DP |
  142. // | | | 3
  143. // +====+------+
  144. // 4
  145. viewport_.SetDesktopSize(8, 6);
  146. viewport_.SetSurfaceSize(2, 3);
  147. AssertTransformationReceived(FROM_HERE, 0.5f, 0.f, 0.f);
  148. // Scale at pivot point (2, 3) by 1.5x.
  149. // +------------------+
  150. // | |
  151. // | +====+ DP | 4.5
  152. // | | VP | |
  153. // | | | |
  154. // +---+====+---------+
  155. // 2 6
  156. viewport_.ScaleDesktop(2.f, 3.f, 1.5f);
  157. AssertTransformationReceived(FROM_HERE, 0.75f, -1.f, -1.5f);
  158. // Move VP to the top-right.
  159. // +-------------+====+
  160. // | | VP |
  161. // | DP | |
  162. // | +====+ 4.5
  163. // | 2 |
  164. // +------------------+
  165. // 6
  166. viewport_.MoveDesktop(-10000.f, 10000.f);
  167. AssertTransformationReceived(FROM_HERE, 0.75f, -4.f, 0.f);
  168. // Scale at (2, 0) by 0.5x.
  169. // VP
  170. // +====+
  171. // +--+----+
  172. // DP | | |
  173. // +--+----+
  174. // +====+
  175. viewport_.ScaleDesktop(2.f, 0.f, 0.5f);
  176. AssertTransformationReceived(FROM_HERE, 0.375, -1.f, 0.375f);
  177. // Scale all the way down.
  178. // +========+
  179. // | VP |
  180. // +--------+
  181. // | DP |
  182. // +--------+
  183. // | |
  184. // +========+
  185. viewport_.ScaleDesktop(20.f, 0.f, 0.0001f);
  186. AssertTransformationReceived(FROM_HERE, 0.25f, 0.f, 0.75f);
  187. }
  188. TEST_F(DesktopViewportTest, TestSetViewportCenter) {
  189. // Numbers in desktop coordinates.
  190. //
  191. // +====+------+
  192. // | VP | DP |
  193. // | | | 6
  194. // +====+------+
  195. // 8
  196. viewport_.SetDesktopSize(8, 6);
  197. viewport_.SetSurfaceSize(2, 3);
  198. AssertTransformationReceived(FROM_HERE, 0.5f, 0.f, 0.f);
  199. // 1.6
  200. // +==+--------+
  201. // |VP|2.4 DP |
  202. // +==+ | 6
  203. // +-----------+
  204. // 8
  205. viewport_.ScaleDesktop(0.f, 0.f, 2.5f);
  206. AssertTransformationReceived(FROM_HERE, 1.25f, 0.f, 0.f);
  207. // Move VP to center of the desktop.
  208. // +------------------+
  209. // | +1.6=+ |
  210. // | | VP |2.4 | 6
  211. // | +====+ |
  212. // +------------------+
  213. // 8
  214. viewport_.SetViewportCenter(4.f, 3.f);
  215. AssertTransformationReceived(FROM_HERE, 1.25f, -4.f, -2.25f);
  216. // Move it out of bound and bounce it back.
  217. // +------------------+
  218. // | |
  219. // | DP |
  220. // | +====+
  221. // | | VP |
  222. // +---------------| |
  223. // +====+
  224. viewport_.SetViewportCenter(1000.f, 1000.f);
  225. AssertTransformationReceived(FROM_HERE, 1.25f, -8.f, -4.5f);
  226. }
  227. TEST_F(DesktopViewportTest, TestScaleDesktop) {
  228. // Number in surface coordinate.
  229. //
  230. // +====+------+
  231. // | VP | DP |
  232. // | | | 3
  233. // +====+------+
  234. // 4
  235. viewport_.SetDesktopSize(8, 6);
  236. viewport_.SetSurfaceSize(2, 3);
  237. AssertTransformationReceived(FROM_HERE, 0.5f, 0.f, 0.f);
  238. ViewMatrix old_transformation(0.5f, {0.f, 0.f});
  239. ViewMatrix::Point surface_point = old_transformation.MapPoint({1.2f, 1.3f});
  240. // Scale a little bit at a pivot point.
  241. viewport_.ScaleDesktop(surface_point.x, surface_point.y, 1.1f);
  242. ViewMatrix new_transformation = ReleaseReceivedTransformation();
  243. // Verify the pivot point is fixed.
  244. ViewMatrix::Point new_surface_point =
  245. new_transformation.MapPoint({1.2f, 1.3f});
  246. ASSERT_FLOAT_EQ(surface_point.x, new_surface_point.x);
  247. ASSERT_FLOAT_EQ(surface_point.y, new_surface_point.y);
  248. // Verify the scale is correct.
  249. ASSERT_FLOAT_EQ(old_transformation.GetScale() * 1.1f,
  250. new_transformation.GetScale());
  251. }
  252. TEST_F(DesktopViewportTest, AsymmetricalSafeInsetsPanAndZoom) {
  253. // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
  254. // left: 2, top: 2, right: 1, bottom: 1
  255. viewport_.SetDesktopSize(6, 5);
  256. viewport_.SetSafeInsets(2, 2, 1, 1);
  257. viewport_.SetSurfaceSize(6, 5);
  258. // Viewport is initialized to fit the inset area instead of the whole surface
  259. // area.
  260. AssertTransformationReceived(FROM_HERE, 0.5, 2, 2);
  261. // Move the viewport all the way to the bottom right.
  262. viewport_.MoveViewport(100, 100);
  263. // The bottom right of the desktop is stuck with the bottom right of the
  264. // safe area.
  265. AssertTransformationReceived(FROM_HERE, 0.5, 2, 1.5);
  266. // Zoom the viewport on the bottom right of the safe area to match the
  267. // resolution of the surface.
  268. viewport_.ScaleDesktop(5, 4, 2);
  269. AssertTransformationReceived(FROM_HERE, 1, -1, -1);
  270. // Move the desktop by <1, 1>. Now it perfectly fits the surface.
  271. viewport_.MoveDesktop(1, 1);
  272. AssertTransformationReceived(FROM_HERE, 1, 0, 0);
  273. // Move the desktop all the way to the top left. Now it stucks with the top
  274. // left corner of the safe area.
  275. viewport_.MoveDesktop(100, 100);
  276. AssertTransformationReceived(FROM_HERE, 1, 2, 2);
  277. }
  278. TEST_F(DesktopViewportTest, SingleNotchSafeInsetPanAndZoom) {
  279. // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
  280. // left: 1, right: 1, top: 0, bottom: 0
  281. viewport_.SetDesktopSize(6, 5);
  282. viewport_.SetSafeInsets(1, 1, 0, 0);
  283. viewport_.SetSurfaceSize(6, 5);
  284. AssertTransformationReceived(FROM_HERE, 5 / 6.f, 1, 1);
  285. viewport_.MoveViewport(100, 100);
  286. AssertTransformationReceived(FROM_HERE, 5 / 6.f, 1, 5 / 6.f);
  287. viewport_.ScaleDesktop(6, 5, 1.2);
  288. AssertTransformationReceived(FROM_HERE, 1, 0, 0);
  289. }
  290. TEST_F(DesktopViewportTest, SymmetricSafeInsetPanAndZoom) {
  291. // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
  292. // left: 1, right: 1, top: 1, bottom: 1
  293. viewport_.SetDesktopSize(6, 5);
  294. viewport_.SetSafeInsets(1, 1, 1, 1);
  295. viewport_.SetSurfaceSize(6, 5);
  296. AssertTransformationReceived(FROM_HERE, 2 / 3.f, 1, 1);
  297. viewport_.MoveViewport(100, 100);
  298. AssertTransformationReceived(FROM_HERE, 2 / 3.f, 1, 2 / 3.f);
  299. viewport_.ScaleDesktop(5, 4, 1.5);
  300. AssertTransformationReceived(FROM_HERE, 1, -1, -1);
  301. viewport_.MoveDesktop(1, 1);
  302. AssertTransformationReceived(FROM_HERE, 1, 0, 0);
  303. }
  304. TEST_F(DesktopViewportTest, RemoveSafeInsets) {
  305. // Initialize with 6x5 desktop and 6x5 screen with this safe inset:
  306. // left: 2, top: 2, right: 1, bottom: 1
  307. viewport_.SetDesktopSize(6, 5);
  308. viewport_.SetSafeInsets(2, 2, 1, 1);
  309. viewport_.SetSurfaceSize(6, 5);
  310. AssertTransformationReceived(FROM_HERE, 0.5, 2, 2);
  311. // Move the viewport all the way to the bottom right.
  312. viewport_.MoveViewport(100, 100);
  313. AssertTransformationReceived(FROM_HERE, 0.5, 2, 1.5);
  314. // Now remove the safe insets.
  315. viewport_.SetSafeInsets(0, 0, 0, 0);
  316. // Desktop is now stretched to fit the whole surface.
  317. AssertTransformationReceived(FROM_HERE, 1, 0, 0);
  318. }
  319. TEST_F(DesktopViewportTest, AddAndRemoveSafeInsets) {
  320. // This test case tests showing and hiding soft keyboard.
  321. // Initialize with 12x9 desktop and screen with this safe inset:
  322. // left: 2, top: 2, right: 1, bottom: 1
  323. viewport_.SetDesktopSize(12, 9);
  324. viewport_.SetSafeInsets(2, 2, 1, 1);
  325. viewport_.SetSurfaceSize(12, 9);
  326. AssertTransformationReceived(FROM_HERE, 0.75, 2, 2);
  327. // Increase the bottom insets to simulate keyboard popup.
  328. viewport_.SetSafeInsets(2, 2, 1, 4);
  329. AssertTransformationReceived(FROM_HERE, 0.75, 2, 2);
  330. // Move the viewport all the way down. (=moving the desktop all the way up.)
  331. viewport_.MoveViewport(100, 100);
  332. AssertTransformationReceived(FROM_HERE, 0.75, 2, -1.75);
  333. // Now remove the extra insets.
  334. viewport_.SetSafeInsets(2, 2, 1, 1);
  335. // Viewport should bounce back.
  336. AssertTransformationReceived(FROM_HERE, 0.75, 2, 1.25);
  337. }
  338. } // namespace remoting