//============================================================================ // 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 #include #include #include #include #include #include #include #include #include namespace { static constexpr vtkm::Id ARRAY_SIZE = 10; class ErrorDeviceIndependent : public vtkm::cont::Error { public: ErrorDeviceIndependent(const std::string& msg) : vtkm::cont::Error(msg, true) { } }; class ErrorDeviceDependent : public vtkm::cont::Error { public: ErrorDeviceDependent(const std::string& msg) : vtkm::cont::Error(msg, false) { } }; struct TryExecuteTestFunctor { vtkm::IdComponent NumCalls; VTKM_CONT TryExecuteTestFunctor() : NumCalls(0) { } template VTKM_CONT bool operator()(Device, const vtkm::cont::ArrayHandle& in, vtkm::cont::ArrayHandle& out) { using Algorithm = vtkm::cont::DeviceAdapterAlgorithm; Algorithm::Copy(in, out); this->NumCalls++; return true; } }; template struct TryExecuteTestErrorFunctor { template VTKM_CONT bool operator()(Device) { throw ExceptionT("Test message"); } }; template void TryExecuteTests(DeviceList, bool expectSuccess) { vtkm::cont::ArrayHandle inArray; vtkm::cont::ArrayHandle outArray; inArray.Allocate(ARRAY_SIZE); SetPortal(inArray.WritePortal()); TryExecuteTestFunctor functor; bool result = vtkm::cont::TryExecute(functor, DeviceList(), inArray, outArray); if (expectSuccess) { VTKM_TEST_ASSERT(result, "Call returned failure when expected success."); VTKM_TEST_ASSERT(functor.NumCalls == 1, "Bad number of calls"); CheckPortal(outArray.ReadPortal()); } else { VTKM_TEST_ASSERT(!result, "Call returned true when expected failure."); } //verify the ability to pass rvalue functors vtkm::cont::ArrayHandle outArray2; result = vtkm::cont::TryExecute(TryExecuteTestFunctor(), DeviceList(), inArray, outArray2); if (expectSuccess) { VTKM_TEST_ASSERT(result, "Call returned failure when expected success."); CheckPortal(outArray2.ReadPortal()); } else { VTKM_TEST_ASSERT(!result, "Call returned true when expected failure."); } } struct EdgeCaseFunctor { template bool operator()(DeviceList, int, float, bool) const { return true; } template bool operator()(DeviceList) const { return true; } }; void TryExecuteAllEdgeCases() { using ValidDevice = vtkm::cont::DeviceAdapterTagSerial; using SingleValidList = vtkm::List; std::cout << "TryExecute no Runtime, no Device, no parameters." << std::endl; vtkm::cont::TryExecute(EdgeCaseFunctor()); std::cout << "TryExecute no Runtime, no Device, with parameters." << std::endl; vtkm::cont::TryExecute(EdgeCaseFunctor(), int{ 42 }, float{ 3.14f }, bool{ true }); std::cout << "TryExecute with Device, no parameters." << std::endl; vtkm::cont::TryExecute(EdgeCaseFunctor(), SingleValidList()); std::cout << "TryExecute with Device, with parameters." << std::endl; vtkm::cont::TryExecute( EdgeCaseFunctor(), SingleValidList(), int{ 42 }, float{ 3.14f }, bool{ true }); } template void RunErrorTest(bool shouldFail, bool shouldThrow, bool shouldDisable) { using Device = vtkm::cont::DeviceAdapterTagSerial; using Functor = TryExecuteTestErrorFunctor; // Initialize this one to what we expect -- it won't get set if we throw. bool succeeded = !shouldFail; bool threw = false; bool disabled = false; vtkm::cont::ScopedRuntimeDeviceTracker scopedTracker(Device{}); try { succeeded = vtkm::cont::TryExecute(Functor{}); threw = false; } catch (...) { threw = true; } auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); disabled = !tracker.CanRunOn(Device{}); std::cout << "Failed: " << !succeeded << " " << "Threw: " << threw << " " << "Disabled: " << disabled << "\n" << std::endl; VTKM_TEST_ASSERT(shouldFail == !succeeded, "TryExecute return status incorrect."); VTKM_TEST_ASSERT(threw == shouldThrow, "TryExecute throw behavior incorrect."); VTKM_TEST_ASSERT(disabled == shouldDisable, "TryExecute device-disabling behavior incorrect."); } void TryExecuteErrorTests() { std::cout << "Test ErrorBadAllocation." << std::endl; RunErrorTest(true, false, true); std::cout << "Test ErrorBadDevice." << std::endl; RunErrorTest(true, false, true); std::cout << "Test ErrorBadType." << std::endl; RunErrorTest(true, false, false); std::cout << "Test ErrorBadValue." << std::endl; RunErrorTest(true, true, false); std::cout << "Test custom vtkm Error (dev indep)." << std::endl; RunErrorTest(true, true, false); std::cout << "Test custom vtkm Error (dev dep)." << std::endl; RunErrorTest(true, false, false); std::cout << "Test std::exception." << std::endl; RunErrorTest(true, false, false); std::cout << "Test throw non-exception." << std::endl; RunErrorTest(true, false, false); } static void Run() { using ValidDevice = vtkm::cont::DeviceAdapterTagSerial; using InvalidDevice = vtkm::cont::DeviceAdapterTagUndefined; TryExecuteAllEdgeCases(); std::cout << "Try a list with a single entry." << std::endl; using SingleValidList = vtkm::List; TryExecuteTests(SingleValidList(), true); std::cout << "Try a list with two valid devices." << std::endl; using DoubleValidList = vtkm::List; TryExecuteTests(DoubleValidList(), true); std::cout << "Try a list with only invalid device." << std::endl; using SingleInvalidList = vtkm::List; TryExecuteTests(SingleInvalidList(), false); std::cout << "Try a list with an invalid and valid device." << std::endl; using InvalidAndValidList = vtkm::List; TryExecuteTests(InvalidAndValidList(), true); TryExecuteErrorTests(); } } // anonymous namespace int UnitTestTryExecute(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(Run, argc, argv); }