terminal/src/host/ut_host/ViewportTests.cpp

1052 lines
35 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 "../../types/inc/Viewport.hpp"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using Viewport = Microsoft::Console::Types::Viewport;
class ViewportTests
{
TEST_CLASS(ViewportTests);
TEST_METHOD(CreateEmpty)
{
const auto v = Viewport::Empty();
VERIFY_ARE_EQUAL(0, v.Left());
VERIFY_ARE_EQUAL(-1, v.RightInclusive());
VERIFY_ARE_EQUAL(0, v.RightExclusive());
VERIFY_ARE_EQUAL(0, v.Top());
VERIFY_ARE_EQUAL(-1, v.BottomInclusive());
VERIFY_ARE_EQUAL(0, v.BottomExclusive());
VERIFY_ARE_EQUAL(0, v.Height());
VERIFY_ARE_EQUAL(0, v.Width());
VERIFY_ARE_EQUAL(COORD({ 0 }), v.Origin());
VERIFY_ARE_EQUAL(COORD({ 0 }), v.Dimensions());
}
TEST_METHOD(CreateFromInclusive)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
COORD origin;
origin.X = rect.Left;
origin.Y = rect.Top;
COORD dimensions;
dimensions.X = rect.Right - rect.Left + 1;
dimensions.Y = rect.Bottom - rect.Top + 1;
const auto v = Viewport::FromInclusive(rect);
VERIFY_ARE_EQUAL(rect.Left, v.Left());
VERIFY_ARE_EQUAL(rect.Right, v.RightInclusive());
VERIFY_ARE_EQUAL(rect.Right + 1, v.RightExclusive());
VERIFY_ARE_EQUAL(rect.Top, v.Top());
VERIFY_ARE_EQUAL(rect.Bottom, v.BottomInclusive());
VERIFY_ARE_EQUAL(rect.Bottom + 1, v.BottomExclusive());
VERIFY_ARE_EQUAL(dimensions.Y, v.Height());
VERIFY_ARE_EQUAL(dimensions.X, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
VERIFY_ARE_EQUAL(dimensions, v.Dimensions());
}
TEST_METHOD(CreateFromExclusive)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
COORD origin;
origin.X = rect.Left;
origin.Y = rect.Top;
COORD dimensions;
dimensions.X = rect.Right - rect.Left;
dimensions.Y = rect.Bottom - rect.Top;
const auto v = Viewport::FromExclusive(rect);
VERIFY_ARE_EQUAL(rect.Left, v.Left());
VERIFY_ARE_EQUAL(rect.Right - 1, v.RightInclusive());
VERIFY_ARE_EQUAL(rect.Right, v.RightExclusive());
VERIFY_ARE_EQUAL(rect.Top, v.Top());
VERIFY_ARE_EQUAL(rect.Bottom - 1, v.BottomInclusive());
VERIFY_ARE_EQUAL(rect.Bottom, v.BottomExclusive());
VERIFY_ARE_EQUAL(dimensions.Y, v.Height());
VERIFY_ARE_EQUAL(dimensions.X, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
VERIFY_ARE_EQUAL(dimensions, v.Dimensions());
}
TEST_METHOD(CreateFromDimensionsWidthHeight)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
COORD origin;
origin.X = rect.Left;
origin.Y = rect.Top;
COORD dimensions;
dimensions.X = rect.Right - rect.Left + 1;
dimensions.Y = rect.Bottom - rect.Top + 1;
const auto v = Viewport::FromDimensions(origin, dimensions.X, dimensions.Y);
VERIFY_ARE_EQUAL(rect.Left, v.Left());
VERIFY_ARE_EQUAL(rect.Right, v.RightInclusive());
VERIFY_ARE_EQUAL(rect.Right + 1, v.RightExclusive());
VERIFY_ARE_EQUAL(rect.Top, v.Top());
VERIFY_ARE_EQUAL(rect.Bottom, v.BottomInclusive());
VERIFY_ARE_EQUAL(rect.Bottom + 1, v.BottomExclusive());
VERIFY_ARE_EQUAL(dimensions.Y, v.Height());
VERIFY_ARE_EQUAL(dimensions.X, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
VERIFY_ARE_EQUAL(dimensions, v.Dimensions());
}
TEST_METHOD(CreateFromDimensions)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
COORD origin;
origin.X = rect.Left;
origin.Y = rect.Top;
COORD dimensions;
dimensions.X = rect.Right - rect.Left + 1;
dimensions.Y = rect.Bottom - rect.Top + 1;
const auto v = Viewport::FromDimensions(origin, dimensions);
VERIFY_ARE_EQUAL(rect.Left, v.Left());
VERIFY_ARE_EQUAL(rect.Right, v.RightInclusive());
VERIFY_ARE_EQUAL(rect.Right + 1, v.RightExclusive());
VERIFY_ARE_EQUAL(rect.Top, v.Top());
VERIFY_ARE_EQUAL(rect.Bottom, v.BottomInclusive());
VERIFY_ARE_EQUAL(rect.Bottom + 1, v.BottomExclusive());
VERIFY_ARE_EQUAL(dimensions.Y, v.Height());
VERIFY_ARE_EQUAL(dimensions.X, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
VERIFY_ARE_EQUAL(dimensions, v.Dimensions());
}
TEST_METHOD(CreateFromDimensionsNoOrigin)
{
SMALL_RECT rect;
rect.Top = 0;
rect.Left = 0;
rect.Bottom = 5;
rect.Right = 20;
COORD origin;
origin.X = rect.Left;
origin.Y = rect.Top;
COORD dimensions;
dimensions.X = rect.Right - rect.Left + 1;
dimensions.Y = rect.Bottom - rect.Top + 1;
const auto v = Viewport::FromDimensions(dimensions);
VERIFY_ARE_EQUAL(rect.Left, v.Left());
VERIFY_ARE_EQUAL(rect.Right, v.RightInclusive());
VERIFY_ARE_EQUAL(rect.Right + 1, v.RightExclusive());
VERIFY_ARE_EQUAL(rect.Top, v.Top());
VERIFY_ARE_EQUAL(rect.Bottom, v.BottomInclusive());
VERIFY_ARE_EQUAL(rect.Bottom + 1, v.BottomExclusive());
VERIFY_ARE_EQUAL(dimensions.Y, v.Height());
VERIFY_ARE_EQUAL(dimensions.X, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
VERIFY_ARE_EQUAL(dimensions, v.Dimensions());
}
TEST_METHOD(CreateFromCoord)
{
COORD origin;
origin.X = 12;
origin.Y = 24;
const auto v = Viewport::FromCoord(origin);
VERIFY_ARE_EQUAL(origin.X, v.Left());
VERIFY_ARE_EQUAL(origin.X, v.RightInclusive());
VERIFY_ARE_EQUAL(origin.X + 1, v.RightExclusive());
VERIFY_ARE_EQUAL(origin.Y, v.Top());
VERIFY_ARE_EQUAL(origin.Y, v.BottomInclusive());
VERIFY_ARE_EQUAL(origin.Y + 1, v.BottomExclusive());
VERIFY_ARE_EQUAL(1, v.Height());
VERIFY_ARE_EQUAL(1, v.Width());
VERIFY_ARE_EQUAL(origin, v.Origin());
// clang-format off
VERIFY_ARE_EQUAL(COORD({ 1, 1, }), v.Dimensions());
// clang-format on
}
TEST_METHOD(ToRect)
{
COORD origin;
origin.X = 2;
origin.Y = 4;
COORD dimensions;
dimensions.X = 10;
dimensions.Y = 20;
const auto v = Viewport::FromDimensions(origin, dimensions);
const RECT rc = v.ToRect();
const SMALL_RECT exclusive = v.ToExclusive();
VERIFY_ARE_EQUAL(exclusive.Left, v.Left());
VERIFY_ARE_EQUAL(rc.left, v.Left());
VERIFY_ARE_EQUAL(exclusive.Top, v.Top());
VERIFY_ARE_EQUAL(rc.top, v.Top());
VERIFY_ARE_EQUAL(exclusive.Right, v.RightExclusive());
VERIFY_ARE_EQUAL(rc.right, v.RightExclusive());
VERIFY_ARE_EQUAL(exclusive.Bottom, v.BottomExclusive());
VERIFY_ARE_EQUAL(rc.bottom, v.BottomExclusive());
}
TEST_METHOD(IsInBoundsCoord)
{
SMALL_RECT r;
r.Top = 3;
r.Bottom = 5;
r.Left = 10;
r.Right = 20;
const auto v = Viewport::FromInclusive(r);
COORD c;
c.X = r.Left;
c.Y = r.Top;
VERIFY_IS_TRUE(v.IsInBounds(c), L"Top left corner in bounds.");
c.Y = r.Bottom;
VERIFY_IS_TRUE(v.IsInBounds(c), L"Bottom left corner in bounds.");
c.X = r.Right;
VERIFY_IS_TRUE(v.IsInBounds(c), L"Bottom right corner in bounds.");
c.Y = r.Top;
VERIFY_IS_TRUE(v.IsInBounds(c), L"Top right corner in bounds.");
c.X++;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One right out the top right is out of bounds.");
c.X--;
c.Y--;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One up out the top right is out of bounds.");
c.X = r.Left;
c.Y = r.Top;
c.X--;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One left out the top left is out of bounds.");
c.X++;
c.Y--;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One up out the top left is out of bounds.");
c.X = r.Left;
c.Y = r.Bottom;
c.X--;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One left out the bottom left is out of bounds.");
c.X++;
c.Y++;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One down out the bottom left is out of bounds.");
c.X = r.Right;
c.Y = r.Bottom;
c.X++;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One right out the bottom right is out of bounds.");
c.X--;
c.Y++;
VERIFY_IS_FALSE(v.IsInBounds(c), L"One down out the bottom right is out of bounds.");
}
TEST_METHOD(IsInBoundsViewport)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
const auto original = rect;
const auto view = Viewport::FromInclusive(rect);
auto test = Viewport::FromInclusive(rect);
VERIFY_IS_TRUE(view.IsInBounds(test), L"Same size/position viewport is in bounds.");
rect.Top++;
rect.Bottom--;
rect.Left++;
rect.Right--;
test = Viewport::FromInclusive(rect);
VERIFY_IS_TRUE(view.IsInBounds(test), L"Viewport inscribed inside viewport is in bounds.");
rect = original;
rect.Top--;
test = Viewport::FromInclusive(rect);
VERIFY_IS_FALSE(view.IsInBounds(test), L"Viewport that is one taller upwards is out of bounds.");
rect = original;
rect.Bottom++;
test = Viewport::FromInclusive(rect);
VERIFY_IS_FALSE(view.IsInBounds(test), L"Viewport that is one taller downwards is out of bounds.");
rect = original;
rect.Left--;
test = Viewport::FromInclusive(rect);
VERIFY_IS_FALSE(view.IsInBounds(test), L"Viewport that is one wider leftwards is out of bounds.");
rect = original;
rect.Right++;
test = Viewport::FromInclusive(rect);
VERIFY_IS_FALSE(view.IsInBounds(test), L"Viewport that is one wider rightwards is out of bounds.");
rect = original;
rect.Left++;
rect.Right++;
rect.Top++;
rect.Bottom++;
test = Viewport::FromInclusive(rect);
VERIFY_IS_FALSE(view.IsInBounds(test), L"Viewport offset at the origin but same size is out of bounds.");
}
TEST_METHOD(ClampCoord)
{
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
const auto view = Viewport::FromInclusive(rect);
COORD pos;
pos.X = rect.Left;
pos.Y = rect.Top;
auto before = pos;
view.Clamp(pos);
VERIFY_ARE_EQUAL(before, pos, L"Verify clamp did nothing for position in top left corner.");
pos.X = rect.Left;
pos.Y = rect.Bottom;
before = pos;
view.Clamp(pos);
VERIFY_ARE_EQUAL(before, pos, L"Verify clamp did nothing for position in bottom left corner.");
pos.X = rect.Right;
pos.Y = rect.Bottom;
before = pos;
view.Clamp(pos);
VERIFY_ARE_EQUAL(before, pos, L"Verify clamp did nothing for position in bottom right corner.");
pos.X = rect.Right;
pos.Y = rect.Top;
before = pos;
view.Clamp(pos);
VERIFY_ARE_EQUAL(before, pos, L"Verify clamp did nothing for position in top right corner.");
COORD expected;
expected.X = rect.Right;
expected.Y = rect.Top;
pos = expected;
pos.X++;
pos.Y--;
before = pos;
view.Clamp(pos);
VERIFY_ARE_NOT_EQUAL(before, pos, L"Verify clamp modified position out the top right corner back.");
VERIFY_ARE_EQUAL(expected, pos, L"Verify position was clamped into the top right corner.");
expected.X = rect.Left;
expected.Y = rect.Top;
pos = expected;
pos.X--;
pos.Y--;
before = pos;
view.Clamp(pos);
VERIFY_ARE_NOT_EQUAL(before, pos, L"Verify clamp modified position out the top left corner back.");
VERIFY_ARE_EQUAL(expected, pos, L"Verify position was clamped into the top left corner.");
expected.X = rect.Left;
expected.Y = rect.Bottom;
pos = expected;
pos.X--;
pos.Y++;
before = pos;
view.Clamp(pos);
VERIFY_ARE_NOT_EQUAL(before, pos, L"Verify clamp modified position out the bottom left corner back.");
VERIFY_ARE_EQUAL(expected, pos, L"Verify position was clamped into the bottom left corner.");
expected.X = rect.Right;
expected.Y = rect.Bottom;
pos = expected;
pos.X++;
pos.Y++;
before = pos;
view.Clamp(pos);
VERIFY_ARE_NOT_EQUAL(before, pos, L"Verify clamp modified position out the bottom right corner back.");
VERIFY_ARE_EQUAL(expected, pos, L"Verify position was clamped into the bottom right corner.");
Viewport invalidView = Viewport::Empty();
VERIFY_THROWS_SPECIFIC(invalidView.Clamp(pos),
wil::ResultException,
[](wil::ResultException& e) { return e.GetErrorCode() == E_NOT_VALID_STATE; });
}
TEST_METHOD(ClampViewport)
{
// Create the rectangle/view we will clamp to.
SMALL_RECT rect;
rect.Top = 3;
rect.Bottom = 5;
rect.Left = 10;
rect.Right = 20;
const auto view = Viewport::FromInclusive(rect);
Log::Comment(L"Make a rectangle that is larger than and fully encompasses our clamping rectangle.");
SMALL_RECT testRect;
testRect.Top = rect.Top - 3;
testRect.Bottom = rect.Bottom + 3;
testRect.Left = rect.Left - 3;
testRect.Right = rect.Right + 3;
auto testView = Viewport::FromInclusive(testRect);
auto actual = view.Clamp(testView);
VERIFY_ARE_EQUAL(view, actual, L"All sides should get reduced down to the size of the given rect.");
Log::Comment(L"Make a rectangle that is fully inscribed inside our clamping rectangle.");
testRect.Top = rect.Top + 1;
testRect.Bottom = rect.Bottom - 1;
testRect.Left = rect.Left + 1;
testRect.Right = rect.Right - 1;
testView = Viewport::FromInclusive(testRect);
actual = view.Clamp(testView);
VERIFY_ARE_EQUAL(testView, actual, L"Verify that nothing changed because this rectangle already sat fully inside the clamping rectangle.");
Log::Comment(L"Craft a rectangle where the left is outside the right, right is outside the left, top is outside the bottom, and bottom is outside the top.");
testRect.Top = rect.Bottom + 10;
testRect.Bottom = rect.Top - 10;
testRect.Left = rect.Right + 10;
testRect.Right = rect.Left - 10;
testView = Viewport::FromInclusive(testRect);
Log::Comment(L"We expect it to be pulled back so each coordinate is in bounds, but the rectangle is still invalid (since left will be > right).");
SMALL_RECT expected;
expected.Top = rect.Bottom;
expected.Bottom = rect.Top;
expected.Left = rect.Right;
expected.Right = rect.Left;
const auto expectedView = Viewport::FromInclusive(expected);
actual = view.Clamp(testView);
VERIFY_ARE_EQUAL(expectedView, actual, L"Every dimension should be pulled just inside the clamping rectangle.");
}
TEST_METHOD(IncrementInBounds)
{
bool success = false;
SMALL_RECT edges;
edges.Left = 10;
edges.Right = 19;
edges.Top = 20;
edges.Bottom = 29;
const auto v = Viewport::FromInclusive(edges);
COORD original;
COORD screen;
// #1 coord inside region
original.X = screen.X = 15;
original.Y = screen.Y = 25;
success = v.IncrementInBounds(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, original.X + 1);
VERIFY_ARE_EQUAL(screen.Y, original.Y);
// #2 coord right edge, not bottom
original.X = screen.X = edges.Right;
original.Y = screen.Y = 25;
success = v.IncrementInBounds(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Left);
VERIFY_ARE_EQUAL(screen.Y, original.Y + 1);
// #3 coord right edge, bottom
original.X = screen.X = edges.Right;
original.Y = screen.Y = edges.Bottom;
success = v.IncrementInBounds(screen);
VERIFY_IS_FALSE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Right);
VERIFY_ARE_EQUAL(screen.Y, edges.Bottom);
}
TEST_METHOD(IncrementInBoundsCircular)
{
bool success = false;
SMALL_RECT edges;
edges.Left = 10;
edges.Right = 19;
edges.Top = 20;
edges.Bottom = 29;
const auto v = Viewport::FromInclusive(edges);
COORD original;
COORD screen;
// #1 coord inside region
original.X = screen.X = 15;
original.Y = screen.Y = 25;
success = v.IncrementInBoundsCircular(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, original.X + 1);
VERIFY_ARE_EQUAL(screen.Y, original.Y);
// #2 coord right edge, not bottom
original.X = screen.X = edges.Right;
original.Y = screen.Y = 25;
success = v.IncrementInBoundsCircular(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Left);
VERIFY_ARE_EQUAL(screen.Y, original.Y + 1);
// #3 coord right edge, bottom
original.X = screen.X = edges.Right;
original.Y = screen.Y = edges.Bottom;
success = v.IncrementInBoundsCircular(screen);
VERIFY_IS_FALSE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Left);
VERIFY_ARE_EQUAL(screen.Y, edges.Top);
}
TEST_METHOD(DecrementInBounds)
{
bool success = false;
SMALL_RECT edges;
edges.Left = 10;
edges.Right = 19;
edges.Top = 20;
edges.Bottom = 29;
const auto v = Viewport::FromInclusive(edges);
COORD original;
COORD screen;
// #1 coord inside region
original.X = screen.X = 15;
original.Y = screen.Y = 25;
success = v.DecrementInBounds(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, original.X - 1);
VERIFY_ARE_EQUAL(screen.Y, original.Y);
// #2 coord left edge, not top
original.X = screen.X = edges.Left;
original.Y = screen.Y = 25;
success = v.DecrementInBounds(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Right);
VERIFY_ARE_EQUAL(screen.Y, original.Y - 1);
// #3 coord left edge, top
original.X = screen.X = edges.Left;
original.Y = screen.Y = edges.Top;
success = v.DecrementInBounds(screen);
VERIFY_IS_FALSE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Left);
VERIFY_ARE_EQUAL(screen.Y, edges.Top);
}
TEST_METHOD(DecrementInBoundsCircular)
{
bool success = false;
SMALL_RECT edges;
edges.Left = 10;
edges.Right = 19;
edges.Top = 20;
edges.Bottom = 29;
const auto v = Viewport::FromInclusive(edges);
COORD original;
COORD screen;
// #1 coord inside region
original.X = screen.X = 15;
original.Y = screen.Y = 25;
success = v.DecrementInBoundsCircular(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, original.X - 1);
VERIFY_ARE_EQUAL(screen.Y, original.Y);
// #2 coord left edge, not top
original.X = screen.X = edges.Left;
original.Y = screen.Y = 25;
success = v.DecrementInBoundsCircular(screen);
VERIFY_IS_TRUE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Right);
VERIFY_ARE_EQUAL(screen.Y, original.Y - 1);
// #3 coord left edge, top
original.X = screen.X = edges.Left;
original.Y = screen.Y = edges.Top;
success = v.DecrementInBoundsCircular(screen);
VERIFY_IS_FALSE(success);
VERIFY_ARE_EQUAL(screen.X, edges.Right);
VERIFY_ARE_EQUAL(screen.Y, edges.Bottom);
}
SHORT RandomShort()
{
SHORT s;
do
{
s = (SHORT)rand() % SHORT_MAX;
} while (s == 0i16);
return s;
}
TEST_METHOD(MoveInBounds)
{
const UINT cTestLoopInstances = 100;
const SHORT sRowWidth = 20;
VERIFY_IS_TRUE(sRowWidth > 0);
// 20x20 box
SMALL_RECT srectEdges;
srectEdges.Top = srectEdges.Left = 0;
srectEdges.Bottom = srectEdges.Right = sRowWidth - 1;
const auto v = Viewport::FromInclusive(srectEdges);
// repeat test
for (UINT i = 0; i < cTestLoopInstances; i++)
{
COORD coordPos;
coordPos.X = RandomShort() % 20;
coordPos.Y = RandomShort() % 20;
SHORT sAddAmount = RandomShort() % (sRowWidth * sRowWidth);
COORD coordFinal;
coordFinal.X = (coordPos.X + sAddAmount) % sRowWidth;
coordFinal.Y = coordPos.Y + ((coordPos.X + sAddAmount) / sRowWidth);
Log::Comment(String().Format(L"Add To Position: (%d, %d) Amount to add: %d", coordPos.Y, coordPos.X, sAddAmount));
// Movement result is expected to be true, unless there's an error.
bool fExpectedResult = true;
// if we've calculated past the final row, then the function will reset to the original position and the output will be false.
if (coordFinal.Y >= sRowWidth)
{
coordFinal = coordPos;
fExpectedResult = false;
}
bool const fActualResult = v.MoveInBounds(sAddAmount, coordPos);
VERIFY_ARE_EQUAL(fExpectedResult, fActualResult);
VERIFY_ARE_EQUAL(coordPos.X, coordFinal.X);
VERIFY_ARE_EQUAL(coordPos.Y, coordFinal.Y);
Log::Comment(String().Format(L"Actual: (%d, %d) Expected: (%d, %d)", coordPos.Y, coordPos.X, coordFinal.Y, coordFinal.X));
}
}
TEST_METHOD(CompareInBounds)
{
SMALL_RECT edges;
edges.Left = 10;
edges.Right = 19;
edges.Top = 20;
edges.Bottom = 29;
const auto v = Viewport::FromInclusive(edges);
COORD first, second;
first.X = 12;
first.Y = 24;
second = first;
second.X += 2;
VERIFY_ARE_EQUAL(-2, v.CompareInBounds(first, second), L"Second and first on same row. Second is right of first.");
VERIFY_ARE_EQUAL(2, v.CompareInBounds(second, first), L"Reverse params, should get opposite direction, same magnitude.");
first.X = edges.Left;
first.Y = 24;
second.X = edges.Right;
second.Y = first.Y - 1;
VERIFY_ARE_EQUAL(1, v.CompareInBounds(first, second), L"Second is up a line at the right edge from first at the line below on the left edge.");
VERIFY_ARE_EQUAL(-1, v.CompareInBounds(second, first), L"Reverse params, should get opposite direction, same magnitude.");
}
TEST_METHOD(Offset)
{
SMALL_RECT edges;
edges.Top = 0;
edges.Left = 0;
edges.Right = 10;
edges.Bottom = 10;
const auto original = Viewport::FromInclusive(edges);
Log::Comment(L"Move down and to the right first.");
COORD adjust = { 7, 2 };
SMALL_RECT expectedEdges;
expectedEdges.Top = edges.Top + adjust.Y;
expectedEdges.Bottom = edges.Bottom + adjust.Y;
expectedEdges.Left = edges.Left + adjust.X;
expectedEdges.Right = edges.Right + adjust.X;
Viewport expected = Viewport::FromInclusive(expectedEdges);
Viewport actual = Viewport::Offset(original, adjust);
VERIFY_ARE_EQUAL(expected, actual);
Log::Comment(L"Now try moving up and to the left.");
adjust = { -3, -5 };
expectedEdges.Top = edges.Top + adjust.Y;
expectedEdges.Bottom = edges.Bottom + adjust.Y;
expectedEdges.Left = edges.Left + adjust.X;
expectedEdges.Right = edges.Right + adjust.X;
expected = Viewport::FromInclusive(expectedEdges);
actual = Viewport::Offset(original, adjust);
VERIFY_ARE_EQUAL(expected, actual);
Log::Comment(L"Now try adding way too much to cause an overflow.");
adjust = { SHORT_MAX, SHORT_MAX };
VERIFY_THROWS_SPECIFIC(const auto vp = Viewport::Offset(original, adjust),
wil::ResultException,
[](wil::ResultException& e) { return e.GetErrorCode() == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); });
}
TEST_METHOD(Union)
{
SMALL_RECT srOne;
srOne.Left = 4;
srOne.Right = 10;
srOne.Top = 6;
srOne.Bottom = 14;
const auto one = Viewport::FromInclusive(srOne);
SMALL_RECT srTwo;
srTwo.Left = 5;
srTwo.Right = 13;
srTwo.Top = 2;
srTwo.Bottom = 10;
const auto two = Viewport::FromInclusive(srTwo);
SMALL_RECT srExpected;
srExpected.Left = srOne.Left < srTwo.Left ? srOne.Left : srTwo.Left;
srExpected.Right = srOne.Right > srTwo.Right ? srOne.Right : srTwo.Right;
srExpected.Top = srOne.Top < srTwo.Top ? srOne.Top : srTwo.Top;
srExpected.Bottom = srOne.Bottom > srTwo.Bottom ? srOne.Bottom : srTwo.Bottom;
const auto expected = Viewport::FromInclusive(srExpected);
const auto actual = Viewport::Union(one, two);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(Intersect)
{
SMALL_RECT srOne;
srOne.Left = 4;
srOne.Right = 10;
srOne.Top = 6;
srOne.Bottom = 14;
const auto one = Viewport::FromInclusive(srOne);
SMALL_RECT srTwo;
srTwo.Left = 5;
srTwo.Right = 13;
srTwo.Top = 2;
srTwo.Bottom = 10;
const auto two = Viewport::FromInclusive(srTwo);
SMALL_RECT srExpected;
srExpected.Left = srOne.Left > srTwo.Left ? srOne.Left : srTwo.Left;
srExpected.Right = srOne.Right < srTwo.Right ? srOne.Right : srTwo.Right;
srExpected.Top = srOne.Top > srTwo.Top ? srOne.Top : srTwo.Top;
srExpected.Bottom = srOne.Bottom < srTwo.Bottom ? srOne.Bottom : srTwo.Bottom;
const auto expected = Viewport::FromInclusive(srExpected);
const auto actual = Viewport::Intersect(one, two);
VERIFY_ARE_EQUAL(expected, actual);
}
TEST_METHOD(SubtractFour)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
SMALL_RECT srRemove;
srRemove.Top = 3;
srRemove.Left = 3;
srRemove.Bottom = 6;
srRemove.Right = 6;
const auto remove = Viewport::FromInclusive(srRemove);
std::vector<Viewport> expected;
// SMALL_RECT constructed as: Left, Top, Right, Bottom
// Top View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srOriginal.Top, srOriginal.Right, srRemove.Top - 1 }));
// Bottom View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srRemove.Bottom + 1, srOriginal.Right, srOriginal.Bottom }));
// Left View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srRemove.Top, srRemove.Left - 1, srRemove.Bottom }));
// Right View
expected.emplace_back(Viewport::FromInclusive({ srRemove.Right + 1, srRemove.Top, srOriginal.Right, srRemove.Bottom }));
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
}
TEST_METHOD(SubtractThree)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
SMALL_RECT srRemove;
srRemove.Top = 3;
srRemove.Left = 3;
srRemove.Bottom = 6;
srRemove.Right = 15;
const auto remove = Viewport::FromInclusive(srRemove);
std::vector<Viewport> expected;
// SMALL_RECT constructed as: Left, Top, Right, Bottom
// Top View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srOriginal.Top, srOriginal.Right, srRemove.Top - 1 }));
// Bottom View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srRemove.Bottom + 1, srOriginal.Right, srOriginal.Bottom }));
// Left View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srRemove.Top, srRemove.Left - 1, srRemove.Bottom }));
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
}
TEST_METHOD(SubtractTwo)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
SMALL_RECT srRemove;
srRemove.Top = 3;
srRemove.Left = 3;
srRemove.Bottom = 15;
srRemove.Right = 15;
const auto remove = Viewport::FromInclusive(srRemove);
std::vector<Viewport> expected;
// SMALL_RECT constructed as: Left, Top, Right, Bottom
// Top View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srOriginal.Top, srOriginal.Right, srRemove.Top - 1 }));
// Left View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srRemove.Top, srRemove.Left - 1, srOriginal.Bottom }));
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
}
TEST_METHOD(SubtractOne)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
SMALL_RECT srRemove;
srRemove.Top = 3;
srRemove.Left = -12;
srRemove.Bottom = 15;
srRemove.Right = 15;
const auto remove = Viewport::FromInclusive(srRemove);
std::vector<Viewport> expected;
// SMALL_RECT constructed as: Left, Top, Right, Bottom
// Top View
expected.emplace_back(Viewport::FromInclusive({ srOriginal.Left, srOriginal.Top, srOriginal.Right, srRemove.Top - 1 }));
const auto foo = expected.cbegin();
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
}
TEST_METHOD(SubtractZero)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
SMALL_RECT srRemove;
srRemove.Top = 12;
srRemove.Left = 12;
srRemove.Bottom = 15;
srRemove.Right = 15;
const auto remove = Viewport::FromInclusive(srRemove);
std::vector<Viewport> expected;
expected.emplace_back(original);
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual");
Log::Comment(L"Now validate that each viewport has the expected area.");
for (size_t i = 0; i < expected.size(); i++)
{
const auto& exp = expected.at(i);
const auto& act = actual.at(i);
VERIFY_ARE_EQUAL(exp, act);
}
}
TEST_METHOD(SubtractSame)
{
SMALL_RECT srOriginal;
srOriginal.Top = 0;
srOriginal.Left = 0;
srOriginal.Bottom = 10;
srOriginal.Right = 10;
const auto original = Viewport::FromInclusive(srOriginal);
const auto remove = original;
const auto actual = Viewport::Subtract(original, remove);
VERIFY_ARE_EQUAL(0u, actual.size(), L"There should be no viewports returned");
}
};