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