terminal/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp
Carlos Zamora 4b48f74f5f
Enable Word Navigation in UiaTextRange (#3659)
Enables support for word navigation when using an automation client (i.e.: Narrator, etc...). Specifically, adds this functionality to the UiaTextRange class. The only delimiter used is whitespace because that's how words are separated in English.

# General "Word Movement" Expectations
The resulting text range should include any word break characters that are present at the end of the word, but before the start of the next word. (Source)

If you already are on a word, getting the "next word" means you skip the word you are on, and highlight the upcoming word appropriately. (similar idea when moving backwards)


# Word Expansion
Since word selection is supposed to detect word delimiters already, I figured I'd reuse that code. I moved it from TerminalCore to the TextBuffer.

Then I built on top of it by adding an optional additional parameter that decides if you want to include...
- the delimiter run when moving forward
- the character run when moving backwards
It defaults to false so that we don't have to care when using it in selection. But we change it to true when using it in our UiaTextRange

# UiaTextRange
The code is based on character movement. This allows us to actually work with boundary conditions.

The main thing to remember here is that each text range is recorded as a MoveState. The text range is most easily defined when you think about the start Endpoint and the end Endpoint. An Endpoint is just a linear 1-dimensional indexing of the text buffer. Examples:
- Endpoint 0 --> (0,0)
- Endpoint 79 --> (79,0) (when the buffer width is 80)
- Endpoint 80 -->(0,1) (when the buffer width is 80)
- When moving forward, the strategy is to focus on moving the end Endpoint. That way, we properly get the indexing for the "next" word (this also fixes a wrapping issue). Then, we update the start Endpoint. (This is reversed for moving backwards).
- When moving a specific Endpoint, we just have a few extra if statements to properly adjust for moving start vs end.

# Hooking it up
All we really had to do is add an enum. This part was super easy :)

I originally wanted the delimiters to be able to be defined. I'm not so sure about that anymore. Either way, I hardcoded our delimiter into a variable so if we ever want to expand on it or make that customizable, we just modify that variable.

# Defining your own word delimiters

- Import a word delimiter into the constructor of the ScreenInfoUiaProvider (SIUP)
  - This defines a word delimiter for all the UiaTextRanges (UTR) created by in this context
- import a word delimiter into the UTR directly
  - this provides more control over what a "word" is
  - this can be useful if you have an idea of what text a particular UTR will encounter and you want to customize the word navigation for it (i.e consider adding / or \\ for file paths)

The default param of " " is scattered throughout because this is the word delimiter used in the English language.
2019-12-12 15:22:12 -08:00

