vtk-m2/vtkm/cont/testing/UnitTestArrayHandleDecorator.cxx
2019-12-20 17:03:36 -05:00

652 lines
23 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/ArrayHandleDecorator.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/BinaryOperators.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
struct DecoratorTests
{
static constexpr vtkm::Id ARRAY_SIZE = 10;
// Decorator implementation that demonstrates how to write invertible functors
// that combine three array handles with complex access logic. The resulting
// ArrayHandleDecorator can be both read from and written to.
//
// Constructs functors that take three portals.
//
// The first portal's values are accessed in reverse order.
// The second portal's values are accessed in normal order.
// The third portal's values are accessed via ((idx + 3) % size).
//
// Functor will return the max of the first two added to the third.
//
// InverseFunctor will update the third portal such that the Functor would
// return the indicated value.
struct InvertibleDecorImpl
{
// The functor used for reading data from the three portals.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto idx1 = this->Portal1.GetNumberOfValues() - idx - 1;
const auto idx2 = idx;
const auto idx3 = (idx + 3) % this->Portal3.GetNumberOfValues();
const auto v1 = this->Portal1.Get(idx1);
const auto v2 = this->Portal2.Get(idx2);
const auto v3 = this->Portal3.Get(idx3);
return vtkm::Max(v1, v2) + v3;
}
};
// The functor used for writing. Only Portal3 is written to, the other
// portals may be read-only.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct InverseFunctor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT void operator()(vtkm::Id idx, const ValueType& vIn) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto vNew = static_cast<ValueType>(vIn - vtkm::Max(v1, v2));
this->Portal3.Set((idx + 3) % this->Portal3.GetNumberOfValues(), vNew);
}
};
// Factory function that takes 3 portals as input and creates an instance
// of Functor with them. Variadic template parameters are used here, but are
// not necessary.
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
// Factory function that takes 3 portals as input and creates an instance
// of InverseFunctor with them. Variadic template parameters are used here,
// but are not necessary.
template <typename... PortalTs>
InverseFunctor<typename std::decay<PortalTs>::type...> CreateInverseFunctor(
PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Same as above, but cannot be inverted. The resulting ArrayHandleDecorator
// will be read-only.
struct NonInvertibleDecorImpl
{
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto v3 = this->Portal3.Get((idx + 3) % this->Portal3.GetNumberOfValues());
return vtkm::Max(v1, v2) + v3;
}
};
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Decorator implementation that demonstrates how to create functors that
// hold custom state. Here, the functors have a customizable Operation
// member.
//
// This implementation is used to create a read-only ArrayHandleDecorator
// that combines the values in two other ArrayHandles using an arbitrary
// binary operation (e.g. vtkm::Maximum, vtkm::Add, etc).
template <typename ValueType, typename OperationType>
struct BinaryOperationDecorImpl
{
OperationType Operation;
// The functor use to read values. Note that it holds extra state in
// addition to the portals.
template <typename Portal1Type, typename Portal2Type>
struct Functor
{
Portal1Type Portal1;
Portal2Type Portal2;
OperationType Operation;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return this->Operation(static_cast<ValueType>(this->Portal1.Get(idx)),
static_cast<ValueType>(this->Portal2.Get(idx)));
}
};
// A non-variadic example of a factory function to produce a functor. This
// is where the extra state is passed into the functor.
template <typename P1T, typename P2T>
Functor<P1T, P2T> CreateFunctor(P1T p1, P2T p2) const
{
return { p1, p2, this->Operation };
}
};
// Decorator implementation that reverses the ScanExtended operation.
//
// The resulting ArrayHandleDecorator will take an array produced by the
// ScanExtended algorithm and return the original ScanExtended input.
//
// Some interesting things about this:
// - The ArrayHandleDecorator's ValueType will not be the same as the
// ScanPortal's ValueType. The Decorator ValueType is determined by the
// return type of Functor::operator().
// - The ScanPortal has more values than the ArrayHandleDecorator. The
// number of values the ArrayHandleDecorator should hold is set during
// construction and may differ from the arrays it holds.
template <typename ValueType>
struct ScanExtendedToNumIndicesDecorImpl
{
template <typename ScanPortalType>
struct Functor
{
ScanPortalType ScanPortal;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->ScanPortal.Get(idx + 1) - this->ScanPortal.Get(idx));
}
};
template <typename ScanPortalType>
Functor<ScanPortalType> CreateFunctor(ScanPortalType portal) const
{
return { portal };
}
};
// Decorator implementation that combines two source arrays using the formula
// `[source1] * 10 + [source2]` and supports resizing.
template <typename ValueType>
struct DecompositionDecorImpl
{
template <typename Portal1T, typename Portal2T>
struct Functor
{
Portal1T Portal1;
Portal2T Portal2;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->Portal1.Get(idx) * 10 + this->Portal2.Get(idx));
}
};
template <typename Portal1T, typename Portal2T>
struct InverseFunctor
{
Portal1T Portal1;
Portal2T Portal2;
VTKM_EXEC_CONT
void operator()(vtkm::Id idx, const ValueType& val) const
{
this->Portal1.Set(idx, static_cast<ValueType>(std::floor(val / 10)));
this->Portal2.Set(idx, static_cast<ValueType>(std::fmod(val, 10)));
}
};
template <typename Portal1T, typename Portal2T>
VTKM_CONT Functor<typename std::decay<Portal1T>::type, typename std::decay<Portal2T>::type>
CreateFunctor(Portal1T&& p1, Portal2T&& p2) const
{
return { std::forward<Portal1T>(p1), std::forward<Portal2T>(p2) };
}
template <typename Portal1T, typename Portal2T>
VTKM_CONT
InverseFunctor<typename std::decay<Portal1T>::type, typename std::decay<Portal2T>::type>
CreateInverseFunctor(Portal1T&& p1, Portal2T&& p2) const
{
return { std::forward<Portal1T>(p1), std::forward<Portal2T>(p2) };
}
// Resize methods:
template <typename Array1T, typename Array2T>
VTKM_CONT void AllocateSourceArrays(vtkm::Id numVals, Array1T&& array1, Array2T&& array2) const
{
array1.Allocate(numVals);
array2.Allocate(numVals);
}
template <typename Array1T, typename Array2T>
VTKM_CONT void ShrinkSourceArrays(vtkm::Id numVals, Array1T&& array1, Array2T&& array2) const
{
array1.Shrink(numVals);
array2.Shrink(numVals);
}
};
template <typename ValueType>
void InversionTest() const
{
auto ah1 = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 2 }, ARRAY_SIZE);
auto ah2 = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<ValueType> ah3;
vtkm::cont::Algorithm::Fill(ah3, ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
auto ah3Const = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
{ // Has a writable handle and an invertible functor:
auto ahInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(vtkm::cont::internal::IsWritableArrayHandle<decltype(ahInv)>::value);
}
{ // Has no writable handles and an invertible functor:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has writable handles, but the functor cannot be inverted:
auto ahNInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has no writable handles and the functor cannot be inverted:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Test reading/writing to an invertible handle:
// Copy ah3 since we'll be modifying it:
vtkm::cont::ArrayHandle<ValueType> ah3Copy;
vtkm::cont::ArrayCopy(ah3, ah3Copy);
auto ahDecor =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Copy);
{
auto portalDecor = ahDecor.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 23 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 21 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 19 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 17 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 15 });
}
// Copy a constant array into the decorator. This should modify ah3Copy.
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(ValueType{ 25 }, ARRAY_SIZE),
ahDecor);
{ // Accessing portal should give all 25s:
auto portalDecor = ahDecor.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 25 });
}
{ // ah3Copy should have updated values:
auto portalAH3Copy = ah3Copy.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.Get(0) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(1) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(2) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(3) == ValueType{ 7 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(4) == ValueType{ 9 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(5) == ValueType{ 11 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(6) == ValueType{ 13 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(9) == ValueType{ 15 });
}
}
}
template <typename ValueType, typename OperationType>
void BinaryOperatorTest() const
{
auto ahCount = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
auto ahConst = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
const OperationType op;
BinaryOperationDecorImpl<ValueType, OperationType> impl{ op };
auto decorArray = vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, impl, ahCount, ahConst);
{
auto decorPortal = decorArray.GetPortalConstControl();
auto countPortal = ahCount.GetPortalConstControl();
auto constPortal = ahConst.GetPortalConstControl();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(decorPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
vtkm::cont::ArrayHandle<ValueType> copiedInExec;
vtkm::cont::ArrayCopy(decorArray, copiedInExec);
{
auto copiedPortal = copiedInExec.GetPortalConstControl();
auto countPortal = ahCount.GetPortalConstControl();
auto constPortal = ahConst.GetPortalConstControl();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(copiedPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
}
template <typename ValueType>
void ScanExtendedToNumIndicesTest() const
{
auto numIndicesOrig =
vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<vtkm::Id> scan;
vtkm::cont::Algorithm::ScanExtended(vtkm::cont::make_ArrayHandleCast<vtkm::Id>(numIndicesOrig),
scan);
// Some interesting things to notice:
// - `numIndicesDecor` will have `ARRAY_SIZE` entries, while `scan` has
// `ARRAY_SIZE + 1`.
// - `numIndicesDecor` uses the current function scope `ValueType`, since
// that is what the functor from the implementation class returns. `scan`
// uses `vtkm::Id`.
auto numIndicesDecor = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, ScanExtendedToNumIndicesDecorImpl<ValueType>{}, scan);
{
auto origPortal = numIndicesOrig.GetPortalConstControl();
auto decorPortal = numIndicesDecor.GetPortalConstControl();
VTKM_STATIC_ASSERT(VTKM_PASS_COMMAS(
std::is_same<decltype(origPortal.Get(0)), decltype(decorPortal.Get(0))>::value));
VTKM_TEST_ASSERT(origPortal.GetNumberOfValues() == decorPortal.GetNumberOfValues());
for (vtkm::Id i = 0; i < origPortal.GetNumberOfValues(); ++i)
{
VTKM_TEST_ASSERT(origPortal.Get(i) == decorPortal.Get(i));
}
}
}
template <typename ValueType>
void DecompositionTest() const
{
vtkm::cont::ArrayHandle<ValueType> a1;
vtkm::cont::ArrayHandle<ValueType> a2;
auto decor =
vtkm::cont::make_ArrayHandleDecorator(0, DecompositionDecorImpl<ValueType>{}, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 0);
decor.Allocate(5);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
{
auto decorPortal = decor.GetPortalControl();
decorPortal.Set(0, 13);
decorPortal.Set(1, 8);
decorPortal.Set(2, 43);
decorPortal.Set(3, 92);
decorPortal.Set(4, 117);
}
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 5);
{
auto a1Portal = a1.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(0), 1));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(1), 0));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(2), 4));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(3), 9));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(4), 11));
}
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 5);
{
auto a2Portal = a2.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(0), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(2), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(3), 2));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(4), 7));
}
decor.Shrink(3);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 3);
{
auto decorPortal = decor.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(0), 13));
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(2), 43));
}
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 3);
{
auto a1Portal = a1.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(0), 1));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(1), 0));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(2), 4));
}
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 3);
{
auto a2Portal = a2.GetPortalConstControl();
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(0), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(2), 3));
}
}
template <typename ValueType>
void operator()(const ValueType) const
{
this->InversionTest<ValueType>();
this->BinaryOperatorTest<ValueType, vtkm::Maximum>();
this->BinaryOperatorTest<ValueType, vtkm::Minimum>();
this->BinaryOperatorTest<ValueType, vtkm::Add>();
this->BinaryOperatorTest<ValueType, vtkm::Subtract>();
this->BinaryOperatorTest<ValueType, vtkm::Multiply>();
this->ScanExtendedToNumIndicesTest<ValueType>();
this->DecompositionTest<ValueType>();
}
};
// ArrayHandleDecorator that implements AllocateSourceArrays and ShrinkSourceArrays, thus allowing
// it to be resized.
struct ResizableDecorImpl
{
// We don't actually read/write from this, so use a dummy functor:
struct Functor
{
VTKM_EXEC_CONT vtkm::Id operator()(vtkm::Id) const { return 0; }
};
template <typename... PortalTs>
VTKM_CONT Functor CreateFunctor(PortalTs...) const
{
return Functor{};
}
template <typename Array1T, typename Array2T>
void ShrinkSourceArrays(vtkm::Id newSize, Array1T& a1, Array2T& a2) const
{
VTKM_IS_ARRAY_HANDLE(Array1T);
VTKM_IS_ARRAY_HANDLE(Array2T);
// Resize each to 2*newSize:
a1.Shrink(2 * newSize);
a2.Shrink(2 * newSize);
}
template <typename Array1T, typename Array2T>
void AllocateSourceArrays(vtkm::Id newSize, Array1T& a1, Array2T& a2) const
{
VTKM_IS_ARRAY_HANDLE(Array1T);
VTKM_IS_ARRAY_HANDLE(Array2T);
// Resize each to 3*newSize:
a1.Allocate(3 * newSize);
a2.Allocate(3 * newSize);
}
};
// ArrayHandleDecorator that implements AllocateSourceArrays and ShrinkSourceArrays, thus allowing
// it to be resized.
struct NonResizableDecorImpl
{
// We don't actually read/write from this, so use a dummy functor:
struct Functor
{
VTKM_EXEC_CONT vtkm::Id operator()(vtkm::Id) const { return 0; }
};
template <typename... PortalTs>
VTKM_CONT Functor CreateFunctor(PortalTs...) const
{
return Functor{};
}
};
void ResizeTest()
{
{
vtkm::cont::ArrayHandle<vtkm::Id> a1;
vtkm::cont::ArrayHandle<vtkm::Id> a2;
ResizableDecorImpl impl;
auto decor = vtkm::cont::make_ArrayHandleDecorator(5, impl, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
decor.Allocate(10); // Should allocate a1&a2 to have 30 values:
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 30);
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 30);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 10);
decor.Shrink(3); // Should resize a1&a2 to have 6 values:
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 6);
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 6);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 3);
}
{
vtkm::cont::ArrayHandle<vtkm::Id> a1;
a1.Allocate(20);
vtkm::cont::ArrayHandle<vtkm::Id> a2;
a2.Allocate(20);
NonResizableDecorImpl impl;
auto decor = vtkm::cont::make_ArrayHandleDecorator(5, impl, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
// Allocate and Shrink should throw an ErrorBadType:
bool threw = false;
try
{
decor.Allocate(10);
}
catch (vtkm::cont::ErrorBadType& e)
{
std::cerr << "Caught expected exception: " << e.what() << "\n";
threw = true;
}
VTKM_TEST_ASSERT(threw, "Allocate did not throw as expected.");
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
threw = false;
try
{
decor.Shrink(3);
}
catch (vtkm::cont::ErrorBadType& e)
{
std::cerr << "Caught expected exception: " << e.what() << "\n";
threw = true;
}
VTKM_TEST_ASSERT(threw, "Allocate did not throw as expected.");
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
}
}
void TestArrayHandleDecorator()
{
vtkm::testing::Testing::TryTypes(DecoratorTests{}, vtkm::TypeListScalarAll{});
ResizeTest();
}
} // anonymous namespace
int UnitTestArrayHandleDecorator(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(TestArrayHandleDecorator, argc, argv);
}