1# Copyright 2021 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Tests for pw_console.console_app""" 15 16import logging 17import unittest 18from unittest.mock import MagicMock 19 20from prompt_toolkit.application import create_app_session 21from prompt_toolkit.output import ColorDepth 22 23# inclusive-language: ignore 24from prompt_toolkit.output import DummyOutput as FakeOutput 25 26from pw_console.console_app import ConsoleApp 27from pw_console.console_prefs import ConsolePrefs 28from pw_console.window_manager import _WINDOW_SPLIT_ADJUST 29from pw_console.window_list import _WINDOW_HEIGHT_ADJUST, DisplayMode 30 31 32def _create_console_app(logger_count=2): 33 prefs = ConsolePrefs( 34 project_file=False, project_user_file=False, user_file=False 35 ) 36 prefs.set_code_theme('default') 37 console_app = ConsoleApp(color_depth=ColorDepth.DEPTH_8_BIT, prefs=prefs) 38 console_app.focus_on_container = MagicMock() 39 40 loggers = {} 41 for i in range(logger_count): 42 loggers['Log{}'.format(i)] = [logging.getLogger('test_log{}'.format(i))] 43 for window_title, logger_instances in loggers.items(): 44 console_app.add_log_handler(window_title, logger_instances) 45 return console_app 46 47 48_WINDOW_MANAGER_WIDTH = 80 49_WINDOW_MANAGER_HEIGHT = 30 50_DEFAULT_WINDOW_WIDTH = 10 51_DEFAULT_WINDOW_HEIGHT = 10 52 53 54def _window_list_widths(window_manager): 55 window_manager.update_window_manager_size( 56 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 57 ) 58 59 return [ 60 window_list.width.preferred 61 for window_list in window_manager.window_lists 62 ] 63 64 65def _window_list_heights(window_manager): 66 window_manager.update_window_manager_size( 67 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 68 ) 69 70 return [ 71 window_list.height.preferred 72 for window_list in window_manager.window_lists 73 ] 74 75 76def _window_pane_widths(window_manager, window_list_index=0): 77 window_manager.update_window_manager_size( 78 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 79 ) 80 81 return [ 82 pane.width.preferred 83 for pane in window_manager.window_lists[window_list_index].active_panes 84 ] 85 86 87def _window_pane_heights(window_manager, window_list_index=0): 88 window_manager.update_window_manager_size( 89 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 90 ) 91 92 return [ 93 pane.height.preferred 94 for pane in window_manager.window_lists[window_list_index].active_panes 95 ] 96 97 98def _window_pane_counts(window_manager): 99 return [ 100 len(window_list.active_panes) 101 for window_list in window_manager.window_lists 102 ] 103 104 105def window_pane_titles(window_manager): 106 return [ 107 [ 108 pane.pane_title() + ' - ' + pane.pane_subtitle() 109 for pane in window_list.active_panes 110 ] 111 for window_list in window_manager.window_lists 112 ] 113 114 115def target_list_and_pane(window_manager, list_index, pane_index): 116 # pylint: disable=protected-access 117 # Bypass prompt_toolkit has_focus() 118 pane = window_manager.window_lists[list_index].active_panes[pane_index] 119 # If the pane is in focus it will be visible. 120 pane.show_pane = True 121 window_manager._get_active_window_list_and_pane = MagicMock( # type: ignore 122 return_value=( 123 window_manager.window_lists[list_index], 124 window_manager.window_lists[list_index].active_panes[pane_index], 125 ) 126 ) 127 128 129class TestWindowManager(unittest.TestCase): 130 # pylint: disable=protected-access 131 """Tests for window management functions.""" 132 133 maxDiff = None 134 135 def test_find_window_list_and_pane(self) -> None: 136 """Test getting the window list for a given pane.""" 137 with create_app_session(output=FakeOutput()): 138 console_app = _create_console_app(logger_count=3) 139 140 window_manager = console_app.window_manager 141 self.assertEqual([4], _window_pane_counts(window_manager)) 142 143 # Move 2 windows to the right into their own splits 144 target_list_and_pane(window_manager, 0, 0) 145 window_manager.move_pane_right() 146 target_list_and_pane(window_manager, 0, 0) 147 window_manager.move_pane_right() 148 target_list_and_pane(window_manager, 1, 0) 149 window_manager.move_pane_right() 150 # 3 splits, first split has 2 windows 151 self.assertEqual([2, 1, 1], _window_pane_counts(window_manager)) 152 153 # Move the first window in the first split left 154 target_list_and_pane(window_manager, 0, 0) 155 window_manager.move_pane_left() 156 # 4 splits, each with their own window 157 self.assertEqual([1, 1, 1, 1], _window_pane_counts(window_manager)) 158 159 # Move the first window to the right 160 target_list_and_pane(window_manager, 0, 0) 161 window_manager.move_pane_right() 162 # 3 splits, first split has 2 windows 163 self.assertEqual([2, 1, 1], _window_pane_counts(window_manager)) 164 165 target_pane = window_manager.window_lists[2].active_panes[0] 166 167 ( 168 result_window_list, 169 result_pane_index, 170 ) = window_manager.find_window_list_and_pane_index(target_pane) 171 self.assertEqual( 172 (result_window_list, result_pane_index), 173 (window_manager.window_lists[2], 0), 174 ) 175 window_manager.remove_pane(target_pane) 176 self.assertEqual([2, 1], _window_pane_counts(window_manager)) 177 178 def test_window_list_moving_and_resizing(self) -> None: 179 """Test window split movement resizing.""" 180 with create_app_session(output=FakeOutput()): 181 console_app = _create_console_app(logger_count=3) 182 183 window_manager = console_app.window_manager 184 185 target_list_and_pane(window_manager, 0, 0) 186 # Should have one window list split of size 50. 187 self.assertEqual( 188 _window_list_widths(window_manager), 189 [_WINDOW_MANAGER_WIDTH], 190 ) 191 192 # Move one pane to the right, creating a new window_list split. 193 window_manager.move_pane_right() 194 195 self.assertEqual( 196 _window_list_widths(window_manager), 197 [ 198 int(_WINDOW_MANAGER_WIDTH / 2), 199 int(_WINDOW_MANAGER_WIDTH / 2), 200 ], 201 ) 202 203 # Move another pane to the right twice, creating a third 204 # window_list split. 205 target_list_and_pane(window_manager, 0, 0) 206 window_manager.move_pane_right() 207 208 # Above window pane is at a new location 209 target_list_and_pane(window_manager, 1, 0) 210 window_manager.move_pane_right() 211 212 # Should have 3 splits now 213 self.assertEqual( 214 _window_list_widths(window_manager), 215 [ 216 int(_WINDOW_MANAGER_WIDTH / 3), 217 int(_WINDOW_MANAGER_WIDTH / 3), 218 int(_WINDOW_MANAGER_WIDTH / 3), 219 ], 220 ) 221 222 # Total of 4 active panes 223 self.assertEqual(len(list(window_manager.active_panes())), 4) 224 225 # Target the middle split 226 target_list_and_pane(window_manager, 1, 0) 227 # Shrink the middle split twice 228 window_manager.shrink_split() 229 window_manager.shrink_split() 230 self.assertEqual( 231 _window_list_widths(window_manager), 232 [ 233 int(_WINDOW_MANAGER_WIDTH / 3), 234 int(_WINDOW_MANAGER_WIDTH / 3) - (2 * _WINDOW_SPLIT_ADJUST), 235 int(_WINDOW_MANAGER_WIDTH / 3) + (2 * _WINDOW_SPLIT_ADJUST), 236 ], 237 ) 238 239 # Target the first split 240 target_list_and_pane(window_manager, 0, 0) 241 window_manager.reset_split_sizes() 242 # Shrink the first split twice 243 window_manager.shrink_split() 244 self.assertEqual( 245 _window_list_widths(window_manager), 246 [ 247 int(_WINDOW_MANAGER_WIDTH / 3) - (1 * _WINDOW_SPLIT_ADJUST), 248 int(_WINDOW_MANAGER_WIDTH / 3) + (1 * _WINDOW_SPLIT_ADJUST), 249 int(_WINDOW_MANAGER_WIDTH / 3), 250 ], 251 ) 252 253 # Target the third (last) split 254 target_list_and_pane(window_manager, 2, 0) 255 window_manager.reset_split_sizes() 256 # Shrink the third split once 257 window_manager.shrink_split() 258 self.assertEqual( 259 _window_list_widths(window_manager), 260 [ 261 int(_WINDOW_MANAGER_WIDTH / 3), 262 int(_WINDOW_MANAGER_WIDTH / 3) + (1 * _WINDOW_SPLIT_ADJUST), 263 int(_WINDOW_MANAGER_WIDTH / 3) - (1 * _WINDOW_SPLIT_ADJUST), 264 ], 265 ) 266 267 window_manager.reset_split_sizes() 268 # Enlarge the third split a few times. 269 window_manager.enlarge_split() 270 window_manager.enlarge_split() 271 window_manager.enlarge_split() 272 self.assertEqual( 273 _window_list_widths(window_manager), 274 [ 275 int(_WINDOW_MANAGER_WIDTH / 3), 276 int(_WINDOW_MANAGER_WIDTH / 3) - (3 * _WINDOW_SPLIT_ADJUST), 277 int(_WINDOW_MANAGER_WIDTH / 3) + (3 * _WINDOW_SPLIT_ADJUST), 278 ], 279 ) 280 281 # Target the middle split 282 target_list_and_pane(window_manager, 1, 0) 283 # Move the middle window pane left 284 window_manager.move_pane_left() 285 # This is called on the next render pass 286 window_manager.rebalance_window_list_sizes() 287 # Middle split should be removed 288 self.assertEqual( 289 _window_list_widths(window_manager), 290 [ 291 int(_WINDOW_MANAGER_WIDTH / 2) - (3 * _WINDOW_SPLIT_ADJUST), 292 # This split is removed 293 int(_WINDOW_MANAGER_WIDTH / 2) + (2 * _WINDOW_SPLIT_ADJUST), 294 ], 295 ) 296 297 # Revert sizes to default 298 window_manager.reset_split_sizes() 299 self.assertEqual( 300 _window_list_widths(window_manager), 301 [ 302 int(_WINDOW_MANAGER_WIDTH / 2), 303 int(_WINDOW_MANAGER_WIDTH / 2), 304 ], 305 ) 306 307 def test_get_pane_titles(self) -> None: 308 """Test window resizing.""" 309 with create_app_session(output=FakeOutput()): 310 console_app = _create_console_app(logger_count=3) 311 312 window_manager = console_app.window_manager 313 list_pane_titles = [ 314 # Remove mouse click handler partials in tup[2] 315 [(tup[0], tup[1]) for tup in window_list.get_pane_titles()] 316 for window_list in window_manager.window_lists 317 ] 318 self.assertEqual( 319 list_pane_titles[0], 320 [ 321 ('', ' '), 322 ('class:window-tab-inactive', ' Log2 test_log2 '), 323 ('', ' '), 324 ('class:window-tab-inactive', ' Log1 test_log1 '), 325 ('', ' '), 326 ('class:window-tab-inactive', ' Log0 test_log0 '), 327 ('', ' '), 328 ('class:window-tab-inactive', ' Python Repl '), 329 ('', ' '), 330 ], 331 ) 332 333 def test_window_pane_movement_resizing(self) -> None: 334 """Test window resizing.""" 335 with create_app_session(output=FakeOutput()): 336 console_app = _create_console_app(logger_count=3) 337 338 window_manager = console_app.window_manager 339 340 # 4 panes, 3 for the loggers and 1 for the repl. 341 self.assertEqual( 342 len(window_manager.first_window_list().active_panes), 4 343 ) 344 345 def target_window_pane(index: int): 346 # Bypass prompt_toolkit has_focus() 347 window_manager._get_active_window_list_and_pane = ( 348 MagicMock( # type: ignore 349 return_value=( 350 window_manager.window_lists[0], 351 window_manager.window_lists[0].active_panes[index], 352 ) 353 ) 354 ) 355 window_list = console_app.window_manager.first_window_list() 356 window_list.get_current_active_pane = MagicMock( # type: ignore 357 return_value=window_list.active_panes[index] 358 ) 359 360 # Target the first window pane 361 target_window_pane(0) 362 363 # Shrink the first pane 364 window_manager.shrink_pane() 365 self.assertEqual( 366 _window_pane_heights(window_manager), 367 [ 368 _DEFAULT_WINDOW_HEIGHT - (1 * _WINDOW_HEIGHT_ADJUST), 369 _DEFAULT_WINDOW_HEIGHT + (1 * _WINDOW_HEIGHT_ADJUST), 370 _DEFAULT_WINDOW_HEIGHT, 371 _DEFAULT_WINDOW_HEIGHT, 372 ], 373 ) 374 375 # Reset pane sizes 376 window_manager.window_lists[0].current_window_list_height = ( 377 4 * _DEFAULT_WINDOW_HEIGHT 378 ) 379 window_manager.reset_pane_sizes() 380 self.assertEqual( 381 _window_pane_heights(window_manager), 382 [ 383 _DEFAULT_WINDOW_HEIGHT, 384 _DEFAULT_WINDOW_HEIGHT, 385 _DEFAULT_WINDOW_HEIGHT, 386 _DEFAULT_WINDOW_HEIGHT, 387 ], 388 ) 389 390 # Shrink last pane 391 target_window_pane(3) 392 393 window_manager.shrink_pane() 394 self.assertEqual( 395 _window_pane_heights(window_manager), 396 [ 397 _DEFAULT_WINDOW_HEIGHT, 398 _DEFAULT_WINDOW_HEIGHT, 399 _DEFAULT_WINDOW_HEIGHT + (1 * _WINDOW_HEIGHT_ADJUST), 400 _DEFAULT_WINDOW_HEIGHT - (1 * _WINDOW_HEIGHT_ADJUST), 401 ], 402 ) 403 404 # Enlarge second pane 405 target_window_pane(1) 406 window_manager.reset_pane_sizes() 407 408 window_manager.enlarge_pane() 409 window_manager.enlarge_pane() 410 self.assertEqual( 411 _window_pane_heights(window_manager), 412 [ 413 _DEFAULT_WINDOW_HEIGHT, 414 _DEFAULT_WINDOW_HEIGHT + (2 * _WINDOW_HEIGHT_ADJUST), 415 _DEFAULT_WINDOW_HEIGHT - (2 * _WINDOW_HEIGHT_ADJUST), 416 _DEFAULT_WINDOW_HEIGHT, 417 ], 418 ) 419 420 # Check window pane ordering 421 self.assertEqual( 422 window_pane_titles(window_manager), 423 [ 424 [ 425 'Log2 - test_log2', 426 'Log1 - test_log1', 427 'Log0 - test_log0', 428 'Python Repl - ', 429 ], 430 ], 431 ) 432 433 target_window_pane(0) 434 window_manager.move_pane_down() 435 self.assertEqual( 436 window_pane_titles(window_manager), 437 [ 438 [ 439 'Log1 - test_log1', 440 'Log2 - test_log2', 441 'Log0 - test_log0', 442 'Python Repl - ', 443 ], 444 ], 445 ) 446 target_window_pane(2) 447 window_manager.move_pane_up() 448 target_window_pane(1) 449 window_manager.move_pane_up() 450 self.assertEqual( 451 window_pane_titles(window_manager), 452 [ 453 [ 454 'Log0 - test_log0', 455 'Log1 - test_log1', 456 'Log2 - test_log2', 457 'Python Repl - ', 458 ], 459 ], 460 ) 461 target_window_pane(0) 462 window_manager.move_pane_up() 463 self.assertEqual( 464 window_pane_titles(window_manager), 465 [ 466 [ 467 'Log0 - test_log0', 468 'Log1 - test_log1', 469 'Log2 - test_log2', 470 'Python Repl - ', 471 ], 472 ], 473 ) 474 475 def test_focus_next_and_previous_pane(self) -> None: 476 """Test switching focus to next and previous window panes.""" 477 with create_app_session(output=FakeOutput()): 478 console_app = _create_console_app(logger_count=4) 479 480 window_manager = console_app.window_manager 481 window_manager.window_lists[0].set_display_mode(DisplayMode.STACK) 482 self.assertEqual( 483 window_pane_titles(window_manager), 484 [ 485 [ 486 'Log3 - test_log3', 487 'Log2 - test_log2', 488 'Log1 - test_log1', 489 'Log0 - test_log0', 490 'Python Repl - ', 491 ], 492 ], 493 ) 494 495 # Scenario: Move between panes with a single stacked window list. 496 497 # Set the first pane in focus. 498 target_list_and_pane(window_manager, 0, 0) 499 # Switch focus to the next pane 500 window_manager.focus_next_pane() 501 # Pane index 1 should now be focused. 502 console_app.focus_on_container.assert_called_once_with( 503 window_manager.window_lists[0].active_panes[1] 504 ) 505 console_app.focus_on_container.reset_mock() 506 507 # Set the first pane in focus. 508 target_list_and_pane(window_manager, 0, 0) 509 # Switch focus to the previous pane 510 window_manager.focus_previous_pane() 511 # Previous pane should wrap around to the last pane in the first 512 # window_list. 513 console_app.focus_on_container.assert_called_once_with( 514 window_manager.window_lists[0].active_panes[-1] 515 ) 516 console_app.focus_on_container.reset_mock() 517 518 # Set the last pane in focus. 519 target_list_and_pane(window_manager, 0, 4) 520 # Switch focus to the next pane 521 window_manager.focus_next_pane() 522 # Next pane should wrap around to the first pane in the first 523 # window_list. 524 console_app.focus_on_container.assert_called_once_with( 525 window_manager.window_lists[0].active_panes[0] 526 ) 527 console_app.focus_on_container.reset_mock() 528 529 # Scenario: Move between panes with a single tabbed window list. 530 531 # Switch to Tabbed view mode 532 window_manager.window_lists[0].set_display_mode(DisplayMode.TABBED) 533 # The set_display_mode call above will call focus_on_container once. 534 console_app.focus_on_container.reset_mock() 535 536 # Setup the switch_to_tab mock 537 window_manager.window_lists[0].switch_to_tab = MagicMock( 538 wraps=window_manager.window_lists[0].switch_to_tab 539 ) 540 541 # Set the first pane/tab in focus. 542 target_list_and_pane(window_manager, 0, 0) 543 # Switch focus to the next pane/tab 544 window_manager.focus_next_pane() 545 # Check switch_to_tab is called 546 window_manager.window_lists[ 547 0 548 ].switch_to_tab.assert_called_once_with(1) 549 # And that focus_on_container is called only once 550 console_app.focus_on_container.assert_called_once_with( 551 window_manager.window_lists[0].active_panes[1] 552 ) 553 console_app.focus_on_container.reset_mock() 554 window_manager.window_lists[0].switch_to_tab.reset_mock() 555 556 # Set the last pane/tab in focus. 557 target_list_and_pane(window_manager, 0, 4) 558 # Switch focus to the next pane/tab 559 window_manager.focus_next_pane() 560 # Check switch_to_tab is called 561 window_manager.window_lists[ 562 0 563 ].switch_to_tab.assert_called_once_with(0) 564 # And that focus_on_container is called only once 565 console_app.focus_on_container.assert_called_once_with( 566 window_manager.window_lists[0].active_panes[0] 567 ) 568 console_app.focus_on_container.reset_mock() 569 window_manager.window_lists[0].switch_to_tab.reset_mock() 570 571 # Set the first pane/tab in focus. 572 target_list_and_pane(window_manager, 0, 0) 573 # Switch focus to the prev pane/tab 574 window_manager.focus_previous_pane() 575 # Check switch_to_tab is called 576 window_manager.window_lists[ 577 0 578 ].switch_to_tab.assert_called_once_with(4) 579 # And that focus_on_container is called only once 580 console_app.focus_on_container.assert_called_once_with( 581 window_manager.window_lists[0].active_panes[4] 582 ) 583 console_app.focus_on_container.reset_mock() 584 window_manager.window_lists[0].switch_to_tab.reset_mock() 585 586 # Scenario: Move between multiple window lists with mixed stacked 587 # and tabbed view modes. 588 589 # Setup: Move two panes to the right into their own stacked 590 # window_list. 591 target_list_and_pane(window_manager, 0, 4) 592 window_manager.move_pane_right() 593 target_list_and_pane(window_manager, 0, 3) 594 window_manager.move_pane_right() 595 self.assertEqual( 596 window_pane_titles(window_manager), 597 [ 598 [ 599 'Log3 - test_log3', 600 'Log2 - test_log2', 601 'Log1 - test_log1', 602 ], 603 [ 604 'Log0 - test_log0', 605 'Python Repl - ', 606 ], 607 ], 608 ) 609 610 # Setup the switch_to_tab mock on the second window_list 611 window_manager.window_lists[1].switch_to_tab = MagicMock( 612 wraps=window_manager.window_lists[1].switch_to_tab 613 ) 614 615 # Set Log1 in focus 616 target_list_and_pane(window_manager, 0, 2) 617 window_manager.focus_next_pane() 618 # Log0 should now have focus 619 console_app.focus_on_container.assert_called_once_with( 620 window_manager.window_lists[1].active_panes[0] 621 ) 622 console_app.focus_on_container.reset_mock() 623 624 # Set Log0 in focus 625 target_list_and_pane(window_manager, 1, 0) 626 window_manager.focus_previous_pane() 627 # Log1 should now have focus 628 console_app.focus_on_container.assert_called_once_with( 629 window_manager.window_lists[0].active_panes[2] 630 ) 631 # The first window list is in tabbed mode so switch_to_tab should be 632 # called once. 633 window_manager.window_lists[ 634 0 635 ].switch_to_tab.assert_called_once_with(2) 636 # Reset 637 window_manager.window_lists[0].switch_to_tab.reset_mock() 638 console_app.focus_on_container.reset_mock() 639 640 # Set Python Repl in focus 641 target_list_and_pane(window_manager, 1, 1) 642 window_manager.focus_next_pane() 643 # Log3 should now have focus 644 console_app.focus_on_container.assert_called_once_with( 645 window_manager.window_lists[0].active_panes[0] 646 ) 647 window_manager.window_lists[ 648 0 649 ].switch_to_tab.assert_called_once_with(0) 650 # Reset 651 window_manager.window_lists[0].switch_to_tab.reset_mock() 652 console_app.focus_on_container.reset_mock() 653 654 # Set Log3 in focus 655 target_list_and_pane(window_manager, 0, 0) 656 window_manager.focus_next_pane() 657 # Log2 should now have focus 658 console_app.focus_on_container.assert_called_once_with( 659 window_manager.window_lists[0].active_panes[1] 660 ) 661 window_manager.window_lists[ 662 0 663 ].switch_to_tab.assert_called_once_with(1) 664 # Reset 665 window_manager.window_lists[0].switch_to_tab.reset_mock() 666 console_app.focus_on_container.reset_mock() 667 668 # Set Python Repl in focus 669 target_list_and_pane(window_manager, 1, 1) 670 window_manager.focus_previous_pane() 671 # Log0 should now have focus 672 console_app.focus_on_container.assert_called_once_with( 673 window_manager.window_lists[1].active_panes[0] 674 ) 675 # The second window list is in stacked mode so switch_to_tab should 676 # not be called. 677 window_manager.window_lists[1].switch_to_tab.assert_not_called() 678 # Reset 679 window_manager.window_lists[1].switch_to_tab.reset_mock() 680 console_app.focus_on_container.reset_mock() 681 682 def test_resize_vertical_splits(self) -> None: 683 """Test resizing window splits.""" 684 with create_app_session(output=FakeOutput()): 685 console_app = _create_console_app(logger_count=4) 686 window_manager = console_app.window_manager 687 688 # Required before moving windows 689 window_manager.update_window_manager_size( 690 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 691 ) 692 window_manager.create_root_container() 693 694 # Vertical split by default 695 self.assertTrue(window_manager.vertical_window_list_spliting()) 696 697 # Move windows to create 3 splits 698 target_list_and_pane(window_manager, 0, 0) 699 window_manager.move_pane_right() 700 target_list_and_pane(window_manager, 0, 0) 701 window_manager.move_pane_right() 702 target_list_and_pane(window_manager, 1, 1) 703 window_manager.move_pane_right() 704 705 # Check windows are where expected 706 self.assertEqual( 707 window_pane_titles(window_manager), 708 [ 709 [ 710 'Log1 - test_log1', 711 'Log0 - test_log0', 712 'Python Repl - ', 713 ], 714 [ 715 'Log2 - test_log2', 716 ], 717 [ 718 'Log3 - test_log3', 719 ], 720 ], 721 ) 722 723 # Check initial split widths 724 widths = [ 725 int(_WINDOW_MANAGER_WIDTH / 3), 726 int(_WINDOW_MANAGER_WIDTH / 3), 727 int(_WINDOW_MANAGER_WIDTH / 3), 728 ] 729 self.assertEqual(_window_list_widths(window_manager), widths) 730 731 # Decrease size of first split 732 window_manager.adjust_split_size(window_manager.window_lists[0], -4) 733 widths = [ 734 widths[0] - (4 * _WINDOW_SPLIT_ADJUST), 735 widths[1] + (4 * _WINDOW_SPLIT_ADJUST), 736 widths[2], 737 ] 738 self.assertEqual(_window_list_widths(window_manager), widths) 739 740 # Increase size of last split 741 widths = [ 742 widths[0], 743 widths[1] - (4 * _WINDOW_SPLIT_ADJUST), 744 widths[2] + (4 * _WINDOW_SPLIT_ADJUST), 745 ] 746 window_manager.adjust_split_size(window_manager.window_lists[2], 4) 747 self.assertEqual(_window_list_widths(window_manager), widths) 748 749 # Check heights are all the same 750 window_manager.rebalance_window_list_sizes() 751 heights = [ 752 int(_WINDOW_MANAGER_HEIGHT), 753 int(_WINDOW_MANAGER_HEIGHT), 754 int(_WINDOW_MANAGER_HEIGHT), 755 ] 756 self.assertEqual(_window_list_heights(window_manager), heights) 757 758 def test_resize_horizontal_splits(self) -> None: 759 """Test resizing window splits.""" 760 with create_app_session(output=FakeOutput()): 761 console_app = _create_console_app(logger_count=4) 762 window_manager = console_app.window_manager 763 764 # We want horizontal window splits 765 window_manager.vertical_window_list_spliting = MagicMock( 766 return_value=False 767 ) 768 self.assertFalse(window_manager.vertical_window_list_spliting()) 769 770 # Required before moving windows 771 window_manager.update_window_manager_size( 772 _WINDOW_MANAGER_WIDTH, _WINDOW_MANAGER_HEIGHT 773 ) 774 window_manager.create_root_container() 775 776 # Move windows to create 3 splits 777 target_list_and_pane(window_manager, 0, 0) 778 window_manager.move_pane_right() 779 target_list_and_pane(window_manager, 0, 0) 780 window_manager.move_pane_right() 781 target_list_and_pane(window_manager, 1, 1) 782 window_manager.move_pane_right() 783 784 # Check windows are where expected 785 self.assertEqual( 786 window_pane_titles(window_manager), 787 [ 788 [ 789 'Log1 - test_log1', 790 'Log0 - test_log0', 791 'Python Repl - ', 792 ], 793 [ 794 'Log2 - test_log2', 795 ], 796 [ 797 'Log3 - test_log3', 798 ], 799 ], 800 ) 801 802 # Check initial split widths 803 heights = [ 804 int(_WINDOW_MANAGER_HEIGHT / 3), 805 int(_WINDOW_MANAGER_HEIGHT / 3), 806 int(_WINDOW_MANAGER_HEIGHT / 3), 807 ] 808 self.assertEqual(_window_list_heights(window_manager), heights) 809 810 # Decrease size of first split 811 window_manager.adjust_split_size(window_manager.window_lists[0], -4) 812 heights = [ 813 heights[0] - (4 * _WINDOW_SPLIT_ADJUST), 814 heights[1] + (4 * _WINDOW_SPLIT_ADJUST), 815 heights[2], 816 ] 817 self.assertEqual(_window_list_heights(window_manager), heights) 818 819 # Increase size of last split 820 heights = [ 821 heights[0], 822 heights[1] - (4 * _WINDOW_SPLIT_ADJUST), 823 heights[2] + (4 * _WINDOW_SPLIT_ADJUST), 824 ] 825 window_manager.adjust_split_size(window_manager.window_lists[2], 4) 826 self.assertEqual(_window_list_heights(window_manager), heights) 827 828 # Check widths are all the same 829 window_manager.rebalance_window_list_sizes() 830 widths = [ 831 int(_WINDOW_MANAGER_WIDTH), 832 int(_WINDOW_MANAGER_WIDTH), 833 int(_WINDOW_MANAGER_WIDTH), 834 ] 835 self.assertEqual(_window_list_widths(window_manager), widths) 836 837 838if __name__ == '__main__': 839 unittest.main() 840