1647 lines
65 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "..\..\inc\consoletaeftemplates.hpp"
#include "CommonState.hpp"
#include "uiaTextRange.hpp"
#include "../../../buffer/out/textBuffer.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace Microsoft::WRL;
using namespace Microsoft::Console::Interactivity::Win32;
// 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 IRawElementProviderSimple
{
public:
// IUnknown methods
IFACEMETHODIMP_(ULONG)
AddRef()
{
return 1;
}
IFACEMETHODIMP_(ULONG)
Release()
{
return 1;
}
IFACEMETHODIMP QueryInterface(_In_ REFIID /*riid*/,
_COM_Outptr_result_maybenull_ void** /*ppInterface*/)
{
return E_NOTIMPL;
};
// IRawElementProviderSimple methods
IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* /*pOptions*/)
{
return E_NOTIMPL;
}
IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID /*iid*/,
_COM_Outptr_result_maybenull_ IUnknown** /*ppInterface*/)
{
return E_NOTIMPL;
}
IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID /*idProp*/,
_Out_ VARIANT* /*pVariant*/)
{
return E_NOTIMPL;
}
IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** /*ppProvider*/)
{
return E_NOTIMPL;
}
};
class UiaTextRangeTests
{
TEST_CLASS(UiaTextRangeTests);
CommonState* _state;
DummyElementProvider _dummyProvider;
SCREEN_INFORMATION* _pScreenInfo;
TextBuffer* _pTextBuffer;
UiaTextRange* _range;
IUiaData* _pUiaData;
TEST_METHOD_SETUP(MethodSetup)
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
// set up common state
_state = new CommonState();
_state->PrepareGlobalFont();
_state->PrepareGlobalScreenBuffer();
_state->PrepareNewTextBufferInfo();
// set up pointers
_pScreenInfo = &gci.GetActiveOutputBuffer();
_pTextBuffer = &_pScreenInfo->GetTextBuffer();
_pUiaData = &gci.renderData;
// fill text buffer with text
for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i)
{
ROW& row = _pTextBuffer->GetRowByOffset(i);
auto& charRow = row.GetCharRow();
for (auto& cell : charRow)
{
cell.Char() = L'a';
}
}
// set up default range
Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&_range,
_pUiaData,
&_dummyProvider,
0,
0,
false);
return true;
}
TEST_METHOD_CLEANUP(MethodCleanup)
{
_state->CleanupNewTextBufferInfo();
_state->CleanupGlobalScreenBuffer();
_state->CleanupGlobalFont();
delete _state;
delete _range;
_pScreenInfo = nullptr;
_pTextBuffer = nullptr;
_pUiaData = nullptr;
return true;
}
const size_t _getRowWidth() const
{
const CharRow& charRow = _pTextBuffer->_GetFirstRow().GetCharRow();
return charRow.MeasureRight() - charRow.MeasureLeft();
}
TEST_METHOD(DegenerateRangesDetected)
{
// make a degenerate range and verify that it reports degenerate
Microsoft::WRL::ComPtr<UiaTextRange> degenerate;
Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&degenerate,
_pUiaData,
&_dummyProvider,
20,
19,
true);
VERIFY_IS_TRUE(degenerate->IsDegenerate());
VERIFY_ARE_EQUAL(0u, degenerate->_rowCountInRange(_pUiaData));
VERIFY_ARE_EQUAL(degenerate->_start, degenerate->_end);
// make a non-degenerate range and verify that it reports as such
Microsoft::WRL::ComPtr<UiaTextRange> notDegenerate1;
Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&notDegenerate1,
_pUiaData,
&_dummyProvider,
20,
20,
false);
VERIFY_IS_FALSE(notDegenerate1->IsDegenerate());
VERIFY_ARE_EQUAL(1u, notDegenerate1->_rowCountInRange(_pUiaData));
}
TEST_METHOD(CanCheckIfScreenInfoRowIsInViewport)
{
// check a viewport that's one line tall
SMALL_RECT viewport;
viewport.Top = 0;
viewport.Bottom = 0;
VERIFY_IS_TRUE(_range->_isScreenInfoRowInViewport(0, viewport));
VERIFY_IS_FALSE(_range->_isScreenInfoRowInViewport(1, viewport));
// check a slightly larger viewport
viewport.Bottom = 5;
for (auto i = 0; i <= viewport.Bottom; ++i)
{
VERIFY_IS_TRUE(_range->_isScreenInfoRowInViewport(i, viewport),
NoThrowString().Format(L"%d should be in viewport", i));
}
VERIFY_IS_FALSE(_range->_isScreenInfoRowInViewport(viewport.Bottom + 1, viewport));
}
TEST_METHOD(CanTranslateScreenInfoRowToViewport)
{
const int totalRows = _pTextBuffer->TotalRowCount();
SMALL_RECT viewport;
viewport.Top = 0;
viewport.Bottom = 10;
std::vector<std::pair<int, int>> viewportSizes = {
{ 0, 10 }, // viewport at top
{ 2, 10 }, // shifted viewport
{ totalRows - 5, totalRows + 3 } // viewport with 0th row
};
for (auto it = viewportSizes.begin(); it != viewportSizes.end(); ++it)
{
viewport.Top = static_cast<SHORT>(it->first);
viewport.Bottom = static_cast<SHORT>(it->second);
for (int i = viewport.Top; _range->_isScreenInfoRowInViewport(i, viewport); ++i)
{
VERIFY_ARE_EQUAL(i - viewport.Top, _range->_screenInfoRowToViewportRow(i, viewport));
}
}
// ScreenInfoRows that are above the viewport return a
// negative value
viewport.Top = 5;
viewport.Bottom = 10;
VERIFY_ARE_EQUAL(-1, _range->_screenInfoRowToViewportRow(4, viewport));
VERIFY_ARE_EQUAL(-2, _range->_screenInfoRowToViewportRow(3, viewport));
}
TEST_METHOD(CanTranslateEndpointToTextBufferRow)
{
const auto rowWidth = _getRowWidth();
for (auto i = 0; i < 300; ++i)
{
VERIFY_ARE_EQUAL(i / rowWidth, _range->_endpointToTextBufferRow(_pUiaData, i));
}
}
TEST_METHOD(CanTranslateTextBufferRowToEndpoint)
{
const auto rowWidth = _getRowWidth();
for (unsigned int i = 0; i < 5; ++i)
{
VERIFY_ARE_EQUAL(i * rowWidth, _range->_textBufferRowToEndpoint(_pUiaData, i));
// make sure that the translation is reversible
VERIFY_ARE_EQUAL(i, _range->_endpointToTextBufferRow(_pUiaData, _range->_textBufferRowToEndpoint(_pUiaData, i)));
}
}
TEST_METHOD(CanTranslateTextBufferRowToScreenInfoRow)
{
const auto rowWidth = _getRowWidth();
for (unsigned int i = 0; i < 5; ++i)
{
VERIFY_ARE_EQUAL(i, _range->_textBufferRowToScreenInfoRow(_pUiaData, _range->_screenInfoRowToTextBufferRow(_pUiaData, i)));
}
}
TEST_METHOD(CanTranslateEndpointToColumn)
{
const auto rowWidth = _getRowWidth();
for (auto i = 0; i < 300; ++i)
{
const auto column = i % rowWidth;
VERIFY_ARE_EQUAL(column, _range->_endpointToColumn(_pUiaData, i));
}
}
TEST_METHOD(CanGetTotalRows)
{
const auto totalRows = _pTextBuffer->TotalRowCount();
VERIFY_ARE_EQUAL(totalRows,
_range->_getTotalRows(_pUiaData));
}
TEST_METHOD(CanGetRowWidth)
{
const auto rowWidth = _getRowWidth();
VERIFY_ARE_EQUAL(rowWidth, _range->_getRowWidth(_pUiaData));
}
TEST_METHOD(CanNormalizeRow)
{
const int totalRows = _pTextBuffer->TotalRowCount();
std::vector<std::pair<unsigned int, unsigned int>> rowMappings = {
{ 0, 0 },
{ totalRows / 2, totalRows / 2 },
{ totalRows - 1, totalRows - 1 },
{ totalRows, 0 },
{ totalRows + 1, 1 },
{ -1, totalRows - 1 }
};
for (auto it = rowMappings.begin(); it != rowMappings.end(); ++it)
{
VERIFY_ARE_EQUAL(static_cast<int>(it->second), _range->_normalizeRow(_pUiaData, it->first));
}
}
TEST_METHOD(CanGetViewportHeight)
{
SMALL_RECT viewport;
viewport.Top = 0;
viewport.Bottom = 0;
// Viewports are inclusive, so Top == Bottom really means 1 row
VERIFY_ARE_EQUAL(1u, _range->_getViewportHeight(viewport));
// make the viewport 10 rows tall
viewport.Top = 3;
viewport.Bottom = 12;
VERIFY_ARE_EQUAL(10u, _range->_getViewportHeight(viewport));
}
TEST_METHOD(CanGetViewportWidth)
{
SMALL_RECT viewport;
viewport.Left = 0;
viewport.Right = 0;
// Viewports are inclusive, Left == Right is really 1 column
VERIFY_ARE_EQUAL(1u, _range->_getViewportWidth(viewport));
// test a more normal size
viewport.Right = 300;
VERIFY_ARE_EQUAL(viewport.Right + 1u, _range->_getViewportWidth(viewport));
}
TEST_METHOD(CanCompareScreenCoords)
{
const std::vector<std::tuple<ScreenInfoRow, Column, ScreenInfoRow, Column, int>> testData = {
{ 0, 0, 0, 0, 0 },
{ 5, 0, 5, 0, 0 },
{ 2, 3, 2, 3, 0 },
{ 0, 6, 0, 6, 0 },
{ 1, 5, 2, 5, -1 },
{ 5, 4, 7, 3, -1 },
{ 3, 4, 3, 5, -1 },
{ 2, 0, 1, 9, 1 },
{ 4, 5, 4, 3, 1 }
};
for (auto data : testData)
{
VERIFY_ARE_EQUAL(std::get<4>(data),
UiaTextRange::_compareScreenCoords(_pUiaData,
std::get<0>(data),
std::get<1>(data),
std::get<2>(data),
std::get<3>(data)));
}
}
TEST_METHOD(CanMoveByCharacter)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
Endpoint, // start
Endpoint // end
>> testData =
{
{
L"can't move backward from (0, 0)",
{
0, 0,
0, 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
0u,
0u
},
{
L"can move backward within a row",
{
0, 1,
0, 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
0u,
0u
},
{
L"can move forward in a row",
{
2, 1,
4, 5,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 2) + 6,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 2) + 6
},
{
L"can't move past the last column in the last row",
{
bottomRow, lastColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex
},
{
L"can move to a new row when necessary when moving forward",
{
topRow, lastColumnIndex,
topRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 4,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 4
},
{
L"can move to a new row when necessary when moving backward",
{
topRow + 1, firstColumnIndex,
topRow + 1, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-5,
-5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + (lastColumnIndex - 4),
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + (lastColumnIndex - 4)
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
int amountMoved;
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByCharacter(_pUiaData,
std::get<2>(data),
std::get<1>(data),
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
}
}
TEST_METHOD(CanMoveByWord_EmptyBuffer)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
Endpoint, // start
Endpoint // end
>> testData =
{
{
L"can't move backward from (0, 0)",
{
0, 0,
0, 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
0u,
lastColumnIndex
},
{
L"can move backward within a row",
{
0, 1,
0, 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
0u,
lastColumnIndex
},
{
L"can move forward in a row",
{
2, 1,
4, 5,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 8),
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 8) + lastColumnIndex
},
{
L"can't move past the last column in the last row",
{
bottomRow, lastColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow),
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex
},
{
L"can move to a new row when necessary when moving forward",
{
topRow, lastColumnIndex,
topRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5),
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex
},
{
L"can move to a new row when necessary when moving backward",
{
topRow + 1, firstColumnIndex,
topRow + 1, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-5,
-2,
0u,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
int amountMoved;
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByWord(_pUiaData,
std::get<2>(data),
std::get<1>(data),
L"",
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
}
}
TEST_METHOD(CanMoveByWord_NonEmptyBuffer)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
const std::wstring_view text[] = {
L"word1 word2 word3",
L"word4 word5 word6"
};
for (auto i = 0; i < 2; i++)
{
_pTextBuffer->WriteLine(text[i], { 0, gsl::narrow<SHORT>(i) });
}
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
Endpoint, // start
Endpoint // end
>> testData =
{
{
L"move backwards on the word by (0,0)",
{
0, 1,
0, 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
0u,
6
},
{
L"get next word while on first word",
{
0, 0,
0, 0,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
0,
6
},
{
L"get next word twice while on first word",
{
0, 0,
0, 0,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
2,
2,
7,
14
},
{
L"move forward to next row with word",
{
topRow, lastColumnIndex,
topRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1),
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 6
},
{
L"move backwards to previous row with word",
{
topRow + 1, firstColumnIndex,
topRow + 1, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 15,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
int amountMoved;
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByWord(_pUiaData,
std::get<2>(data),
std::get<1>(data),
L"",
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
}
}
TEST_METHOD(CanMoveByLine)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
Endpoint, // start
Endpoint // end
>> testData =
{
{
L"can't move backward from top row",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-4,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
},
{
L"can move forward from top row",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
4,
4,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 4) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 4) + lastColumnIndex
},
{
L"can't move forward from bottom row",
{
bottomRow, firstColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
3,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex
},
{
L"can move backward from bottom row",
{
bottomRow, firstColumnIndex,
bottomRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-3,
-3,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow - 3) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow - 3) + lastColumnIndex
},
{
L"can't move backward when part of the top row is in the range",
{
topRow, firstColumnIndex + 5,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex + 5,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex
},
{
L"can't move forward when part of the bottom row is in the range",
{
bottomRow, firstColumnIndex,
bottomRow, firstColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
0,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
int amountMoved;
std::pair<Endpoint, Endpoint> newEndpoints = UiaTextRange::_moveByLine(_pUiaData,
std::get<2>(data),
std::get<1>(data),
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<4>(data), newEndpoints.first);
VERIFY_ARE_EQUAL(std::get<5>(data), newEndpoints.second);
}
}
TEST_METHOD(CanMoveEndpointByUnitCharacter)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
TextPatternRangeEndpoint, // endpoint to move
Endpoint, // start
Endpoint, // end
bool // degenerate
>> testData =
{
{
L"can't move _start past the beginning of the document when _start is positioned at the beginning",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can partially move _start to the begining of the document when it is closer than the move count requested",
{
topRow, firstColumnIndex + 3,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-5,
-3,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can't move _end past the begining of the document",
{
topRow, firstColumnIndex,
topRow, firstColumnIndex + 4,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-5,
-4,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
false
},
{
L"_start follows _end when passed during movement",
{
topRow, firstColumnIndex + 5,
topRow, firstColumnIndex + 10,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-7,
-7,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 3,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 3,
true
},
{
L"can't move _end past the beginning of the document when _end is positioned at the end",
{
bottomRow, firstColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can partially move _end to the end of the document when it is closer than the move count requested",
{
topRow, firstColumnIndex,
bottomRow, lastColumnIndex - 3,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
3,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can't move _start past the end of the document",
{
bottomRow, lastColumnIndex - 4,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
4,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"_end follows _start when passed during movement",
{
topRow, firstColumnIndex + 5,
topRow, firstColumnIndex + 10,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
7,
7,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 12,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 12,
true
},
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
std::tuple<Endpoint, Endpoint, bool> result;
int amountMoved;
result = UiaTextRange::_moveEndpointByUnitCharacter(_pUiaData,
std::get<2>(data),
std::get<4>(data),
std::get<1>(data),
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<5>(data), std::get<0>(result));
VERIFY_ARE_EQUAL(std::get<6>(data), std::get<1>(result));
VERIFY_ARE_EQUAL(std::get<7>(data), std::get<2>(result));
}
}
TEST_METHOD(CanMoveEndpointByUnitWord)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
const std::wstring_view text[] = {
L"word1 word2 word3",
L"word4 word5 word6"
};
for (auto i = 0; i < 2; i++)
{
_pTextBuffer->WriteLine(text[i], { 0, gsl::narrow<SHORT>(i) });
}
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
TextPatternRangeEndpoint, // endpoint to move
Endpoint, // start
Endpoint, // end
bool // degenerate
>> testData =
{
{
L"can't move _start past the beginning of the document when _start is positioned at the beginning",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can partially move _start to the begining of the document when it is closer than the move count requested",
{
topRow, firstColumnIndex + 15,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-5,
-2,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can't move _end past the begining of the document",
{
topRow, firstColumnIndex,
topRow, firstColumnIndex + 2,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-2,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
false
},
{
L"_start follows _end when passed during movement",
{
topRow + 1, firstColumnIndex + 2,
topRow + 1, firstColumnIndex + 10,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-4,
-4,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 6,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 6,
true
},
{
L"can't move _end past the beginning of the document when _end is positioned at the end",
{
bottomRow, firstColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can partially move _end to the end of the document when it is closer than the move count requested",
{
topRow, firstColumnIndex,
bottomRow, lastColumnIndex - 3,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can't move _start past the end of the document",
{
bottomRow, lastColumnIndex - 4,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
5,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"_end follows _start when passed during movement",
{
topRow, firstColumnIndex,
topRow, firstColumnIndex + 3,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
2,
2,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow)+15,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow)+15,
true
},
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
std::tuple<Endpoint, Endpoint, bool> result;
int amountMoved;
result = UiaTextRange::_moveEndpointByUnitWord(_pUiaData,
std::get<2>(data),
std::get<4>(data),
std::get<1>(data),
L" ",
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<5>(data), std::get<0>(result));
VERIFY_ARE_EQUAL(std::get<6>(data), std::get<1>(result));
VERIFY_ARE_EQUAL(std::get<7>(data), std::get<2>(result));
}
}
TEST_METHOD(CanMoveEndpointByUnitLine)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
TextPatternRangeEndpoint, // endpoint to move
Endpoint, // start
Endpoint, // end
bool // degenerate
>> testData =
{
{
L"can move _end forward without affecting _start",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + lastColumnIndex,
false
},
{
L"can move _end backward without affecting _start",
{
topRow + 1, firstColumnIndex,
topRow + 5, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-2,
-2,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + lastColumnIndex,
false
},
{
L"can move _start forward without affecting _end",
{
topRow + 1, firstColumnIndex,
topRow + 5, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
2,
2,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex,
false
},
{
L"can move _start backward without affecting _end",
{
topRow + 2, firstColumnIndex,
topRow + 5, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex,
false
},
{
L"can move _start backwards when it's already on the top row",
{
topRow, lastColumnIndex,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can't move _start backwards when it's at the start of the document already",
{
topRow, firstColumnIndex,
topRow, lastColumnIndex,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex,
false
},
{
L"can move _end forwards when it's on the bottom row",
{
topRow, firstColumnIndex,
bottomRow, lastColumnIndex - 3,
bottomRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can't move _end forwards when it's at the end of the document already",
{
topRow, firstColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"moving _start forward when it's already on the bottom row creates a degenerate range at the document end",
{
bottomRow, firstColumnIndex,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
true
},
{
L"moving _end backward when it's already on the top row creates a degenerate range at the document start",
{
topRow, firstColumnIndex + 4,
topRow, lastColumnIndex - 5,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
true
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
std::tuple<Endpoint, Endpoint, bool> result;
int amountMoved;
result = UiaTextRange::_moveEndpointByUnitLine(_pUiaData,
std::get<2>(data),
std::get<4>(data),
std::get<1>(data),
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<5>(data), std::get<0>(result));
VERIFY_ARE_EQUAL(std::get<6>(data), std::get<1>(result));
VERIFY_ARE_EQUAL(std::get<7>(data), std::get<2>(result));
}
}
TEST_METHOD(CanMoveEndpointByUnitDocument)
{
const Column firstColumnIndex = 0;
const Column lastColumnIndex = _pScreenInfo->GetBufferSize().Width() - 1;
const ScreenInfoRow topRow = 0;
const ScreenInfoRow bottomRow = _pTextBuffer->TotalRowCount() - 1;
// clang-format off
const std::vector<std::tuple<std::wstring,
UiaTextRange::MoveState,
int, // amount to move
int, // amount actually moved
TextPatternRangeEndpoint, // endpoint to move
Endpoint, // start
Endpoint, // end
bool // degenerate
>> testData =
{
{
L"can move _end forward to end of document without affecting _start",
{
topRow, firstColumnIndex + 4,
topRow, firstColumnIndex + 4,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex + 4,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can move _start backward to end of document without affect _end",
{
topRow, firstColumnIndex + 4,
topRow, firstColumnIndex + 4,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 4,
false
},
{
L"can't move _end forward when it's already at the end of the document",
{
topRow + 3, firstColumnIndex + 2,
bottomRow, lastColumnIndex,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + firstColumnIndex + 2,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
false
},
{
L"can't move _start backward when it's already at the start of the document",
{
topRow, firstColumnIndex,
topRow + 5, firstColumnIndex + 6,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
0,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + 6,
false
},
{
L"moving _end backward creates degenerate range at start of document",
{
topRow + 5, firstColumnIndex + 2,
topRow + 5, firstColumnIndex + 6,
topRow,
lastColumnIndex,
firstColumnIndex,
UiaTextRange::MovementIncrement::Backward,
UiaTextRange::MovementDirection::Backward
},
-1,
-1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_End,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex,
true
},
{
L"moving _start forward creates degenerate range at end of document",
{
topRow + 5, firstColumnIndex + 2,
topRow + 5, firstColumnIndex + 6,
bottomRow,
firstColumnIndex,
lastColumnIndex,
UiaTextRange::MovementIncrement::Forward,
UiaTextRange::MovementDirection::Forward
},
1,
1,
TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex,
true
}
};
// clang-format on
for (auto data : testData)
{
Log::Comment(std::get<0>(data).c_str());
std::tuple<Endpoint, Endpoint, bool> result;
int amountMoved;
result = UiaTextRange::_moveEndpointByUnitDocument(_pUiaData,
std::get<2>(data),
std::get<4>(data),
std::get<1>(data),
&amountMoved);
VERIFY_ARE_EQUAL(std::get<3>(data), amountMoved);
VERIFY_ARE_EQUAL(std::get<5>(data), std::get<0>(result));
VERIFY_ARE_EQUAL(std::get<6>(data), std::get<1>(result));
VERIFY_ARE_EQUAL(std::get<7>(data), std::get<2>(result));
}
}
};