This PR ensures we correctly restore the cursor column after resizing the statusline. To ensure we have space for the statusline, we have to emit a newline to move up everything on screen. The newline causes the cursor to move to the start of the next line, which needs to be undone. Normally, we would use escape codes to save & restore the cursor position, but that doesn't work here, as the cursor position may have (purposely) changed. Instead, we move the cursor up one line using an escape code, but we weren't restoring the column. Interestingly, Editline was able to recover from this issue through the LineInfo struct which contains the buffer and the cursor location, which allows us to compute the column. This PR addresses the bug by having Editline "refresh" the cursor position. Fixes #134064
122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
import lldb
|
|
import re
|
|
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test.lldbpexpect import PExpectTest
|
|
|
|
|
|
# PExpect uses many timeouts internally and doesn't play well
|
|
# under ASAN on a loaded machine..
|
|
@skipIfAsan
|
|
class TestStatusline(PExpectTest):
|
|
# Change this value to something smaller to make debugging this test less
|
|
# tedious.
|
|
TIMEOUT = 60
|
|
|
|
TERMINAL_HEIGHT = 10
|
|
TERMINAL_WIDTH = 60
|
|
|
|
def do_setup(self):
|
|
# Create a target and run to a breakpoint.
|
|
exe = self.getBuildArtifact("a.out")
|
|
self.expect(
|
|
"target create {}".format(exe), substrs=["Current executable set to"]
|
|
)
|
|
self.expect('breakpoint set -p "Break here"', substrs=["Breakpoint 1"])
|
|
self.expect("run", substrs=["stop reason"])
|
|
self.resize()
|
|
|
|
def resize(self, height=None, width=None):
|
|
height = self.TERMINAL_HEIGHT if not height else height
|
|
width = self.TERMINAL_WIDTH if not width else width
|
|
# Change the terminal dimensions. When we launch the tests, we reset
|
|
# all the settings, leaving the terminal dimensions unset.
|
|
self.child.setwinsize(height, width)
|
|
|
|
def test(self):
|
|
"""Basic test for the statusline."""
|
|
self.build()
|
|
self.launch(timeout=self.TIMEOUT)
|
|
self.do_setup()
|
|
|
|
# Enable the statusline and check for the control character and that we
|
|
# can see the target, the location and the stop reason.
|
|
self.expect('set set separator "| "')
|
|
self.expect(
|
|
"set set show-statusline true",
|
|
[
|
|
"\x1b[1;{}r".format(self.TERMINAL_HEIGHT - 1),
|
|
"a.out | main.c:2:11 | breakpoint 1.1 ",
|
|
],
|
|
)
|
|
|
|
# Change the terminal dimensions and make sure it's reflected immediately.
|
|
self.child.setwinsize(self.TERMINAL_HEIGHT, 25)
|
|
self.child.expect(re.escape("a.out | main.c:2:11 | bre"))
|
|
self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH)
|
|
|
|
# Change the separator.
|
|
self.expect('set set separator "S "', ["a.out S main.c:2:11"])
|
|
|
|
# Change the format.
|
|
self.expect(
|
|
'set set statusline-format "target = {${target.file.basename}} ${separator}"',
|
|
["target = a.out S"],
|
|
)
|
|
self.expect('set set separator "| "')
|
|
|
|
# Hide the statusline and check or the control character.
|
|
self.expect(
|
|
"set set show-statusline false", ["\x1b[1;{}r".format(self.TERMINAL_HEIGHT)]
|
|
)
|
|
|
|
def test_no_color(self):
|
|
"""Basic test for the statusline with colors disabled."""
|
|
self.build()
|
|
self.launch(use_colors=False, timeout=self.TIMEOUT)
|
|
self.do_setup()
|
|
|
|
# Enable the statusline and check for the "reverse video" control character.
|
|
self.expect(
|
|
"set set show-statusline true",
|
|
[
|
|
"\x1b[7m",
|
|
],
|
|
)
|
|
|
|
def test_deadlock(self):
|
|
"""Regression test for lock inversion between the statusline mutex and
|
|
the output mutex."""
|
|
self.build()
|
|
self.launch(
|
|
extra_args=["-o", "settings set use-color false"], timeout=self.TIMEOUT
|
|
)
|
|
self.child.expect("(lldb)")
|
|
self.resize()
|
|
|
|
exe = self.getBuildArtifact("a.out")
|
|
|
|
self.expect("file {}".format(exe), ["Current executable"])
|
|
self.expect("help", ["Debugger commands"])
|
|
|
|
def test_no_target(self):
|
|
"""Test that we print "no target" when launched without a target."""
|
|
self.launch(timeout=self.TIMEOUT)
|
|
self.resize()
|
|
|
|
self.expect("set set show-statusline true", ["no target"])
|
|
|
|
@skipIfEditlineSupportMissing
|
|
def test_resize(self):
|
|
"""Test that move the cursor when resizing."""
|
|
self.launch(timeout=self.TIMEOUT)
|
|
self.resize()
|
|
self.expect("set set show-statusline true", ["no target"])
|
|
self.resize(20, 60)
|
|
# Check for the newline followed by the escape code to move the cursor
|
|
# up one line.
|
|
self.child.expect(re.escape("\n\x1b[1A"))
|
|
# Check for the escape code to move the cursor back to column 8.
|
|
self.child.expect(re.escape("\x1b[8G"))
|