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