New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix cursorManager selection bugs #7131
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4d91259
Add tests for other selection behavior in cursorManager.
jcsteh 21c18e0
cursorManager: Fix select all when the caret is in the middle or at t…
jcsteh 11b3136
cursorManager: Fix selecting forward, then selecting backward without…
jcsteh 28fe5b8
cursorManager: Fix unselecting to the beginning of the line. (#5746)
jcsteh 8b662c7
Add additional tests.
jcsteh 0eda1cb
Rename tests for consistency.
jcsteh File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,43 +29,157 @@ def test_prevChar(self): | |
|
||
class TestSelection(unittest.TestCase): | ||
|
||
def test_selectNextChar(self): | ||
def test_selForward(self): | ||
cm = CursorManager(text="abc") # Caret at "a" | ||
cm.script_selectCharacter_forward(None) | ||
self.assertEqual(cm.selectionOffsets, (0, 1)) # "a" selected | ||
|
||
def test_selectPrevChar(self): | ||
def test_selBackward(self): | ||
"""Same as test_selForward, but with reversed direction. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_back(None) | ||
self.assertEqual(cm.selectionOffsets, (0, 1)) # "a" selected | ||
|
||
def test_unselectPrevChar(self): | ||
"""Depends on behavior tested by test_selectNextChar. | ||
def test_selForwardThenUnsel(self): | ||
"""Depends on behavior tested by test_selForward. | ||
""" | ||
cm = CursorManager(text="abc") # Caret at "a" | ||
cm.script_selectCharacter_forward(None) # "a" selected | ||
cm.script_selectCharacter_back(None) # "a" unselected | ||
self.assertEqual(cm.selectionOffsets, (0, 0)) # Caret at "a", no selection | ||
|
||
def test_selBackwardThenUnsel(self): | ||
"""Depends on behavior tested by test_selBackward. | ||
Same as test_selForwardThenUnsel, but with reversed directions. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_back(None) # "a" selected | ||
cm.script_selectCharacter_forward(None) # "a" unselected | ||
self.assertEqual(cm.selectionOffsets, (1, 1)) # Caret at "b", no selection | ||
|
||
def test_selForwardTwice(self): | ||
"""Depends on behavior tested in test_selForward. | ||
""" | ||
cm = CursorManager(text="abc") # Caret at "a" | ||
cm.script_selectCharacter_forward(None) # "a" selected | ||
cm.script_selectCharacter_forward(None) # "b" selected | ||
self.assertEqual(cm.selectionOffsets, (0, 2)) # "ab" selected | ||
|
||
def test_selBackwardTwice(self): | ||
"""Depends on behavior tested in test_selBackward. | ||
Same as test_selForwardTwice, but with reversed directions. | ||
""" | ||
cm = CursorManager(text="abc", selection=(2, 2)) # Caret at "c" | ||
cm.script_selectCharacter_back(None) # "b" selected | ||
cm.script_selectCharacter_back(None) # "a" selected | ||
self.assertEqual(cm.selectionOffsets, (0, 2)) # "ab" selected | ||
|
||
def test_selForwardThenUnselThenSelBackward(self): | ||
"""Test selecting forward, then unselecting and selecting backward. | ||
Depends on behavior tested by test_unselectPrevChar. | ||
Depends on behavior tested by test_selForwardThenUnsel. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_forward(None) # "b" selected | ||
cm.script_selectCharacter_back(None) # "b" unselected, caret at "b" | ||
cm.script_selectCharacter_back(None) | ||
self.assertEqual(cm.selectionOffsets, (0, 1)) # "a" selected | ||
|
||
def test_selectForwardThenSelBackward(self): | ||
def test_selBackwardThenUnselThenSelForward(self): | ||
"""Test selecting backward, then unselecting and selecting forward. | ||
Depends on behavior tested by test_selBackwardThenUnsel. | ||
Same as test_selForwardThenUnselThenSelBackward, but with reversed directions. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_back(None) # "a" selected | ||
cm.script_selectCharacter_forward(None) # "a" unselected, caret at "b" | ||
cm.script_selectCharacter_forward(None) | ||
self.assertEqual(cm.selectionOffsets, (1, 2)) # "b" selected | ||
|
||
def test_selForwardThenSelBackward(self): | ||
"""Test selecting forward, then selecting backward without unselecting. | ||
Depends on behavior tested by test_selectNextChar. | ||
Depends on behavior tested by test_selForward. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_forward(None) # "b" selected | ||
cm.script_selectWord_back(None) # "b" unselected, "a" selected | ||
self.assertEqual(cm.selectionOffsets, (0, 1)) # "a" selected | ||
|
||
def test_selBackwardThenSelForward(self): | ||
"""Test selecting backward, then selecting forward without unselecting. | ||
Same as test_selForwardThenSelBackward, but with reversed directions. | ||
""" | ||
cm = CursorManager(text="abc", selection=(2, 2)) # Caret at "c" | ||
cm.script_selectCharacter_back(None) # "b" selected | ||
cm.script_selectWord_forward(None) # "b" unselected, "c" selected | ||
self.assertEqual(cm.selectionOffsets, (2, 3)) # "c" selected | ||
|
||
def test_selForwardThenSelBackwardThenUnsel(self): | ||
"""Test selecting forward, then selecting backward without unselecting, then unselecting forward. | ||
Depends on behavior tested by test_selForwardThenSelBackward. | ||
""" | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectCharacter_forward(None) # "b" selected | ||
cm.script_selectWord_back(None) # "b" unselected, "a" selected | ||
cm.script_selectCharacter_forward(None) # "a" unselected | ||
self.assertEqual(cm.selectionOffsets, (1, 1)) # Caret at "b", no selection | ||
|
||
def test_selBackwardThenSelForwardThenUnsel(self): | ||
"""Test selecting backward, then selecting forward without unselecting, then unselecting backward. | ||
Same as test_selForwardThenSelBackwardThenUnsel, but with reversed directions. | ||
Depends on behavior tested by test_selBackwardThenSelForward. | ||
""" | ||
cm = CursorManager(text="abc", selection=(2, 2)) # Caret at "c" | ||
cm.script_selectCharacter_back(None) # "b" selected | ||
cm.script_selectWord_forward(None) # "b" unselected, "c" selected | ||
cm.script_selectCharacter_back(None) # "c" unselected | ||
self.assertEqual(cm.selectionOffsets, (2, 2)) # Caret at "c", no selection | ||
|
||
def test_selToBottom(self): | ||
cm = CursorManager(text="abc", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectToBottomOfDocument(None) | ||
self.assertEqual(cm.selectionOffsets, (1, 3)) # "bc" selected | ||
|
||
def test_selToTop(self): | ||
cm = CursorManager(text="abc", selection=(2, 2)) # Caret at "c" | ||
cm.script_selectToTopOfDocument(None) | ||
self.assertEqual(cm.selectionOffsets, (0, 2)) # "ab" selected | ||
|
||
def test_selToEndOfLine(self): | ||
cm = CursorManager(text="ab\ncd", selection=(1, 1)) # Caret at "b" | ||
cm.script_selectToEndOfLine(None) | ||
self.assertEqual(cm.selectionOffsets, (1, 3)) # "b\n" selected | ||
|
||
def test_selToBeginningOfLine(self): | ||
cm = CursorManager(text="ab\ncd", selection=(4, 4)) # Caret at "d" | ||
cm.script_selectToBeginningOfLine(None) | ||
self.assertEqual(cm.selectionOffsets, (3, 4)) # "c" selected | ||
|
||
def test_selToBeginningOfLineAtBeginning(self): | ||
"""Test selecting to the beginning of the line when the caret is already at the beginning of the line. | ||
In this case, nothing should happen. | ||
""" | ||
cm = CursorManager(text="ab\ncd", selection=(3, 3)) # Caret at "c" | ||
cm.script_selectToBeginningOfLine(None) | ||
self.assertEqual(cm.selectionOffsets, (3, 3)) # No selection | ||
|
||
def test_selForwardThenSelToBeginningOfLine(self): | ||
"""Depends on behavior tested by test_selForward. | ||
""" | ||
cm = CursorManager(text="ab\ncd", selection=(3, 3)) # Caret at "c" | ||
cm.script_selectCharacter_forward(None) # "c" selected | ||
cm.script_selectToBeginningOfLine(None) # "c" unselected | ||
self.assertEqual(cm.selectionOffsets, (3, 3)) # Caret at "c", no selection | ||
|
||
def test_selToEndThenBeginningOfLine(self): | ||
"""Test for #5746. | ||
Depends on behavior tested in test_selToEndOfLine and test_selToBeginningOfLine. | ||
""" | ||
cm = CursorManager(text="ab") # Caret at "a" | ||
cm.script_selectToEndOfLine(None) | ||
cm.script_selectToBeginningOfLine(None) | ||
self.assertEqual(cm.selectionOffsets, (0, 0)) # Caret at "a", no selection | ||
|
||
class TestSelectAll(unittest.TestCase): | ||
"""Tests the select all command starting from different caret positions. | ||
""" | ||
|
@@ -79,3 +193,9 @@ def _selectAllTest(self, caret): | |
|
||
def test_selectAllFromStart(self): | ||
self._selectAllTest(0) # Caret at "a" | ||
|
||
def test_selectAllFromMiddle(self): | ||
self._selectAllTest(1) # Caret at "b" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What kind of error message do you get when this fails? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reverting the fix, we get this:
|
||
|
||
def test_selectAllFromEnd(self): | ||
self._selectAllTest(2) # Caret at "c" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no coverage for
selectBackwardThenUnselThenSelForward
but there is forselectForwardThenUnselThenSelBackward
?Can these tests be broken down? Is there state that can only be achieved by using the script methods? For instance, instead of testing as in
test_selectBackwardThenSelForwardThenUnsel
, instead have three tests (using a naming scheme of given_do_expect:test_caretAtC_SelectCharBack_bSelected
test_bSelected_selectWordForward_cSelected
test_cSelected_selectCharacterBack_NoSelection
Perhaps there is a reason we cant do this, like some state on CursorManager that we can not set through the constructor. But if we can I think this makes it easier to verify good test coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an oversight when I wrote the original batch of tests. I shall fix.
Yes; there is state here that is only set by the methods. I could set it manually for testing; it's just a boolean, though it's not something that should normally be tweaked by a caller. However, a big part of what we're testing here is that the state gets set correctly throughout these various movements. I'm concerned we might miss something if we initialise state directly, especially since it's already somewhat hard to grasp how one might get into various states.