til::size (#4850)
## Summary of the Pull Request Introduces convenience type `til::size` which automatically implements our best practices for size-related types and provides automatic conversions in/out of the relevant types. ## PR Checklist * [x] In support of Differental Rendering #778 * [X] I work here. * [x] Tests added/passed * [x] I'm a core contributor. ## Detailed Description of the Pull Request / Additional comments - Automatically converts in from anything with a X/Y (console `COORD`) or cx/cy (Win32 `SIZE`) - Automatically converts out to `COORD`, `SIZE`, or `D2D1_SIZE_F`. - Constructs from bare integers written into source file - Default constructs to empty - Uses Chromium Math for all basic math operations (+, -, *, /) - Provides equality tests - Adds initial proposal for division-to-ceiling (round up division) that attempts to `ceil` without any floating point math. - Accessors for height/width - Type converting accessors (that use safe conversions and throw) for height/width - Convenience function for area calculation (as that's common with type) and uses safe math to do it. - TAEF/WEX Output and Comparators so they will print very nicely with `VERIFY` and `Log` macros in our testing suite. ## Validation Steps Performed - See automated tests of functionality.
This commit is contained in:
parent
2b6e96a745
commit
57ee5a9d0d
|
@ -6,6 +6,7 @@
|
|||
#include "til/at.h"
|
||||
#include "til/color.h"
|
||||
#include "til/some.h"
|
||||
#include "til/size.h"
|
||||
#include "til/u8u16convert.h"
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
|
|
265
src/inc/til/size.h
Normal file
265
src/inc/til/size.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
class SizeTests;
|
||||
#endif
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
class size
|
||||
{
|
||||
public:
|
||||
constexpr size() noexcept :
|
||||
size(0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
// On 64-bit processors, int and ptrdiff_t are different fundamental types.
|
||||
// On 32-bit processors, they're the same which makes this a double-definition
|
||||
// with the `ptrdiff_t` one below.
|
||||
#if defined(_M_AMD64) || defined(_M_ARM64)
|
||||
constexpr size(int width, int height) noexcept :
|
||||
size(static_cast<ptrdiff_t>(width), static_cast<ptrdiff_t>(height))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
size(size_t width, size_t height)
|
||||
{
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width).AssignIfValid(&_width));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height).AssignIfValid(&_height));
|
||||
}
|
||||
|
||||
constexpr size(ptrdiff_t width, ptrdiff_t height) noexcept :
|
||||
_width(width),
|
||||
_height(height)
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has an X and a Y field that appear convertable to an integer value
|
||||
template<typename TOther>
|
||||
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
|
||||
size(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
|
||||
{
|
||||
}
|
||||
|
||||
// This template will convert to size from anything that has a cx and a cy field that appear convertable to an integer value
|
||||
template<typename TOther>
|
||||
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().cx)> && std::is_integral_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
|
||||
size(static_cast<ptrdiff_t>(other.cx), static_cast<ptrdiff_t>(other.cy))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator==(const size& other) const noexcept
|
||||
{
|
||||
return _width == other._width &&
|
||||
_height == other._height;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const size& other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
size operator+(const size& other) const
|
||||
{
|
||||
ptrdiff_t width;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(_width, other._width).AssignIfValid(&width));
|
||||
|
||||
ptrdiff_t height;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckAdd(_height, other._height).AssignIfValid(&height));
|
||||
|
||||
return size{ width, height };
|
||||
}
|
||||
|
||||
size operator-(const size& other) const
|
||||
{
|
||||
ptrdiff_t width;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(_width, other._width).AssignIfValid(&width));
|
||||
|
||||
ptrdiff_t height;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckSub(_height, other._height).AssignIfValid(&height));
|
||||
|
||||
return size{ width, height };
|
||||
}
|
||||
|
||||
size operator*(const size& other) const
|
||||
{
|
||||
ptrdiff_t width;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, other._width).AssignIfValid(&width));
|
||||
|
||||
ptrdiff_t height;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckMul(_height, other._height).AssignIfValid(&height));
|
||||
|
||||
return size{ width, height };
|
||||
}
|
||||
|
||||
size operator/(const size& other) const
|
||||
{
|
||||
ptrdiff_t width;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckDiv(_width, other._width).AssignIfValid(&width));
|
||||
|
||||
ptrdiff_t height;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckDiv(_height, other._height).AssignIfValid(&height));
|
||||
|
||||
return size{ width, height };
|
||||
}
|
||||
|
||||
size divide_ceil(const size& other) const
|
||||
{
|
||||
// Divide normally to get the floor.
|
||||
const size floor = *this / other;
|
||||
|
||||
ptrdiff_t adjWidth = 0;
|
||||
ptrdiff_t adjHeight = 0;
|
||||
|
||||
// Check for width remainder, anything not 0.
|
||||
// If we multiply the floored number with the other, it will equal
|
||||
// the old width if there was no remainder.
|
||||
if (other._width * floor._width != _width)
|
||||
{
|
||||
// If there was any remainder,
|
||||
// Grow the magnitude by 1 in the
|
||||
// direction of the sign.
|
||||
if (floor.width() >= 0)
|
||||
{
|
||||
++adjWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
--adjWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for height remainder, anything not 0.
|
||||
// If we multiply the floored number with the other, it will equal
|
||||
// the old width if there was no remainder.
|
||||
if (other._height * floor._height != _height)
|
||||
{
|
||||
// If there was any remainder,
|
||||
// Grow the magnitude by 1 in the
|
||||
// direction of the sign.
|
||||
if (_height >= 0)
|
||||
{
|
||||
++adjHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
--adjHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return floor + size{ adjWidth, adjHeight };
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t width() const noexcept
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T width() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t height() const noexcept
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T height() const
|
||||
{
|
||||
T ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ptrdiff_t area() const
|
||||
{
|
||||
ptrdiff_t result;
|
||||
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, _height).AssignIfValid(&result));
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _WINCONTYPES_
|
||||
operator COORD() const
|
||||
{
|
||||
COORD ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.X));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.Y));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WINDEF_
|
||||
operator SIZE() const
|
||||
{
|
||||
SIZE ret;
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.cx));
|
||||
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.cy));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DCOMMON_H_INCLUDED
|
||||
constexpr operator D2D1_SIZE_F() const noexcept
|
||||
{
|
||||
return D2D1_SIZE_F{ gsl::narrow_cast<float>(_width), gsl::narrow_cast<float>(_height) };
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
ptrdiff_t _width;
|
||||
ptrdiff_t _height;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class ::SizeTests;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
#ifdef __WEX_COMMON_H__
|
||||
namespace WEX::TestExecution
|
||||
{
|
||||
template<>
|
||||
class VerifyOutputTraits<::til::size>
|
||||
{
|
||||
public:
|
||||
static WEX::Common::NoThrowString ToString(const ::til::size& size)
|
||||
{
|
||||
return WEX::Common::NoThrowString().Format(L"[W:%td, H:%td]", size.width(), size.height());
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class VerifyCompareTraits<::til::size, ::til::size>
|
||||
{
|
||||
public:
|
||||
static bool AreEqual(const ::til::size& expected, const ::til::size& actual) noexcept
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
|
||||
static bool AreSame(const ::til::size& expected, const ::til::size& actual) noexcept
|
||||
{
|
||||
return &expected == &actual;
|
||||
}
|
||||
|
||||
static bool IsLessThan(const ::til::size& expectedLess, const ::til::size& expectedGreater) = delete;
|
||||
|
||||
static bool IsGreaterThan(const ::til::size& expectedGreater, const ::til::size& expectedLess) = delete;
|
||||
|
||||
static bool IsNull(const ::til::size& object) noexcept
|
||||
{
|
||||
return object == til::size{};
|
||||
}
|
||||
};
|
||||
};
|
||||
#endif
|
|
@ -25,7 +25,17 @@ Abstract:
|
|||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
|
||||
#define BLOCK_TIL
|
||||
// This includes support libraries from the CRT, STL, WIL, and GSL
|
||||
#include "LibraryIncludes.h"
|
||||
|
||||
#include "WexTestClass.h"
|
||||
|
||||
// Include DirectX common structures so we can test them.
|
||||
// (before TIL so its support lights up)
|
||||
#include <dcommon.h>
|
||||
|
||||
// Include TIL after Wex to get test comparators.
|
||||
#include "til.h"
|
||||
|
||||
// clang-format on
|
||||
|
|
494
src/til/ut_til/SizeTests.cpp
Normal file
494
src/til/ut_til/SizeTests.cpp
Normal file
|
@ -0,0 +1,494 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
#include "til/size.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
|
||||
class SizeTests
|
||||
{
|
||||
TEST_CLASS(SizeTests);
|
||||
|
||||
TEST_METHOD(DefaultConstruct)
|
||||
{
|
||||
const til::size sz;
|
||||
VERIFY_ARE_EQUAL(0, sz._width);
|
||||
VERIFY_ARE_EQUAL(0, sz._height);
|
||||
}
|
||||
|
||||
TEST_METHOD(RawConstruct)
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(5, sz._width);
|
||||
VERIFY_ARE_EQUAL(10, sz._height);
|
||||
}
|
||||
|
||||
TEST_METHOD(UnsignedConstruct)
|
||||
{
|
||||
Log::Comment(L"0.) Normal unsigned construct.");
|
||||
{
|
||||
const size_t width = 5;
|
||||
const size_t height = 10;
|
||||
|
||||
const til::size sz{ width, height };
|
||||
VERIFY_ARE_EQUAL(5, sz._width);
|
||||
VERIFY_ARE_EQUAL(10, sz._height);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Unsigned construct overflow on width.");
|
||||
{
|
||||
constexpr size_t width = std::numeric_limits<size_t>().max();
|
||||
const size_t height = 10;
|
||||
|
||||
auto fn = [&]() {
|
||||
til::size sz{ width, height };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Unsigned construct overflow on height.");
|
||||
{
|
||||
constexpr size_t height = std::numeric_limits<size_t>().max();
|
||||
const size_t width = 10;
|
||||
|
||||
auto fn = [&]() {
|
||||
til::size sz{ width, height };
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(SignedConstruct)
|
||||
{
|
||||
const ptrdiff_t width = -5;
|
||||
const ptrdiff_t height = -10;
|
||||
|
||||
const til::size sz{ width, height };
|
||||
VERIFY_ARE_EQUAL(width, sz._width);
|
||||
VERIFY_ARE_EQUAL(height, sz._height);
|
||||
}
|
||||
|
||||
TEST_METHOD(CoordConstruct)
|
||||
{
|
||||
COORD coord{ -5, 10 };
|
||||
|
||||
const til::size sz{ coord };
|
||||
VERIFY_ARE_EQUAL(coord.X, sz._width);
|
||||
VERIFY_ARE_EQUAL(coord.Y, sz._height);
|
||||
}
|
||||
|
||||
TEST_METHOD(SizeConstruct)
|
||||
{
|
||||
SIZE size{ 5, -10 };
|
||||
|
||||
const til::size sz{ size };
|
||||
VERIFY_ARE_EQUAL(size.cx, sz._width);
|
||||
VERIFY_ARE_EQUAL(size.cy, sz._height);
|
||||
}
|
||||
|
||||
TEST_METHOD(Equality)
|
||||
{
|
||||
Log::Comment(L"0.) Equal.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_TRUE(s1 == s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Left Width changed.");
|
||||
{
|
||||
const til::size s1{ 4, 10 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_FALSE(s1 == s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Right Width changed.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 6, 10 };
|
||||
VERIFY_IS_FALSE(s1 == s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Left Height changed.");
|
||||
{
|
||||
const til::size s1{ 5, 9 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_FALSE(s1 == s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Right Height changed.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 5, 11 };
|
||||
VERIFY_IS_FALSE(s1 == s2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Inequality)
|
||||
{
|
||||
Log::Comment(L"0.) Equal.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_FALSE(s1 != s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Left Width changed.");
|
||||
{
|
||||
const til::size s1{ 4, 10 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_TRUE(s1 != s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Right Width changed.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 6, 10 };
|
||||
VERIFY_IS_TRUE(s1 != s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"3.) Left Height changed.");
|
||||
{
|
||||
const til::size s1{ 5, 9 };
|
||||
const til::size s2{ 5, 10 };
|
||||
VERIFY_IS_TRUE(s1 != s2);
|
||||
}
|
||||
|
||||
Log::Comment(L"4.) Right Height changed.");
|
||||
{
|
||||
const til::size s1{ 5, 10 };
|
||||
const til::size s2{ 5, 11 };
|
||||
VERIFY_IS_TRUE(s1 != s2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Addition)
|
||||
{
|
||||
Log::Comment(L"0.) Addition of two things that should be in bounds.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
const til::size sz2{ 23, 47 };
|
||||
|
||||
const til::size expected{ sz.width() + sz2.width(), sz.height() + sz2.height() };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz + sz2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Addition results in value that is too large (width).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
|
||||
const til::size sz2{ 1, 1 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz + sz2;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Addition results in value that is too large (height).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
|
||||
const til::size sz2{ 1, 1 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz + sz2;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Subtraction)
|
||||
{
|
||||
Log::Comment(L"0.) Subtraction of two things that should be in bounds.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
const til::size sz2{ 23, 47 };
|
||||
|
||||
const til::size expected{ sz.width() - sz2.width(), sz.height() - sz2.height() };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz - sz2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Subtraction results in value that is too small (width).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
|
||||
const til::size sz2{ -2, -2 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz2 - sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Subtraction results in value that is too small (height).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
|
||||
const til::size sz2{ -2, -2 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz2 - sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Multiplication)
|
||||
{
|
||||
Log::Comment(L"0.) Multiplication of two things that should be in bounds.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
const til::size sz2{ 23, 47 };
|
||||
|
||||
const til::size expected{ sz.width() * sz2.width(), sz.height() * sz2.height() };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz * sz2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Multiplication results in value that is too large (width).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
|
||||
const til::size sz2{ 10, 10 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz* sz2;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Multiplication results in value that is too large (height).");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
|
||||
const til::size sz2{ 10, 10 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz* sz2;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Division)
|
||||
{
|
||||
Log::Comment(L"0.) Division of two things that should be in bounds.");
|
||||
{
|
||||
const til::size sz{ 555, 510 };
|
||||
const til::size sz2{ 23, 47 };
|
||||
|
||||
const til::size expected{ sz.width() / sz2.width(), sz.height() / sz2.height() };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz / sz2);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Division by zero");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
|
||||
const til::size sz2{ 1, 1 };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz2 / sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(DivisionRoundingUp)
|
||||
{
|
||||
Log::Comment(L"1.) Division rounding up with positive result.");
|
||||
{
|
||||
const til::size sz{ 10, 5 };
|
||||
const til::size divisor{ 3, 2 };
|
||||
|
||||
// 10 / 3 is 3.333, rounded up is 4.
|
||||
// 5 / 2 is 2.5, rounded up is 3.
|
||||
const til::size expected{ 4, 3 };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz.divide_ceil(divisor));
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Division rounding larger(up) with negative result.");
|
||||
{
|
||||
const til::size sz{ -10, -5 };
|
||||
const til::size divisor{ 3, 2 };
|
||||
|
||||
// -10 / 3 is -3.333, rounded up is -4.
|
||||
// -5 / 2 is -2.5, rounded up is -3.
|
||||
const til::size expected{ -4, -3 };
|
||||
|
||||
VERIFY_ARE_EQUAL(expected, sz.divide_ceil(divisor));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(Width)
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(sz._width, sz.width());
|
||||
}
|
||||
|
||||
TEST_METHOD(WidthCast)
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz._width), sz.width<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Height)
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(sz._height, sz.height());
|
||||
}
|
||||
|
||||
TEST_METHOD(HeightCast)
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz._height), sz.height<SHORT>());
|
||||
}
|
||||
|
||||
TEST_METHOD(Area)
|
||||
{
|
||||
Log::Comment(L"0.) Area of two things that should be in bounds.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
VERIFY_ARE_EQUAL(sz._width * sz._height, sz.area());
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Area is out of bounds on multiplication.");
|
||||
{
|
||||
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
|
||||
const til::size sz{ bigSize, bigSize };
|
||||
|
||||
auto fn = [&]() {
|
||||
sz.area();
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToCoord)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
COORD val = sz;
|
||||
VERIFY_ARE_EQUAL(5, val.X);
|
||||
VERIFY_ARE_EQUAL(10, val.Y);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Overflow on width.");
|
||||
{
|
||||
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t height = 10;
|
||||
const til::size sz{ width, height };
|
||||
|
||||
auto fn = [&]() {
|
||||
COORD val = sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Overflow on height.");
|
||||
{
|
||||
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t width = 10;
|
||||
const til::size sz{ width, height };
|
||||
|
||||
auto fn = [&]() {
|
||||
COORD val = sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToSize)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
SIZE val = sz;
|
||||
VERIFY_ARE_EQUAL(5, val.cx);
|
||||
VERIFY_ARE_EQUAL(10, val.cy);
|
||||
}
|
||||
|
||||
Log::Comment(L"1.) Fit max width into SIZE (may overflow).");
|
||||
{
|
||||
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t height = 10;
|
||||
const til::size sz{ width, height };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside cx/cy
|
||||
const bool overflowExpected = width > std::numeric_limits<decltype(SIZE::cx)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
SIZE val = sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
SIZE val = sz;
|
||||
VERIFY_ARE_EQUAL(width, val.cx);
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(L"2.) Fit max height into SIZE (may overflow).");
|
||||
{
|
||||
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
|
||||
const ptrdiff_t width = 10;
|
||||
const til::size sz{ width, height };
|
||||
|
||||
// On some platforms, ptrdiff_t will fit inside cx/cy
|
||||
const bool overflowExpected = height > std::numeric_limits<decltype(SIZE::cy)>().max();
|
||||
|
||||
if (overflowExpected)
|
||||
{
|
||||
auto fn = [&]() {
|
||||
SIZE val = sz;
|
||||
};
|
||||
|
||||
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
|
||||
}
|
||||
else
|
||||
{
|
||||
SIZE val = sz;
|
||||
VERIFY_ARE_EQUAL(height, val.cy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_METHOD(CastToD2D1SizeF)
|
||||
{
|
||||
Log::Comment(L"0.) Typical situation.");
|
||||
{
|
||||
const til::size sz{ 5, 10 };
|
||||
D2D1_SIZE_F val = sz;
|
||||
VERIFY_ARE_EQUAL(5, val.width);
|
||||
VERIFY_ARE_EQUAL(10, val.height);
|
||||
}
|
||||
|
||||
// All ptrdiff_ts fit into a float, so there's no exception tests.
|
||||
}
|
||||
};
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
<ClCompile Include="ColorTests.cpp" />
|
||||
<ClCompile Include="SomeTests.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp">
|
||||
|
|
15
src/til/ut_til/til.unit.tests.vcxproj.filters
Normal file
15
src/til/ut_til/til.unit.tests.vcxproj.filters
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(SolutionDir)tools\ConsoleTypes.natvis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="SomeTests.cpp" />
|
||||
<ClCompile Include="..\precomp.cpp" />
|
||||
<ClCompile Include="u8u16convertTests.cpp" />
|
||||
<ClCompile Include="SizeTests.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\precomp.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -80,7 +80,11 @@
|
|||
<DisplayString Condition="_keyDown">{{↓ wch:{_charData} mod:{_activeModifierKeys} repeat:{_repeatCount} vk:{_virtualKeyCode} vsc:{_virtualScanCode}}</DisplayString>
|
||||
<DisplayString Condition="!_keyDown">{{↑ wch:{_charData} mod:{_activeModifierKeys} repeat:{_repeatCount} vk:{_virtualKeyCode} vsc:{_virtualScanCode}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
|
||||
<Type Name="til::size">
|
||||
<DisplayString>{{W: {_width,d} x H: {_height,d} -> A: {_width * _height, d}}}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="til::color">
|
||||
<DisplayString>{{RGB: {(int)r,d}, {(int)g,d}, {(int)b,d}; α: {(int)a,d}}}</DisplayString>
|
||||
</Type>
|
||||
|
|
Loading…
Reference in a new issue