diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 035a98148..e7ffdca52 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -33,6 +33,7 @@ kje liga lje locl +lorem maxed mkmk mru @@ -59,6 +60,7 @@ TLDR tokenizes tonos tshe +uiatextrange UIs und unregister diff --git a/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp b/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp new file mode 100644 index 000000000..b8944f10c --- /dev/null +++ b/src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp @@ -0,0 +1,3171 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// DO NOT MODIFY THESE TESTS DIRECTLY +// These were generated by tools\TestTableWriter\GenerateTests.ps1 +// Read tools\TestTableWriter\README.md for more details +// Define a few helpful variables +constexpr til::rectangle bufferSize{ 0, 0, 80, 300 }; +constexpr short midX{ 40 }; +constexpr short midY{ 150 }; +constexpr short midPopulatedY{ 75 }; +constexpr til::point origin{ 0, 0 }; +constexpr til::point midTop{ midX, 0 }; +constexpr til::point midHistory{ midX, midPopulatedY }; +constexpr til::point midDocEnd{ midX, midY }; +constexpr til::point lastCharPos{ 79, midY }; +constexpr til::point docEnd{ 0, midY + 1 }; +constexpr til::point midEmptySpace{ midX, midY + midPopulatedY }; +constexpr til::point bufferEnd{ 79, 299 }; +constexpr til::point endExclusive{ 0, 300 }; + +constexpr til::point bufferEndLeft{ bufferSize.left(), bufferEnd.y() }; +constexpr auto bufferEndM1C{ point_offset_by_char(bufferEnd, bufferSize, -1) }; +constexpr auto bufferEndM1L{ point_offset_by_line(bufferEnd, bufferSize, -1) }; +constexpr auto bufferEndM4C{ point_offset_by_char(bufferEnd, bufferSize, -4) }; +constexpr auto bufferEndM4L{ point_offset_by_line(bufferEnd, bufferSize, -4) }; +constexpr auto bufferEndM5C{ point_offset_by_char(bufferEnd, bufferSize, -5) }; +constexpr auto bufferEndM5L{ point_offset_by_line(bufferEnd, bufferSize, -5) }; +constexpr til::point docEndLeft{ bufferSize.left(), docEnd.y() }; +constexpr auto docEndM1C{ point_offset_by_char(docEnd, bufferSize, -1) }; +constexpr auto docEndM4L{ point_offset_by_line(docEnd, bufferSize, -4) }; +constexpr auto docEndM5C{ point_offset_by_char(docEnd, bufferSize, -5) }; +constexpr auto docEndP1C{ point_offset_by_char(docEnd, bufferSize, 1) }; + +constexpr til::point lastCharPosLeft{ bufferSize.left(), lastCharPos.y() }; +constexpr auto lastCharPosM1C{ point_offset_by_char(lastCharPos, bufferSize, -1) }; +constexpr auto lastCharPosM1L{ point_offset_by_line(lastCharPos, bufferSize, -1) }; +constexpr auto lastCharPosM4C{ point_offset_by_char(lastCharPos, bufferSize, -4) }; +constexpr auto lastCharPosM4L{ point_offset_by_line(lastCharPos, bufferSize, -4) }; +constexpr auto lastCharPosM5C{ point_offset_by_char(lastCharPos, bufferSize, -5) }; +constexpr auto lastCharPosM5L{ point_offset_by_line(lastCharPos, bufferSize, -5) }; +constexpr auto lastCharPosP1C{ point_offset_by_char(lastCharPos, bufferSize, 1) }; + +constexpr til::point midDocEndLeft{ bufferSize.left(), midDocEnd.y() }; +constexpr auto midDocEndM1C{ point_offset_by_char(midDocEnd, bufferSize, -1) }; +constexpr auto midDocEndM1L{ point_offset_by_line(midDocEnd, bufferSize, -1) }; +constexpr auto midDocEndM4C{ point_offset_by_char(midDocEnd, bufferSize, -4) }; +constexpr auto midDocEndM4L{ point_offset_by_line(midDocEnd, bufferSize, -4) }; +constexpr auto midDocEndM5C{ point_offset_by_char(midDocEnd, bufferSize, -5) }; +constexpr auto midDocEndM5L{ point_offset_by_line(midDocEnd, bufferSize, -5) }; +constexpr auto midDocEndP1C{ point_offset_by_char(midDocEnd, bufferSize, 1) }; +constexpr auto midDocEndP2C{ point_offset_by_char(midDocEnd, bufferSize, 2) }; +constexpr auto midDocEndP5C{ point_offset_by_char(midDocEnd, bufferSize, 5) }; +constexpr auto midDocEndP6C{ point_offset_by_char(midDocEnd, bufferSize, 6) }; + +constexpr auto midEmptySpaceP1C{ point_offset_by_char(midEmptySpace, bufferSize, 1) }; +constexpr til::point midHistoryLeft{ bufferSize.left(), midHistory.y() }; +constexpr auto midHistoryM1C{ point_offset_by_char(midHistory, bufferSize, -1) }; +constexpr auto midHistoryM1L{ point_offset_by_line(midHistory, bufferSize, -1) }; +constexpr auto midHistoryM4C{ point_offset_by_char(midHistory, bufferSize, -4) }; +constexpr auto midHistoryM4L{ point_offset_by_line(midHistory, bufferSize, -4) }; +constexpr auto midHistoryM5C{ point_offset_by_char(midHistory, bufferSize, -5) }; +constexpr auto midHistoryM5L{ point_offset_by_line(midHistory, bufferSize, -5) }; +constexpr auto midHistoryP1C{ point_offset_by_char(midHistory, bufferSize, 1) }; +constexpr auto midHistoryP1L{ point_offset_by_line(midHistory, bufferSize, 1) }; +constexpr auto midHistoryP2C{ point_offset_by_char(midHistory, bufferSize, 2) }; +constexpr auto midHistoryP2L{ point_offset_by_line(midHistory, bufferSize, 2) }; +constexpr auto midHistoryP5C{ point_offset_by_char(midHistory, bufferSize, 5) }; +constexpr auto midHistoryP5L{ point_offset_by_line(midHistory, bufferSize, 5) }; +constexpr auto midHistoryP6C{ point_offset_by_char(midHistory, bufferSize, 6) }; +constexpr auto midHistoryP6L{ point_offset_by_line(midHistory, bufferSize, 6) }; +constexpr auto midTopM1C{ point_offset_by_char(midTop, bufferSize, -1) }; +constexpr auto midTopM4C{ point_offset_by_char(midTop, bufferSize, -4) }; +constexpr auto midTopM5C{ point_offset_by_char(midTop, bufferSize, -5) }; +constexpr auto midTopP1C{ point_offset_by_char(midTop, bufferSize, 1) }; +constexpr auto midTopP1L{ point_offset_by_line(midTop, bufferSize, 1) }; +constexpr auto midTopP2C{ point_offset_by_char(midTop, bufferSize, 2) }; +constexpr auto midTopP5C{ point_offset_by_char(midTop, bufferSize, 5) }; +constexpr auto midTopP5L{ point_offset_by_line(midTop, bufferSize, 5) }; +constexpr auto midTopP6C{ point_offset_by_char(midTop, bufferSize, 6) }; +constexpr auto originP1C{ point_offset_by_char(origin, bufferSize, 1) }; +constexpr auto originP1L{ point_offset_by_line(origin, bufferSize, 1) }; +constexpr auto originP2C{ point_offset_by_char(origin, bufferSize, 2) }; +constexpr auto originP2L{ point_offset_by_line(origin, bufferSize, 2) }; +constexpr auto originP5C{ point_offset_by_char(origin, bufferSize, 5) }; +constexpr auto originP5L{ point_offset_by_line(origin, bufferSize, 5) }; +constexpr auto originP6C{ point_offset_by_char(origin, bufferSize, 6) }; +constexpr auto originP6L{ point_offset_by_line(origin, bufferSize, 6) }; +struct GeneratedMovementTestInput +{ + TextUnit unit; + int moveAmount; + til::point start; + til::point end; +}; +struct GeneratedMovementTestExpected +{ + int moveAmount; + til::point start; + til::point end; +}; +struct GeneratedMovementTest +{ + std::wstring_view name; + GeneratedMovementTestInput input; + GeneratedMovementTestExpected expected; + bool skip; +}; + +static constexpr std::array s_movementTests{ + GeneratedMovementTest{ + L"Move degenerate range at position 1 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + origin, + origin }, + GeneratedMovementTestExpected{ + 1, + originP1C, + originP1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + origin, + origin }, + GeneratedMovementTestExpected{ + 5, + originP5C, + originP5C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 1, + originP1C, + originP2C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 5, + originP5C, + originP6C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -5, + midTopM5C, + midTopM5C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -1, + midTopM1C, + midTopM1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 0, + midTop, + midTop }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 1, + midTopP1C, + midTopP1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 5, + midTopP5C, + midTopP5C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + -5, + midTopM5C, + midTopM4C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + -1, + midTopM1C, + midTop }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + midTop, + midTopP1C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 1, + midTopP1C, + midTopP2C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 5, + midTopP5C, + midTopP6C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -5, + midHistoryM5C, + midHistoryM5C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -1, + midHistoryM1C, + midHistoryM1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 0, + midHistory, + midHistory }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 1, + midHistoryP1C, + midHistoryP1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 5, + midHistoryP5C, + midHistoryP5C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + -5, + midHistoryM5C, + midHistoryM4C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + -1, + midHistoryM1C, + midHistory }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + midHistory, + midHistoryP1C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 1, + midHistoryP1C, + midHistoryP2C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 5, + midHistoryP5C, + midHistoryP6C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + origin, + origin }, + GeneratedMovementTestExpected{ + 1, + originP1L, + originP1L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + origin, + origin }, + GeneratedMovementTestExpected{ + 5, + originP5L, + originP5L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 1, + originP1L, + originP2L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 5, + originP5L, + originP6L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 0, + midTop, + midTop }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 1, + midTopP1L, + midTopP1L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 5, + midTopP5L, + midTopP5L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + originP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 1, + originP1L, + originP2L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 5, + originP5L, + originP6L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -5, + midHistoryM4L, + midHistoryM4L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -1, + midHistoryLeft, + midHistoryLeft }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 0, + midHistory, + midHistory }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 1, + midHistoryP1L, + midHistoryP1L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 5, + midHistoryP5L, + midHistoryP5L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + -5, + midHistoryM5L, + midHistoryM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + -1, + midHistoryM1L, + midHistoryLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + midHistoryLeft, + midHistoryP1L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 1, + midHistoryP1L, + midHistoryP2L }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 5, + midHistoryP5L, + midHistoryP6L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + origin, + origin }, + GeneratedMovementTestExpected{ + 0, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + origin, + origin }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 1 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + origin, + origin }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 1 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + origin, + originP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 0, + midTop, + midTop }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 1, + bufferEnd, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 2 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midTop, + midTop }, + GeneratedMovementTestExpected{ + 1, + bufferEnd, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 2 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midTop, + midTopP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + bufferEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 0, + midHistory, + midHistory }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 3 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midHistory, + midHistory }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 3 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midHistory, + midHistoryP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -5, + bufferEndM5C, + bufferEndM5C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -1, + bufferEndM1C, + bufferEndM1C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + bufferEnd }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + -5, + bufferEndM5C, + bufferEndM4C }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + bufferEndM1C, + bufferEnd }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -5, + bufferEndM4L, + bufferEndM4L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -1, + bufferEndLeft, + bufferEndLeft }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + bufferEnd }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + -5, + bufferEndM5L, + bufferEndM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + bufferEndM1L, + bufferEndLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEndLeft, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEndLeft, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + bufferEndLeft, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 0, + bufferEnd, + bufferEnd }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 8 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + bufferEnd, + bufferEnd }, + GeneratedMovementTestExpected{ + 1, + endExclusive, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 8 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + bufferEnd, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + origin, + endExclusive }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -5, + bufferEndM4C, + bufferEndM4C }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + bufferEnd, + bufferEnd }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -5, + bufferEndM4L, + bufferEndM4L }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + bufferEndLeft, + bufferEndLeft }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 9 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + endExclusive, + endExclusive }, + GeneratedMovementTestExpected{ + 0, + endExclusive, + endExclusive }, + false }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -5, + midDocEndM5C, + midDocEndM5C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -1, + midDocEndM1C, + midDocEndM1C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 0, + midDocEnd, + midDocEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 1, + midDocEndP1C, + midDocEndP1C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 5, + midDocEndP5C, + midDocEndP5C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + -5, + midDocEndM5C, + midDocEndM4C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + -1, + midDocEndM1C, + midDocEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + midDocEnd, + midDocEndP1C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 1, + midDocEndP1C, + midDocEndP2C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 5, + midDocEndP5C, + midDocEndP6C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -5, + midDocEndM4L, + midDocEndM4L }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -1, + midDocEndLeft, + midDocEndLeft }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 0, + midDocEnd, + midDocEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + -5, + midDocEndM5L, + midDocEndM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + -1, + midDocEndM1L, + midDocEndLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + midDocEndLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + midDocEndLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + midDocEndLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 0, + midDocEnd, + midDocEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 4 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midDocEnd, + midDocEnd }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 4 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midDocEnd, + midDocEndP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -5, + lastCharPosM5C, + lastCharPosM5C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -1, + lastCharPosM1C, + lastCharPosM1C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + lastCharPos }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + -5, + lastCharPosM5C, + lastCharPosM4C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + -1, + lastCharPosM1C, + lastCharPos }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -5, + lastCharPosM4L, + lastCharPosM4L }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -1, + lastCharPosLeft, + lastCharPosLeft }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + lastCharPos }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + -5, + lastCharPosM5L, + lastCharPosM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + -1, + lastCharPosM1L, + lastCharPosLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPosLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPosLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + lastCharPosLeft, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 0, + lastCharPos, + lastCharPos }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 5 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + lastCharPos, + lastCharPos }, + GeneratedMovementTestExpected{ + 1, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 5 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + lastCharPos, + lastCharPosP1C }, + GeneratedMovementTestExpected{ + 0, + origin, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -5, + docEndM5C, + docEndM5C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -1, + docEndM1C, + docEndM1C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -5, + docEndM5C, + docEndM5C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -1, + docEndM1C, + docEndM1C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -5, + docEndM4L, + docEndM4L }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -1, + docEndLeft, + docEndLeft }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -5, + docEndM4L, + docEndM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -1, + docEndLeft, + docEndLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 6 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + docEnd, + docEnd }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 6 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + docEnd, + docEndP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -5, + docEndM5C, + docEndM5C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -1, + docEndM1C, + docEndM1C }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -5, + docEndM5C, + docEndM5C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + -1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -1, + docEndM1C, + docEndM1C }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 0 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 0, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 1 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 5 times by Character", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Character, + 5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -5, + docEndM4L, + docEndM4L }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -1, + docEndLeft, + docEndLeft }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -5, + docEndM4L, + docEndM4L }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + -1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -1, + docEndLeft, + docEndLeft }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 0 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 0, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 1 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 5 times by Line", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Line, + 5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move degenerate range at position 7 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midEmptySpace, + midEmptySpace }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 -1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + -1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + -1, + origin, + origin }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 0 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 0, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 1 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 1, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, + GeneratedMovementTest{ + L"Move non-degenerate range at position 7 5 times by Document", + GeneratedMovementTestInput{ + TextUnit::TextUnit_Document, + 5, + midEmptySpace, + midEmptySpaceP1C }, + GeneratedMovementTestExpected{ + 0, + docEnd, + docEnd }, + true }, +}; diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index 5457b9853..26da4fe88 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -11,6 +11,8 @@ #include "../../../buffer/out/textBuffer.hpp" #include "../types/UiaTracing.h" +#include + using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; @@ -19,13 +21,193 @@ using namespace Microsoft::WRL; using namespace Microsoft::Console::Interactivity::Win32; +static constexpr til::point point_offset_by_char(const til::point start, const til::rectangle bounds, ptrdiff_t amt) +{ + ptrdiff_t pos_x = start.x(); + ptrdiff_t pos_y = start.y(); + while (amt != 0) + { + if (amt > 0) + { + if (pos_x == bounds.left() && pos_y == bounds.bottom()) + { + // end exclusive --> can't move any more + break; + } + else if (pos_x == bounds.right() - 1) + { + // right boundary --> wrap + pos_x = bounds.left(); + ++pos_y; + } + else + { + // standard move + ++pos_x; + } + --amt; + } + else + { + if (pos_x == bounds.left() && pos_y == bounds.top()) + { + // origin --> can't move any more + break; + } + else if (pos_x == bounds.left()) + { + // left boundary --> wrap + pos_x = bounds.right() - 1; + --pos_y; + } + else + { + // standard move + --pos_x; + } + ++amt; + } + } + return { pos_x, pos_y }; +} + +static constexpr til::point point_offset_by_line(const til::point start, const til::rectangle bounds, ptrdiff_t amt) +{ + // X = left boundary for UIA + ptrdiff_t pos_x = bounds.left(); + ptrdiff_t pos_y = start.y(); + while (amt != 0) + { + if (amt > 0) + { + if (pos_y == bounds.bottom() + 1) + { + break; + } + else + { + ++pos_y; + } + --amt; + } + else + { + if (pos_y == bounds.top()) + { + break; + } + else + { + --pos_y; + } + ++amt; + } + } + return { pos_x, pos_y }; +} + +// IMPORTANT: reference this _after_ defining point_offset_by_XXX. We need it for some definitions +#include "GeneratedUiaTextRangeMovementTests.g.cpp" + +namespace +{ +#pragma region TAEF hookup for the test case array above + struct ArrayIndexTaefAdapterRow : public Microsoft::WRL::RuntimeClass, IDataRow> + { + HRESULT RuntimeClassInitialize(const size_t index) + { + _index = index; + return S_OK; + } + + STDMETHODIMP GetTestData(BSTR /*pszName*/, SAFEARRAY** ppData) override + { + const auto indexString{ wil::str_printf(L"%zu", _index) }; + auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) }; + LONG index{ 0 }; + auto indexBstr{ wil::make_bstr(indexString.c_str()) }; + (void)SafeArrayPutElement(safeArray, &index, indexBstr.release()); + *ppData = safeArray; + return S_OK; + } + + STDMETHODIMP GetMetadataNames(SAFEARRAY** ppMetadataNames) override + { + *ppMetadataNames = nullptr; + return S_FALSE; + } + + STDMETHODIMP GetMetadata(BSTR /*pszName*/, SAFEARRAY** ppData) override + { + *ppData = nullptr; + return S_FALSE; + } + + STDMETHODIMP GetName(BSTR* ppszRowName) override + { + *ppszRowName = wil::make_bstr(s_movementTests[_index].name.data()).release(); + return S_OK; + } + + private: + size_t _index; + }; + + struct ArrayIndexTaefAdapterSource : public Microsoft::WRL::RuntimeClass, IDataSource> + { + STDMETHODIMP Advance(IDataRow** ppDataRow) override + { + if (_index < s_movementTests.size()) + { + Microsoft::WRL::MakeAndInitialize(ppDataRow, _index++); + } + else + { + *ppDataRow = nullptr; + } + return S_OK; + } + + STDMETHODIMP Reset() override + { + _index = 0; + return S_OK; + } + + STDMETHODIMP GetTestDataNames(SAFEARRAY** names) override + { + auto safeArray{ SafeArrayCreateVector(VT_BSTR, 0, 1) }; + LONG index{ 0 }; + auto dataNameBstr{ wil::make_bstr(L"index") }; + (void)SafeArrayPutElement(safeArray, &index, dataNameBstr.release()); + *names = safeArray; + return S_OK; + } + + STDMETHODIMP GetTestDataType(BSTR /*name*/, BSTR* type) override + { + *type = nullptr; + return S_OK; + } + + private: + size_t _index{ 0 }; + }; +#pragma endregion +} + +extern "C" HRESULT __declspec(dllexport) __cdecl GeneratedMovementTestDataSource(IDataSource** ppDataSource, void*) +{ + auto source{ Microsoft::WRL::Make() }; + return source.CopyTo(ppDataSource); +} + // UiaTextRange takes an object that implements // IRawElementProviderSimple as a constructor argument. Making a real // one would involve setting up the window which we don't want to do // for unit tests so instead we'll use this one. We don't care about // it not doing anything for its implementation because it is not used // during the unit tests below. - class DummyElementProvider final : public ScreenInfoUiaProviderBase { public: @@ -197,9 +379,6 @@ class UiaTextRangeTests TEST_METHOD(DegenerateRangesDetected) { - const auto bufferSize = _pTextBuffer->GetSize(); - const auto origin = bufferSize.Origin(); - // make a degenerate range and verify that it reports degenerate Microsoft::WRL::ComPtr degenerate; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(°enerate, @@ -211,7 +390,7 @@ class UiaTextRangeTests VERIFY_ARE_EQUAL(degenerate->_start, degenerate->_end); // make a non-degenerate range and verify that it reports as such - const COORD end = { origin.X + 1, origin.Y }; + const auto end{ point_offset_by_char(origin, bufferSize, 1) }; Microsoft::WRL::ComPtr notDegenerate; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(¬Degenerate, _pUiaData, @@ -224,9 +403,6 @@ class UiaTextRangeTests TEST_METHOD(CompareRange) { - const auto bufferSize = _pTextBuffer->GetSize(); - const auto origin = bufferSize.Origin(); - Microsoft::WRL::ComPtr utr1; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr1, _pUiaData, @@ -244,7 +420,7 @@ class UiaTextRangeTests VERIFY_IS_TRUE(comparison); // utr2 redefined to have different end from utr1 - const COORD end = { origin.X + 2, origin.Y }; + const auto end{ point_offset_by_char(origin, bufferSize, 2) }; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr2, _pUiaData, &_dummyProvider, @@ -258,9 +434,6 @@ class UiaTextRangeTests TEST_METHOD(CompareEndpoints) { - const auto bufferSize = _pTextBuffer->GetSize(); - const auto origin = bufferSize.Origin(); - Microsoft::WRL::ComPtr utr1; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr1, _pUiaData, @@ -283,7 +456,7 @@ class UiaTextRangeTests VERIFY_IS_TRUE(comparison == 0); // utr2 redefined to have different end from utr1 - const COORD end = { origin.X + 2, origin.Y }; + const auto end{ point_offset_by_char(origin, bufferSize, 2) }; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr2, _pUiaData, &_dummyProvider, @@ -465,8 +638,6 @@ class UiaTextRangeTests start, end)); - const auto bufferSize = _pTextBuffer->GetSize(); - const auto origin = bufferSize.Origin(); Microsoft::WRL::ComPtr target; auto resetTargetUTR = [&]() { @@ -1137,9 +1308,7 @@ class UiaTextRangeTests // at the end exclusive, the UTR should refuse to move past // the end. - const auto bufferSize{ _pTextBuffer->GetSize() }; - const til::point endInclusive{ bufferSize.RightInclusive(), bufferSize.BottomInclusive() }; - const auto endExclusive{ bufferSize.EndExclusive() }; + const til::point endInclusive{ bufferEnd }; // Iterate over each TextUnit. If the we don't support // the given TextUnit, we're supposed to fallback @@ -1153,7 +1322,7 @@ class UiaTextRangeTests THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr, _pUiaData, &_dummyProvider, endInclusive, endExclusive)); THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast(unit))); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); } } @@ -1162,12 +1331,9 @@ class UiaTextRangeTests // GH#7663: When attempting to move from end exclusive, // the UTR should refuse to move past the end. - const auto bufferSize{ _pTextBuffer->GetSize() }; - const COORD origin{ bufferSize.Origin() }; - const COORD lastLineStart{ bufferSize.Left(), bufferSize.BottomInclusive() }; - const COORD secondToLastCharacterPos{ bufferSize.RightInclusive() - 1, bufferSize.BottomInclusive() }; - const COORD endInclusive{ bufferSize.RightInclusive(), bufferSize.BottomInclusive() }; - const COORD endExclusive{ bufferSize.EndExclusive() }; + const auto lastLineStart{ bufferEndLeft }; + const auto secondToLastCharacterPos{ point_offset_by_char(bufferEnd, bufferSize, -1) }; + const auto endInclusive{ bufferEnd }; // Iterate over each TextUnit. If we don't support // the given TextUnit, we're supposed to fallback @@ -1198,11 +1364,11 @@ class UiaTextRangeTests } THROW_IF_FAILED(utr->Move(textUnit, 1, &moveAmt)); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); VERIFY_ARE_EQUAL(0, moveAmt); // write "temp" at (2,2) - const COORD writeTarget{ 2, 2 }; + const til::point writeTarget{ 2, 2 }; _pTextBuffer->Write({ L"temp" }, writeTarget); // Verify expansion works properly @@ -1210,23 +1376,23 @@ class UiaTextRangeTests THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit)); if (textUnit <= TextUnit::TextUnit_Character) { - VERIFY_ARE_EQUAL(endInclusive, utr->_start); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); } else if (textUnit <= TextUnit::TextUnit_Word) { - VERIFY_ARE_EQUAL(writeTarget, utr->_start); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); } else if (textUnit <= TextUnit::TextUnit_Line) { - VERIFY_ARE_EQUAL(lastLineStart, utr->_start); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); } else // textUnit <= TextUnit::TextUnit_Document: { - VERIFY_ARE_EQUAL(origin, utr->_start); - VERIFY_ARE_EQUAL(endExclusive, utr->_end); + VERIFY_ARE_EQUAL(origin, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(endExclusive, til::point{ utr->_end }); } // reset the UTR @@ -1250,23 +1416,23 @@ class UiaTextRangeTests // Special case: _end will always be endInclusive, because... // - degenerate --> it moves with _start to stay degenerate // - !degenerate --> it excludes the last char, to select the second to last char - VERIFY_ARE_EQUAL(degenerate ? endInclusive : secondToLastCharacterPos, utr->_start); - VERIFY_ARE_EQUAL(endInclusive, utr->_end); + VERIFY_ARE_EQUAL(degenerate ? endInclusive : secondToLastCharacterPos, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(endInclusive, til::point{ utr->_end }); } else if (textUnit <= TextUnit::TextUnit_Word) { - VERIFY_ARE_EQUAL(origin, utr->_start); - VERIFY_ARE_EQUAL(degenerate ? origin : writeTarget, utr->_end); + VERIFY_ARE_EQUAL(origin, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(degenerate ? origin : writeTarget, til::point{ utr->_end }); } else if (textUnit <= TextUnit::TextUnit_Line) { - VERIFY_ARE_EQUAL(lastLineStart, utr->_start); - VERIFY_ARE_EQUAL(degenerate ? lastLineStart : endExclusive, utr->_end); + VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(degenerate ? lastLineStart : endExclusive, til::point{ utr->_end }); } else // textUnit <= TextUnit::TextUnit_Document: { - VERIFY_ARE_EQUAL(origin, utr->_start); - VERIFY_ARE_EQUAL(degenerate ? origin : endExclusive, utr->_end); + VERIFY_ARE_EQUAL(origin, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(degenerate ? origin : endExclusive, til::point{ utr->_end }); } } @@ -1274,9 +1440,7 @@ class UiaTextRangeTests { // See GH#7742 for more details. - const auto bufferSize{ _pTextBuffer->GetSize() }; - const COORD origin{ bufferSize.Origin() }; - const COORD originExclusive{ origin.X, origin.Y + 1 }; + const auto originExclusive{ point_offset_by_char(origin, bufferSize, 1) }; _pTextBuffer->Write({ L"My name is Carlos" }, origin); @@ -1314,21 +1478,20 @@ class UiaTextRangeTests TEST_METHOD(ScrollIntoView) { - const auto bufferSize{ _pTextBuffer->GetSize() }; const auto viewportSize{ _pUiaData->GetViewport() }; const std::vector testData{ - { L"Origin", bufferSize.Top() }, - { L"ViewportHeight From Top - 1", bufferSize.Top() + viewportSize.Height() - 1 }, - { L"ViewportHeight From Top", bufferSize.Top() + viewportSize.Height() }, - { L"ViewportHeight From Top + 1", bufferSize.Top() + viewportSize.Height() + 1 }, - { L"ViewportHeight From Bottom - 1", bufferSize.BottomInclusive() - viewportSize.Height() - 1 }, - { L"ViewportHeight From Bottom", bufferSize.BottomInclusive() - viewportSize.Height() }, - { L"ViewportHeight From Bottom + 1", bufferSize.BottomInclusive() - viewportSize.Height() + 1 }, + { L"Origin", gsl::narrow(bufferSize.top()) }, + { L"ViewportHeight From Top - 1", base::ClampedNumeric(bufferSize.top()) + viewportSize.Height() - 1 }, + { L"ViewportHeight From Top", base::ClampedNumeric(bufferSize.top()) + viewportSize.Height() }, + { L"ViewportHeight From Top + 1", base::ClampedNumeric(bufferSize.top()) + viewportSize.Height() + 1 }, + { L"ViewportHeight From Bottom - 1", base::ClampedNumeric(bufferSize.bottom()) - viewportSize.Height() - 2 }, + { L"ViewportHeight From Bottom", base::ClampedNumeric(bufferSize.bottom()) - viewportSize.Height() - 1 }, + { L"ViewportHeight From Bottom + 1", base::ClampedNumeric(bufferSize.bottom()) - viewportSize.Height() + 1 }, // GH#7839: ExclusiveEnd is a non-existent space, // so scrolling to it when !alignToTop used to crash - { L"Exclusive End", bufferSize.BottomExclusive() } + { L"Exclusive End", gsl::narrow(bufferSize.bottom()) } }; BEGIN_TEST_METHOD_PROPERTIES() @@ -1342,7 +1505,7 @@ class UiaTextRangeTests for (const auto test : testData) { Log::Comment(test.comment.c_str()); - const til::point pos{ bufferSize.Left(), test.yPos }; + const til::point pos{ bufferSize.left(), test.yPos }; THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr, _pUiaData, &_dummyProvider, pos, pos)); VERIFY_SUCCEEDED(utr->ScrollIntoView(alignToTop)); } @@ -1663,4 +1826,140 @@ class UiaTextRangeTests UiaTextRange* cloneUtr2 = static_cast(clone2.Get()); VERIFY_IS_TRUE(cloneUtr2->_blockRange); } + + TEST_METHOD(Movement) + { + // Helpful variables + const auto firstChar{ point_offset_by_char(origin, bufferSize, 1) }; + const auto secondChar{ point_offset_by_char(origin, bufferSize, 2) }; + const auto fifthChar{ point_offset_by_char(origin, bufferSize, 5) }; + const auto sixthChar{ point_offset_by_char(origin, bufferSize, 6) }; + const til::point documentEnd{ bufferSize.left(), (bufferSize.height() / 2) + 1 }; + + // Populate buffer + // Split the line into 5 segments alternating between "X" and whitespace + // _________________ + // |XXX XXX XXX| + // |XXX XXX XXX| + // |XXX XXX XXX| + // |XXX XXX XXX| + // |_______________| + { + short i = 0; + auto iter{ _pTextBuffer->GetCellDataAt(origin) }; + const auto segment{ bufferSize.width() / 5 }; + while (iter.Pos() != documentEnd) + { + bool fill{ true }; + if (i % segment == 0) + { + fill = !fill; + } + + if (fill) + { + _pTextBuffer->Write({ L"X" }, iter.Pos()); + } + + ++i; + ++iter; + } + } + + // Define tests + struct TestInput + { + TextUnit unit; + int moveAmt; + til::point start; + til::point end; + }; + + struct TestOutput + { + int moveAmt; + til::point start; + til::point end; + }; + + struct MyTest + { + std::wstring name; + TestInput input; + TestOutput output; + }; + + const std::vector tests{ + MyTest{ L"Degenerate at origin", TestInput{ TextUnit_Character, -5, origin, origin }, TestOutput{ 0, origin, origin } } + }; + } + + TEST_METHOD(GeneratedMovementTests) + { + // Populate the buffer with... + // - 9 segments of alternating text + // - up to half of the buffer (vertically) + // It'll look something like this + // +---------------------------+ + // |XXX XXX XXX XXX XXX| + // |XXX XXX XXX XXX XXX| + // |XXX XXX XXX XXX XXX| + // |XXX XXX XXX XXX XXX| + // |XXX XXX XXX XXX XXX| + // | | + // | | + // | | + // | | + // | | + // +---------------------------+ + { + short i = 0; + auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin()) }; + const auto segment{ bufferSize.width() / 9 }; + while (iter.Pos() != docEnd) + { + bool fill{ true }; + if (i % segment == 0) + { + fill = !fill; + } + + if (fill) + { + _pTextBuffer->Write({ L"X" }, iter.Pos()); + } + + ++i; + ++iter; + } + } + + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"DataSource", L"Export:GeneratedMovementTestDataSource") + END_TEST_METHOD_PROPERTIES() + + WEX::TestExecution::DisableVerifyExceptions disableVerifyExceptions{}; + WEX::TestExecution::SetVerifyOutput verifyOutputScope{ WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures }; + + unsigned int i{}; + TestData::TryGetValue(L"index", i); // index is produced by the ArrayIndexTaefAdapterSource above + const auto& testCase{ s_movementTests[i] }; + + Log::Comment(NoThrowString().Format(L"[%zu.0] Test case \"%.*s\"", i, testCase.name.size(), testCase.name.data())); + if (testCase.skip) + { + Log::Result(WEX::Logging::TestResults::Result::Skipped); + } + else + { + Microsoft::WRL::ComPtr utr; + int amountMoved; + THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&utr, _pUiaData, &_dummyProvider, testCase.input.start, testCase.input.end)); + THROW_IF_FAILED(utr->Move(testCase.input.unit, testCase.input.moveAmount, &amountMoved)); + + VERIFY_ARE_EQUAL(testCase.expected.moveAmount, amountMoved); + VERIFY_ARE_EQUAL(testCase.expected.start, til::point{ utr->_start }); + VERIFY_ARE_EQUAL(testCase.expected.end, til::point{ utr->_end }); + } + } }; diff --git a/tools/TestTableWriter/GenerateTests.ps1 b/tools/TestTableWriter/GenerateTests.ps1 new file mode 100644 index 000000000..c4432de5c --- /dev/null +++ b/tools/TestTableWriter/GenerateTests.ps1 @@ -0,0 +1,164 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +################################################################################ +# This script generates the an array of UiaTextRange tests suitable for replacing the body of +# src\interactivity\win32\ut_interactivity_win32\UiaTextRangeTests.cpp TEST_METHOD(GeneratedMovementTests) +# +# See tools\TestTableWriter\README.md for more details on how to use this script. + +[CmdletBinding()] +Param( + [Parameter(Position=0, ValueFromPipeline=$true)] + [string]$TestPath = "UiaTests.csv" +) + +# 0. Generate a comment telling people to not modify these tests in the .cpp +$result = "// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// DO NOT MODIFY THESE TESTS DIRECTLY +// These were generated by tools\TestTableWriter\GenerateTests.ps1 +// Read tools\TestTableWriter\README.md for more details" + +# TODO: THIS IS PROBABLY WRONG. Bottom/Right are exclusive (I think?) +# 1. Define a few helpful variables to make life easier. +$result += " +// Define a few helpful variables +constexpr til::rectangle bufferSize{ 0, 0, 80, 300 }; +constexpr short midX{ 40 }; +constexpr short midY{ 150 }; +constexpr short midPopulatedY{ 75 }; +constexpr til::point origin{ 0, 0 }; +constexpr til::point midTop{ midX, 0 }; +constexpr til::point midHistory{ midX, midPopulatedY }; +constexpr til::point midDocEnd{ midX, midY }; +constexpr til::point lastCharPos{ 79, midY }; +constexpr til::point docEnd{ 0, midY + 1 }; +constexpr til::point midEmptySpace{ midX, midY + midPopulatedY }; +constexpr til::point bufferEnd{ 79, 299 }; +constexpr til::point endExclusive{ 0, 300 };`n" + +# 2. Import the CSV test file and find all of the variables we need +$tests = Import-Csv $TestPath; +$vars = New-Object System.Collections.Generic.SortedSet[string]; +foreach ($test in $tests) +{ + $vars.Add($test.Start) > $null; + $vars.Add($test.End) > $null; + $vars.Add($test.Result_Start) > $null; + $vars.Add($test.Result_End) > $null; +} + +# 3. Define each of the vars +# 3.a. Some of the variables were already defined at the beginning. So let's remove those. +$vars.Remove("origin") > $null; +$vars.Remove("midTop") > $null; +$vars.Remove("midHistory") > $null; +$vars.Remove("docEnd") > $null; + +# 3.b. Now all of the remaining vars can be deduced from standard vars +foreach ($var in $vars) +{ + # Extract the standard var from the name + $standardVar = $var.Contains("Left") ? $var.Split("Left") : $var.Substring(0, $var.length - 3); + + # i. Contains number --> requires movement + if ($var -match ".*\d+.*") + { + # 3rd to last character denotes the movement direction + # P --> plus/forwards + # M --> minus/backwards + $moveForward = $var.substring($var.length - 3, 1) -eq 'P'; + + # 2nd to last character denotes the movement amount + $moveAmt = $var.substring($var.length -2, 1); + + # last character denotes the movement type + switch ($var.substring($var.length - 1, 1)) { + 'C' # move by character + { + if ($moveForward) + { + $result += "constexpr auto {0}{{ point_offset_by_char({1}, bufferSize, {2}) }};" -f $var, $standardVar, $moveAmt; + } + else + { + $result += "constexpr auto {0}{{ point_offset_by_char({1}, bufferSize, -{2}) }};" -f $var, $standardVar, $moveAmt; + } + } + 'L' # move by line + { + if ($moveForward) + { + $result += "constexpr auto {0}{{ point_offset_by_line({1}, bufferSize, {2}) }};" -f $var, $standardVar, $moveAmt; + } + else + { + $result += "constexpr auto {0}{{ point_offset_by_line({1}, bufferSize, -{2}) }};" -f $var, $standardVar, $moveAmt; + } + } + Default { Write-Host "Error: unknown variable movement type" -ForegroundColor Red } + } + } + # ii. Contains "Left" --> set X to left + elseif ($var.Contains("Left")) + { + $result += "constexpr til::point " + $var + "{ bufferSize.left(), " + $standardVar + ".y() };"; + } + $result += "`n"; +} + +# 4. Write the tests +# 4.a. Introduce a struct to store each test as +$result += "struct GeneratedMovementTestInput +{ + TextUnit unit; + int moveAmount; + til::point start; + til::point end; +}; +struct GeneratedMovementTestExpected +{ + int moveAmount; + til::point start; + til::point end; +}; +struct GeneratedMovementTest +{ + std::wstring_view name; + GeneratedMovementTestInput input; + GeneratedMovementTestExpected expected; + bool skip; +};`n`n"; + +# 4.b. Iterate through CSV file and generate a test for each one +$result += "static constexpr std::array s_movementTests`n{{`n" -f $tests.count; +foreach ($test in $tests) +{ + $degeneratePrefix = $test.degenerate -eq "TRUE" ? "" : "non-"; + $movementType = $test.TextUnit.substring(9); + $testName = "L`"Move {0}degenerate range at position {1} {2} times by {3}`"" -f $degeneratePrefix, $test.Position, $test.MoveAmount, $movementType; + $testInput = "GeneratedMovementTestInput{{ + TextUnit::{0}, + {1}, + {2}, + {3} + }}" -f $test.TextUnit, $test.MoveAmount, $test.Start, $test.End; + $testExpected = "GeneratedMovementTestExpected{{ + {0}, + {1}, + {2} + }}" -f $test.Result_MoveAmount, $test.Result_Start, $test.Result_End; + $skip = $test.Skip -eq "TRUE" ? "true" : "false"; + + $result += " GeneratedMovementTest{{ + {0}, + {1}, + {2}, + {3} + }},`n" -f $testName, $testInput, $testExpected, $skip; +} +$result += "};`n`n" + +$result > "..\..\src\interactivity\win32\ut_interactivity_win32\GeneratedUiaTextRangeMovementTests.g.cpp"; diff --git a/tools/TestTableWriter/README.md b/tools/TestTableWriter/README.md new file mode 100644 index 000000000..a708f1695 --- /dev/null +++ b/tools/TestTableWriter/README.md @@ -0,0 +1,90 @@ +--- +author: Carlos Zamora @carlos-zamora +created on: 2021-08-05 +last updated: 2021-08-05 +--- + +# Test Table Writer + +The Test Table Writer was written as a method to generate UI Automation tests for OpenConsole. UI Automation has a lot of corner cases, so we developed this workflow to simplify storing and updating these test cases (particularly revolving around movement). + +# How to use it +1. Update `UiaTests.csv`: + - This file is used to store the tests in a compact format. The defined columns include... + - Degenerate: is this a degenerate range? + - Position: see the position chart below + - TextUnit: what text unit to move by + - MoveAmount: how many times to move + - Start: the start endpoint of the text range. Represented by a variable name used to signify a position in the buffer. See the variable heuristics section below. + - End: the start endpoint of the text range. Represented by a variable name used to signify a position in the buffer. See the variable heuristics section below. + - Result_MoveAmount: the expected amount to have moved by + - Result_Start: the expected position of the start endpoint after executing the move operation + - Result_End: the expected position of the end endpoint after executing the move operation + - Skip: skip the test. Can be used for failing tests. + - Each row represents a new test in a compact format. + - Use the position chart and the variable heuristics below to add more tests easily. +2. Run `GenerateTests.ps1` + - `GenerateTests.ps1` will load `UiaTests.csv` and export the tests and any necessary variables to "src\interactivity\win32\ut_interactivity_win32\GeneratedUiaTextRangeMovementTests.g.cpp". +3. Build and run the tests + - Build UiaTextRangeTests + - Go to bin\x64\Debug + - Run `clear; .\TE.exe /name:*UiaTextRangeTests*GeneratedMovementTests* .\Conhost.Interactivity.Win32.Unit.Tests.dll` in PowerShell +5. If the tests pass, upload any changes to `UiaTests.csv` and `GeneratedUiaTextRangeMovementTests.g.cpp`. Be sure to run the code formatter. + +# Helpful tips +- How to verify a test is authored correctly + - use MS Word to generate some text (try typing "=lorem(5,5)" then pressing enter to generate text) + - use Accessibility Insights to run the UIA API on MS Word's ITextProvider + - if you create a selection (or move the cursor), then tell Accessibility Insights to get the "Selection" range (or refresh it), you can easily set up a test case +- Run the tests via Visual Studio + - In the Solution Explorer, right-click Interactivity.Win32.Tests.Unit and select "Set as Startup Project" + - right-click it again and select "Properties" + - In the Debugging section, set.. + - "Command" --> `$(OutDir)/TE.exe` + - "Command Arguments" --> `$(TargetPath) /name:*uiatextrange*generated* /inproc` + +# Position chart +The text buffer is assumed to be partially filled. Specifically, the top half of the text buffer contains text, and each row is filled with 9 segments of alternating text. For visualization, +the ascii diagram below shows what the text buffer may look like. +``` ++---------------------------+ +|1XX XXX X2X XXX XXX| +|XXX XXX XXX XXX XXX| +|XXX XXX X3X XXX XXX| +|XXX XXX XXX XXX XXX| +|XXX XXX X4X XXX XX5| +|6 | +| | +| 7 | +| | +| 8| ++---------------------------+ +9 +``` +The following positions are being tested: +1. `origin`: buffer origin +2. `midTop`: middle of the top line +3. `midHistory`: middle of the history +4. `midDocEnd`: middle of the last line of text +5. `lastCharPos`: last character in the buffer +6. `docEnd`: one past the last line of text +7. `midEmptySpace`: middle of the empty space in the buffer +8. `bufferEnd`: end of the buffer +9. `endExclusive`: exclusive end of the buffer + +This is intended to provide adequate testing coverage for GH#6986. + +# Variable Heuristics +Each position above already has a predefined variable name. However, a few heuristics are used to define new variables based on the standard variables above. +- `Left`: the left-most position on the same line as `` +- `PC`, `MC`: + - ``: start at the position of `` + - `P` (or `M`): move forwards (aka "plus") by a certain amount (`M` is used to move backwards [aka "minus"]) + - ``: how much to move forwards by + - `C`: move by character. For simplicity, assumes that each character is one-cell wide. +- `PL`, `ML`: + - same as above, except move by line. For simplicity, assumes that you won't hit a buffer boundary. + +# Helpful terms and concepts +- *degenerate*: the text range encompasses no text. Also, means the start and end endpoints are the same. +- *TextUnit*: a heuristic for how much to move by. Possible values include `TextUnit_Character`, `TextUnit_Word`, and `TextUnit_Line`. See https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-uiautomationtextunits for more details. diff --git a/tools/TestTableWriter/UiaTests.csv b/tools/TestTableWriter/UiaTests.csv new file mode 100644 index 000000000..195f6a8ef --- /dev/null +++ b/tools/TestTableWriter/UiaTests.csv @@ -0,0 +1,256 @@ +Degenerate,Position,TextUnit,MoveAmount,Start,End,Result_MoveAmount,Result_Start,Result_End,Skip +TRUE,1,TextUnit_Character,-5,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Character,-1,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Character,0,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Character,1,origin,origin,1,originP1C,originP1C,FALSE +TRUE,1,TextUnit_Character,5,origin,origin,5,originP5C,originP5C,FALSE +FALSE,1,TextUnit_Character,-5,origin,originP1C,0,origin,originP1C,FALSE +FALSE,1,TextUnit_Character,-1,origin,originP1C,0,origin,originP1C,FALSE +FALSE,1,TextUnit_Character,0,origin,originP1C,0,origin,originP1C,FALSE +FALSE,1,TextUnit_Character,1,origin,originP1C,1,originP1C,originP2C,FALSE +FALSE,1,TextUnit_Character,5,origin,originP1C,5,originP5C,originP6C,FALSE +TRUE,2,TextUnit_Character,-5,midTop,midTop,-5,midTopM5C,midTopM5C,FALSE +TRUE,2,TextUnit_Character,-1,midTop,midTop,-1,midTopM1C,midTopM1C,FALSE +TRUE,2,TextUnit_Character,0,midTop,midTop,0,midTop,midTop,FALSE +TRUE,2,TextUnit_Character,1,midTop,midTop,1,midTopP1C,midTopP1C,FALSE +TRUE,2,TextUnit_Character,5,midTop,midTop,5,midTopP5C,midTopP5C,FALSE +FALSE,2,TextUnit_Character,-5,midTop,midTopP1C,-5,midTopM5C,midTopM4C,FALSE +FALSE,2,TextUnit_Character,-1,midTop,midTopP1C,-1,midTopM1C,midTop,FALSE +FALSE,2,TextUnit_Character,0,midTop,midTopP1C,0,midTop,midTopP1C,FALSE +FALSE,2,TextUnit_Character,1,midTop,midTopP1C,1,midTopP1C,midTopP2C,FALSE +FALSE,2,TextUnit_Character,5,midTop,midTopP1C,5,midTopP5C,midTopP6C,FALSE +TRUE,3,TextUnit_Character,-5,midHistory,midHistory,-5,midHistoryM5C,midHistoryM5C,FALSE +TRUE,3,TextUnit_Character,-1,midHistory,midHistory,-1,midHistoryM1C,midHistoryM1C,FALSE +TRUE,3,TextUnit_Character,0,midHistory,midHistory,0,midHistory,midHistory,FALSE +TRUE,3,TextUnit_Character,1,midHistory,midHistory,1,midHistoryP1C,midHistoryP1C,FALSE +TRUE,3,TextUnit_Character,5,midHistory,midHistory,5,midHistoryP5C,midHistoryP5C,FALSE +FALSE,3,TextUnit_Character,-5,midHistory,midHistoryP1C,-5,midHistoryM5C,midHistoryM4C,FALSE +FALSE,3,TextUnit_Character,-1,midHistory,midHistoryP1C,-1,midHistoryM1C,midHistory,FALSE +FALSE,3,TextUnit_Character,0,midHistory,midHistoryP1C,0,midHistory,midHistoryP1C,FALSE +FALSE,3,TextUnit_Character,1,midHistory,midHistoryP1C,1,midHistoryP1C,midHistoryP2C,FALSE +FALSE,3,TextUnit_Character,5,midHistory,midHistoryP1C,5,midHistoryP5C,midHistoryP6C,FALSE +TRUE,1,TextUnit_Line,-5,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Line,-1,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Line,0,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Line,1,origin,origin,1,originP1L,originP1L,FALSE +TRUE,1,TextUnit_Line,5,origin,origin,5,originP5L,originP5L,FALSE +FALSE,1,TextUnit_Line,-5,origin,originP1C,0,origin,originP1L,TRUE +FALSE,1,TextUnit_Line,-1,origin,originP1C,0,origin,originP1L,TRUE +FALSE,1,TextUnit_Line,0,origin,originP1C,0,origin,originP1L,TRUE +FALSE,1,TextUnit_Line,1,origin,originP1C,1,originP1L,originP2L,FALSE +FALSE,1,TextUnit_Line,5,origin,originP1C,5,originP5L,originP6L,FALSE +TRUE,2,TextUnit_Line,-5,midTop,midTop,-1,origin,origin,TRUE +TRUE,2,TextUnit_Line,-1,midTop,midTop,-1,origin,origin,TRUE +TRUE,2,TextUnit_Line,0,midTop,midTop,0,midTop,midTop,FALSE +TRUE,2,TextUnit_Line,1,midTop,midTop,1,midTopP1L,midTopP1L,FALSE +TRUE,2,TextUnit_Line,5,midTop,midTop,5,midTopP5L,midTopP5L,FALSE +FALSE,2,TextUnit_Line,-5,midTop,midTopP1C,0,origin,originP1L,TRUE +FALSE,2,TextUnit_Line,-1,midTop,midTopP1C,0,origin,originP1L,TRUE +FALSE,2,TextUnit_Line,0,midTop,midTopP1C,0,origin,originP1L,TRUE +FALSE,2,TextUnit_Line,1,midTop,midTopP1C,1,originP1L,originP2L,FALSE +FALSE,2,TextUnit_Line,5,midTop,midTopP1C,5,originP5L,originP6L,FALSE +TRUE,3,TextUnit_Line,-5,midHistory,midHistory,-5,midHistoryM4L,midHistoryM4L,FALSE +TRUE,3,TextUnit_Line,-1,midHistory,midHistory,-1,midHistoryLeft,midHistoryLeft,FALSE +TRUE,3,TextUnit_Line,0,midHistory,midHistory,0,midHistory,midHistory,FALSE +TRUE,3,TextUnit_Line,1,midHistory,midHistory,1,midHistoryP1L,midHistoryP1L,FALSE +TRUE,3,TextUnit_Line,5,midHistory,midHistory,5,midHistoryP5L,midHistoryP5L,FALSE +FALSE,3,TextUnit_Line,-5,midHistory,midHistoryP1C,-5,midHistoryM5L,midHistoryM4L,TRUE +FALSE,3,TextUnit_Line,-1,midHistory,midHistoryP1C,-1,midHistoryM1L,midHistoryLeft,TRUE +FALSE,3,TextUnit_Line,0,midHistory,midHistoryP1C,0,midHistoryLeft,midHistoryP1L,TRUE +FALSE,3,TextUnit_Line,1,midHistory,midHistoryP1C,1,midHistoryP1L,midHistoryP2L,FALSE +FALSE,3,TextUnit_Line,5,midHistory,midHistoryP1C,5,midHistoryP5L,midHistoryP6L,FALSE +TRUE,1,TextUnit_Document,-5,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Document,-1,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Document,0,origin,origin,0,origin,origin,FALSE +TRUE,1,TextUnit_Document,1,origin,origin,1,docEnd,docEnd,TRUE +TRUE,1,TextUnit_Document,5,origin,origin,1,docEnd,docEnd,TRUE +FALSE,1,TextUnit_Document,-5,origin,originP1C,0,origin,docEnd,TRUE +FALSE,1,TextUnit_Document,-1,origin,originP1C,0,origin,docEnd,TRUE +FALSE,1,TextUnit_Document,0,origin,originP1C,0,origin,docEnd,TRUE +FALSE,1,TextUnit_Document,1,origin,originP1C,0,origin,docEnd,TRUE +FALSE,1,TextUnit_Document,5,origin,originP1C,0,origin,docEnd,TRUE +TRUE,2,TextUnit_Document,-5,midTop,midTop,-1,origin,origin,FALSE +TRUE,2,TextUnit_Document,-1,midTop,midTop,-1,origin,origin,FALSE +TRUE,2,TextUnit_Document,0,midTop,midTop,0,midTop,midTop,FALSE +TRUE,2,TextUnit_Document,1,midTop,midTop,1,bufferEnd,bufferEnd,TRUE +TRUE,2,TextUnit_Document,5,midTop,midTop,1,bufferEnd,bufferEnd,TRUE +FALSE,2,TextUnit_Document,-5,midTop,midTopP1C,0,origin,bufferEnd,TRUE +FALSE,2,TextUnit_Document,-1,midTop,midTopP1C,0,origin,bufferEnd,TRUE +FALSE,2,TextUnit_Document,0,midTop,midTopP1C,0,origin,bufferEnd,TRUE +FALSE,2,TextUnit_Document,1,midTop,midTopP1C,0,origin,bufferEnd,TRUE +FALSE,2,TextUnit_Document,5,midTop,midTopP1C,0,origin,bufferEnd,TRUE +TRUE,3,TextUnit_Document,-5,midHistory,midHistory,-1,origin,origin,FALSE +TRUE,3,TextUnit_Document,-1,midHistory,midHistory,-1,origin,origin,FALSE +TRUE,3,TextUnit_Document,0,midHistory,midHistory,0,midHistory,midHistory,FALSE +TRUE,3,TextUnit_Document,1,midHistory,midHistory,1,endExclusive,endExclusive,TRUE +TRUE,3,TextUnit_Document,5,midHistory,midHistory,1,endExclusive,endExclusive,TRUE +FALSE,3,TextUnit_Document,-5,midHistory,midHistoryP1C,0,origin,endExclusive,TRUE +FALSE,3,TextUnit_Document,-1,midHistory,midHistoryP1C,0,origin,endExclusive,TRUE +FALSE,3,TextUnit_Document,0,midHistory,midHistoryP1C,0,origin,endExclusive,TRUE +FALSE,3,TextUnit_Document,1,midHistory,midHistoryP1C,0,origin,endExclusive,TRUE +FALSE,3,TextUnit_Document,5,midHistory,midHistoryP1C,0,origin,endExclusive,TRUE +TRUE,8,TextUnit_Character,-5,bufferEnd,bufferEnd,-5,bufferEndM5C,bufferEndM5C,FALSE +TRUE,8,TextUnit_Character,-1,bufferEnd,bufferEnd,-1,bufferEndM1C,bufferEndM1C,FALSE +TRUE,8,TextUnit_Character,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE +TRUE,8,TextUnit_Character,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +TRUE,8,TextUnit_Character,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +FALSE,8,TextUnit_Character,-5,bufferEnd,endExclusive,-5,bufferEndM5C,bufferEndM4C,FALSE +FALSE,8,TextUnit_Character,-1,bufferEnd,endExclusive,-1,bufferEndM1C,bufferEnd,FALSE +FALSE,8,TextUnit_Character,0,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE +FALSE,8,TextUnit_Character,1,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE +FALSE,8,TextUnit_Character,5,bufferEnd,endExclusive,0,bufferEnd,endExclusive,FALSE +TRUE,8,TextUnit_Line,-5,bufferEnd,bufferEnd,-5,bufferEndM4L,bufferEndM4L,FALSE +TRUE,8,TextUnit_Line,-1,bufferEnd,bufferEnd,-1,bufferEndLeft,bufferEndLeft,FALSE +TRUE,8,TextUnit_Line,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE +TRUE,8,TextUnit_Line,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +TRUE,8,TextUnit_Line,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +FALSE,8,TextUnit_Line,-5,bufferEnd,endExclusive,-5,bufferEndM5L,bufferEndM4L,TRUE +FALSE,8,TextUnit_Line,-1,bufferEnd,endExclusive,-1,bufferEndM1L,bufferEndLeft,TRUE +FALSE,8,TextUnit_Line,0,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE +FALSE,8,TextUnit_Line,1,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE +FALSE,8,TextUnit_Line,5,bufferEnd,endExclusive,0,bufferEndLeft,endExclusive,TRUE +TRUE,8,TextUnit_Document,-5,bufferEnd,bufferEnd,-1,origin,origin,FALSE +TRUE,8,TextUnit_Document,-1,bufferEnd,bufferEnd,-1,origin,origin,FALSE +TRUE,8,TextUnit_Document,0,bufferEnd,bufferEnd,0,bufferEnd,bufferEnd,FALSE +TRUE,8,TextUnit_Document,1,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +TRUE,8,TextUnit_Document,5,bufferEnd,bufferEnd,1,endExclusive,endExclusive,TRUE +FALSE,8,TextUnit_Document,-5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE +FALSE,8,TextUnit_Document,-1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE +FALSE,8,TextUnit_Document,0,bufferEnd,endExclusive,0,origin,endExclusive,TRUE +FALSE,8,TextUnit_Document,1,bufferEnd,endExclusive,0,origin,endExclusive,TRUE +FALSE,8,TextUnit_Document,5,bufferEnd,endExclusive,0,origin,endExclusive,TRUE +TRUE,9,TextUnit_Character,-5,endExclusive,endExclusive,-5,bufferEndM4C,bufferEndM4C,FALSE +TRUE,9,TextUnit_Character,-1,endExclusive,endExclusive,-1,bufferEnd,bufferEnd,FALSE +TRUE,9,TextUnit_Character,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Character,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Character,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Line,-5,endExclusive,endExclusive,-5,bufferEndM4L,bufferEndM4L,FALSE +TRUE,9,TextUnit_Line,-1,endExclusive,endExclusive,-1,bufferEndLeft,bufferEndLeft,FALSE +TRUE,9,TextUnit_Line,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Line,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Line,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Document,-5,endExclusive,endExclusive,-1,origin,origin,FALSE +TRUE,9,TextUnit_Document,-1,endExclusive,endExclusive,-1,origin,origin,FALSE +TRUE,9,TextUnit_Document,0,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Document,1,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,9,TextUnit_Document,5,endExclusive,endExclusive,0,endExclusive,endExclusive,FALSE +TRUE,4,TextUnit_Character,-5,midDocEnd,midDocEnd,-5,midDocEndM5C,midDocEndM5C,TRUE +TRUE,4,TextUnit_Character,-1,midDocEnd,midDocEnd,-1,midDocEndM1C,midDocEndM1C,TRUE +TRUE,4,TextUnit_Character,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE +TRUE,4,TextUnit_Character,1,midDocEnd,midDocEnd,1,midDocEndP1C,midDocEndP1C,TRUE +TRUE,4,TextUnit_Character,5,midDocEnd,midDocEnd,5,midDocEndP5C,midDocEndP5C,TRUE +FALSE,4,TextUnit_Character,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5C,midDocEndM4C,TRUE +FALSE,4,TextUnit_Character,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1C,midDocEnd,TRUE +FALSE,4,TextUnit_Character,0,midDocEnd,midDocEndP1C,0,midDocEnd,midDocEndP1C,TRUE +FALSE,4,TextUnit_Character,1,midDocEnd,midDocEndP1C,1,midDocEndP1C,midDocEndP2C,TRUE +FALSE,4,TextUnit_Character,5,midDocEnd,midDocEndP1C,5,midDocEndP5C,midDocEndP6C,TRUE +TRUE,4,TextUnit_Line,-5,midDocEnd,midDocEnd,-5,midDocEndM4L,midDocEndM4L,TRUE +TRUE,4,TextUnit_Line,-1,midDocEnd,midDocEnd,-1,midDocEndLeft,midDocEndLeft,TRUE +TRUE,4,TextUnit_Line,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE +TRUE,4,TextUnit_Line,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE +TRUE,4,TextUnit_Line,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE +FALSE,4,TextUnit_Line,-5,midDocEnd,midDocEndP1C,-5,midDocEndM5L,midDocEndM4L,TRUE +FALSE,4,TextUnit_Line,-1,midDocEnd,midDocEndP1C,-1,midDocEndM1L,midDocEndLeft,TRUE +FALSE,4,TextUnit_Line,0,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE +FALSE,4,TextUnit_Line,1,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE +FALSE,4,TextUnit_Line,5,midDocEnd,midDocEndP1C,0,midDocEndLeft,docEnd,TRUE +TRUE,4,TextUnit_Document,-5,midDocEnd,midDocEnd,-1,origin,origin,TRUE +TRUE,4,TextUnit_Document,-1,midDocEnd,midDocEnd,-1,origin,origin,TRUE +TRUE,4,TextUnit_Document,0,midDocEnd,midDocEnd,0,midDocEnd,midDocEnd,TRUE +TRUE,4,TextUnit_Document,1,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE +TRUE,4,TextUnit_Document,5,midDocEnd,midDocEnd,1,docEnd,docEnd,TRUE +FALSE,4,TextUnit_Document,-5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE +FALSE,4,TextUnit_Document,-1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE +FALSE,4,TextUnit_Document,0,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE +FALSE,4,TextUnit_Document,1,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE +FALSE,4,TextUnit_Document,5,midDocEnd,midDocEndP1C,0,origin,docEnd,TRUE +TRUE,5,TextUnit_Character,-5,lastCharPos,lastCharPos,-5,lastCharPosM5C,lastCharPosM5C,TRUE +TRUE,5,TextUnit_Character,-1,lastCharPos,lastCharPos,-1,lastCharPosM1C,lastCharPosM1C,TRUE +TRUE,5,TextUnit_Character,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE +TRUE,5,TextUnit_Character,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +TRUE,5,TextUnit_Character,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +FALSE,5,TextUnit_Character,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5C,lastCharPosM4C,TRUE +FALSE,5,TextUnit_Character,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1C,lastCharPos,TRUE +FALSE,5,TextUnit_Character,0,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE +FALSE,5,TextUnit_Character,1,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE +FALSE,5,TextUnit_Character,5,lastCharPos,lastCharPosP1C,0,lastCharPos,docEnd,TRUE +TRUE,5,TextUnit_Line,-5,lastCharPos,lastCharPos,-5,lastCharPosM4L,lastCharPosM4L,TRUE +TRUE,5,TextUnit_Line,-1,lastCharPos,lastCharPos,-1,lastCharPosLeft,lastCharPosLeft,TRUE +TRUE,5,TextUnit_Line,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE +TRUE,5,TextUnit_Line,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +TRUE,5,TextUnit_Line,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +FALSE,5,TextUnit_Line,-5,lastCharPos,lastCharPosP1C,-5,lastCharPosM5L,lastCharPosM4L,TRUE +FALSE,5,TextUnit_Line,-1,lastCharPos,lastCharPosP1C,-1,lastCharPosM1L,lastCharPosLeft,TRUE +FALSE,5,TextUnit_Line,0,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE +FALSE,5,TextUnit_Line,1,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE +FALSE,5,TextUnit_Line,5,lastCharPos,lastCharPosP1C,0,lastCharPosLeft,docEnd,TRUE +TRUE,5,TextUnit_Document,-5,lastCharPos,lastCharPos,-1,origin,origin,TRUE +TRUE,5,TextUnit_Document,-1,lastCharPos,lastCharPos,-1,origin,origin,TRUE +TRUE,5,TextUnit_Document,0,lastCharPos,lastCharPos,0,lastCharPos,lastCharPos,TRUE +TRUE,5,TextUnit_Document,1,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +TRUE,5,TextUnit_Document,5,lastCharPos,lastCharPos,1,docEnd,docEnd,TRUE +FALSE,5,TextUnit_Document,-5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE +FALSE,5,TextUnit_Document,-1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE +FALSE,5,TextUnit_Document,0,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE +FALSE,5,TextUnit_Document,1,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE +FALSE,5,TextUnit_Document,5,lastCharPos,lastCharPosP1C,0,origin,docEnd,TRUE +TRUE,6,TextUnit_Character,-5,docEnd,docEnd,-5,docEndM5C,docEndM5C,TRUE +TRUE,6,TextUnit_Character,-1,docEnd,docEnd,-1,docEndM1C,docEndM1C,TRUE +TRUE,6,TextUnit_Character,0,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Character,1,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Character,5,docEnd,docEnd,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Character,-5,docEnd,docEndP1C,-5,docEndM5C,docEndM5C,TRUE +FALSE,6,TextUnit_Character,-1,docEnd,docEndP1C,-1,docEndM1C,docEndM1C,TRUE +FALSE,6,TextUnit_Character,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Character,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Character,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Line,-5,docEnd,docEnd,-5,docEndM4L,docEndM4L,TRUE +TRUE,6,TextUnit_Line,-1,docEnd,docEnd,-1,docEndLeft,docEndLeft,TRUE +TRUE,6,TextUnit_Line,0,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Line,1,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Line,5,docEnd,docEnd,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Line,-5,docEnd,docEndP1C,-5,docEndM4L,docEndM4L,TRUE +FALSE,6,TextUnit_Line,-1,docEnd,docEndP1C,-1,docEndLeft,docEndLeft,TRUE +FALSE,6,TextUnit_Line,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Line,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Line,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Document,-5,docEnd,docEnd,-1,origin,origin,TRUE +TRUE,6,TextUnit_Document,-1,docEnd,docEnd,-1,origin,origin,TRUE +TRUE,6,TextUnit_Document,0,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Document,1,docEnd,docEnd,0,docEnd,docEnd,TRUE +TRUE,6,TextUnit_Document,5,docEnd,docEnd,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Document,-5,docEnd,docEndP1C,-1,origin,origin,TRUE +FALSE,6,TextUnit_Document,-1,docEnd,docEndP1C,-1,origin,origin,TRUE +FALSE,6,TextUnit_Document,0,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Document,1,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +FALSE,6,TextUnit_Document,5,docEnd,docEndP1C,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpace,-5,docEndM5C,docEndM5C,TRUE +TRUE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpace,-1,docEndM1C,docEndM1C,TRUE +TRUE,7,TextUnit_Character,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Character,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Character,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Character,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM5C,docEndM5C,TRUE +FALSE,7,TextUnit_Character,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndM1C,docEndM1C,TRUE +FALSE,7,TextUnit_Character,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Character,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Character,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpace,-5,docEndM4L,docEndM4L,TRUE +TRUE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpace,-1,docEndLeft,docEndLeft,TRUE +TRUE,7,TextUnit_Line,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Line,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Line,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Line,-5,midEmptySpace,midEmptySpaceP1C,-5,docEndM4L,docEndM4L,TRUE +FALSE,7,TextUnit_Line,-1,midEmptySpace,midEmptySpaceP1C,-1,docEndLeft,docEndLeft,TRUE +FALSE,7,TextUnit_Line,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Line,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Line,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE +TRUE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpace,-1,origin,origin,TRUE +TRUE,7,TextUnit_Document,0,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Document,1,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +TRUE,7,TextUnit_Document,5,midEmptySpace,midEmptySpace,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Document,-5,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE +FALSE,7,TextUnit_Document,-1,midEmptySpace,midEmptySpaceP1C,-1,origin,origin,TRUE +FALSE,7,TextUnit_Document,0,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Document,1,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE +FALSE,7,TextUnit_Document,5,midEmptySpace,midEmptySpaceP1C,0,docEnd,docEnd,TRUE