UnitTestTaskSingular.cxx 10.5 KB
Newer Older
1 2 3 4
//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
5
//
6 7 8 9 10 11 12
//  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/exec/internal/TaskSingular.h>

13
#include <vtkm/exec/FunctorBase.h>
14 15 16 17 18 19 20 21 22
#include <vtkm/exec/arg/BasicArg.h>
#include <vtkm/exec/arg/ThreadIndicesBasic.h>

#include <vtkm/StaticAssert.h>

#include <vtkm/internal/FunctionInterface.h>
#include <vtkm/internal/Invocation.h>

#include <vtkm/testing/Testing.h>
23 24
namespace
{
25 26 27 28

struct TestExecObject
{
  VTKM_EXEC_CONT
29
  TestExecObject()
30
    : Value(nullptr)
31 32
  {
  }
33 34

  VTKM_EXEC_CONT
35 36 37 38
  TestExecObject(vtkm::Id* value)
    : Value(value)
  {
  }
39

40
  vtkm::Id* Value;
41 42 43 44
};

struct MyOutputToInputMapPortal
{
45
  using ValueType = vtkm::Id;
46 47 48 49 50 51
  VTKM_EXEC_CONT
  vtkm::Id Get(vtkm::Id index) const { return index; }
};

struct MyVisitArrayPortal
{
52
  using ValueType = vtkm::IdComponent;
53 54 55
  vtkm::IdComponent Get(vtkm::Id) const { return 1; }
};

56 57 58 59 60 61 62
struct MyThreadToOutputMapPortal
{
  using ValueType = vtkm::Id;
  VTKM_EXEC_CONT
  vtkm::Id Get(vtkm::Id index) const { return index; }
};

63 64 65 66 67 68
struct TestFetchTagInput
{
};
struct TestFetchTagOutput
{
};
69 70 71 72

// Missing TransportTag, but we are not testing that so we can leave it out.
struct TestControlSignatureTagInput
{
73
  using FetchTag = TestFetchTagInput;
74 75 76
};
struct TestControlSignatureTagOutput
{
77
  using FetchTag = TestFetchTagOutput;
78 79 80 81
};

} // anonymous namespace

82 83 84 85 86 87
namespace vtkm
{
namespace exec
{
namespace arg
{
88

89
template <>
90 91 92 93
struct Fetch<TestFetchTagInput,
             vtkm::exec::arg::AspectTagDefault,
             vtkm::exec::arg::ThreadIndicesBasic,
             TestExecObject>
94
{
95
  using ValueType = vtkm::Id;
96 97

  VTKM_EXEC
98 99 100 101
  ValueType Load(const vtkm::exec::arg::ThreadIndicesBasic& indices,
                 const TestExecObject& execObject) const
  {
    return *execObject.Value + 10 * indices.GetInputIndex();
102 103 104
  }

  VTKM_EXEC
105 106
  void Store(const vtkm::exec::arg::ThreadIndicesBasic&, const TestExecObject&, ValueType) const
  {
107 108 109 110
    // No-op
  }
};

111
template <>
112 113 114 115
struct Fetch<TestFetchTagOutput,
             vtkm::exec::arg::AspectTagDefault,
             vtkm::exec::arg::ThreadIndicesBasic,
             TestExecObject>
116
{
117
  using ValueType = vtkm::Id;
118 119

  VTKM_EXEC
120 121
  ValueType Load(const vtkm::exec::arg::ThreadIndicesBasic&, const TestExecObject&) const
  {
122 123 124 125 126
    // No-op
    return ValueType();
  }

  VTKM_EXEC
127 128
  void Store(const vtkm::exec::arg::ThreadIndicesBasic& indices,
             const TestExecObject& execObject,
129 130 131
             ValueType value) const
  {
    *execObject.Value = value + 20 * indices.GetOutputIndex();
132 133 134 135 136 137
  }
};
}
}
} // vtkm::exec::arg

138 139
namespace
{
140

141
using TestControlSignature = void(TestControlSignatureTagInput, TestControlSignatureTagOutput);
142
using TestControlInterface = vtkm::internal::FunctionInterface<TestControlSignature>;
143

144
using TestExecutionSignature1 = void(vtkm::exec::arg::BasicArg<1>, vtkm::exec::arg::BasicArg<2>);
145
using TestExecutionInterface1 = vtkm::internal::FunctionInterface<TestExecutionSignature1>;
146

147
using TestExecutionSignature2 = vtkm::exec::arg::BasicArg<2>(vtkm::exec::arg::BasicArg<1>);
148
using TestExecutionInterface2 = vtkm::internal::FunctionInterface<TestExecutionSignature2>;
149

150 151
using ExecutionParameterInterface =
  vtkm::internal::FunctionInterface<void(TestExecObject, TestExecObject)>;
152

153 154 155 156 157
using InvocationType1 = vtkm::internal::Invocation<ExecutionParameterInterface,
                                                   TestControlInterface,
                                                   TestExecutionInterface1,
                                                   1,
                                                   MyOutputToInputMapPortal,
158 159
                                                   MyVisitArrayPortal,
                                                   MyThreadToOutputMapPortal>;
160 161 162 163 164 165

using InvocationType2 = vtkm::internal::Invocation<ExecutionParameterInterface,
                                                   TestControlInterface,
                                                   TestExecutionInterface2,
                                                   1,
                                                   MyOutputToInputMapPortal,
166 167
                                                   MyVisitArrayPortal,
                                                   MyThreadToOutputMapPortal>;
168 169 170 171 172

// Not a full worklet, but provides operators that we expect in a worklet.
struct TestWorkletProxy : vtkm::exec::FunctorBase
{
  VTKM_EXEC
173
  void operator()(vtkm::Id input, vtkm::Id& output) const { output = input + 100; }
174 175

  VTKM_EXEC
176
  vtkm::Id operator()(vtkm::Id input) const { return input + 200; }
177

178 179 180
  template <typename T,
            typename OutToInArrayType,
            typename VisitArrayType,
181
            typename ThreadToOutArrayType,
182 183
            typename InputDomainType,
            typename G>
184
  VTKM_EXEC vtkm::exec::arg::ThreadIndicesBasic GetThreadIndices(
185 186 187
    const T& threadIndex,
    const OutToInArrayType& outToIn,
    const VisitArrayType& visit,
188
    const ThreadToOutArrayType& threadToOut,
189 190
    const InputDomainType&,
    const G& globalThreadIndexOffset) const
191
  {
192
    const vtkm::Id outIndex = threadToOut.Get(threadIndex);
193
    return vtkm::exec::arg::ThreadIndicesBasic(
194
      threadIndex, outToIn.Get(outIndex), visit.Get(outIndex), outIndex, globalThreadIndexOffset);
195 196 197 198 199 200 201 202 203
  }
};

#define ERROR_MESSAGE "Expected worklet error."

// Not a full worklet, but provides operators that we expect in a worklet.
struct TestWorkletErrorProxy : vtkm::exec::FunctorBase
{
  VTKM_EXEC
204
  void operator()(vtkm::Id, vtkm::Id) const { this->RaiseError(ERROR_MESSAGE); }
205

206 207 208
  template <typename T,
            typename OutToInArrayType,
            typename VisitArrayType,
209
            typename ThreadToOutArrayType,
210 211
            typename InputDomainType,
            typename G>
212
  VTKM_EXEC vtkm::exec::arg::ThreadIndicesBasic GetThreadIndices(
213 214 215
    const T& threadIndex,
    const OutToInArrayType& outToIn,
    const VisitArrayType& visit,
216
    const ThreadToOutArrayType& threadToOut,
217 218
    const InputDomainType&,
    const G& globalThreadIndexOffset) const
219
  {
220
    const vtkm::Id outIndex = threadToOut.Get(threadIndex);
221
    return vtkm::exec::arg::ThreadIndicesBasic(
222
      threadIndex, outToIn.Get(outIndex), visit.Get(outIndex), outIndex, globalThreadIndexOffset);
223 224 225 226 227
  }
};

// Check behavior of InvocationToFetch helper class.

228
VTKM_STATIC_ASSERT(
229 230 231 232 233 234
  (std::is_same<vtkm::exec::internal::detail::
                  InvocationToFetch<vtkm::exec::arg::ThreadIndicesBasic, InvocationType1, 1>::type,
                vtkm::exec::arg::Fetch<TestFetchTagInput,
                                       vtkm::exec::arg::AspectTagDefault,
                                       vtkm::exec::arg::ThreadIndicesBasic,
                                       TestExecObject>>::type::value));
235 236

VTKM_STATIC_ASSERT(
237 238 239 240 241 242
  (std::is_same<vtkm::exec::internal::detail::
                  InvocationToFetch<vtkm::exec::arg::ThreadIndicesBasic, InvocationType1, 2>::type,
                vtkm::exec::arg::Fetch<TestFetchTagOutput,
                                       vtkm::exec::arg::AspectTagDefault,
                                       vtkm::exec::arg::ThreadIndicesBasic,
                                       TestExecObject>>::type::value));
243 244

VTKM_STATIC_ASSERT(
245 246 247 248 249 250
  (std::is_same<vtkm::exec::internal::detail::
                  InvocationToFetch<vtkm::exec::arg::ThreadIndicesBasic, InvocationType2, 0>::type,
                vtkm::exec::arg::Fetch<TestFetchTagOutput,
                                       vtkm::exec::arg::AspectTagDefault,
                                       vtkm::exec::arg::ThreadIndicesBasic,
                                       TestExecObject>>::type::value));
251 252 253 254 255 256 257

void TestNormalFunctorInvoke()
{
  std::cout << "Testing normal worklet invoke." << std::endl;

  vtkm::Id inputTestValue;
  vtkm::Id outputTestValue;
258 259 260
  vtkm::internal::FunctionInterface<void(TestExecObject, TestExecObject)> execObjects =
    vtkm::internal::make_FunctionInterface<void>(TestExecObject(&inputTestValue),
                                                 TestExecObject(&outputTestValue));
261 262 263 264

  std::cout << "  Try void return." << std::endl;
  inputTestValue = 5;
  outputTestValue = static_cast<vtkm::Id>(0xDEADDEAD);
265
  using TaskSingular1 = vtkm::exec::internal::TaskSingular<TestWorkletProxy, InvocationType1>;
266 267 268 269
  TestWorkletProxy worklet;
  InvocationType1 invocation1(execObjects);
  TaskSingular1 taskInvokeWorklet1(worklet, invocation1);

270 271
  taskInvokeWorklet1(1);
  VTKM_TEST_ASSERT(inputTestValue == 5, "Input value changed.");
272
  VTKM_TEST_ASSERT(outputTestValue == inputTestValue + 100 + 30, "Output value not set right.");
273 274 275 276

  std::cout << "  Try return value." << std::endl;
  inputTestValue = 6;
  outputTestValue = static_cast<vtkm::Id>(0xDEADDEAD);
277
  using TaskSingular2 = vtkm::exec::internal::TaskSingular<TestWorkletProxy, InvocationType2>;
278 279 280
  InvocationType2 invocation2(execObjects);
  TaskSingular2 taskInvokeWorklet2(worklet, invocation2);

281 282
  taskInvokeWorklet2(2);
  VTKM_TEST_ASSERT(inputTestValue == 6, "Input value changed.");
283
  VTKM_TEST_ASSERT(outputTestValue == inputTestValue + 200 + 30 * 2, "Output value not set right.");
284 285 286 287 288 289 290 291
}

void TestErrorFunctorInvoke()
{
  std::cout << "Testing invoke with an error raised in the worklet." << std::endl;

  vtkm::Id inputTestValue = 5;
  vtkm::Id outputTestValue = static_cast<vtkm::Id>(0xDEADDEAD);
292 293 294
  vtkm::internal::FunctionInterface<void(TestExecObject, TestExecObject)> execObjects =
    vtkm::internal::make_FunctionInterface<void>(TestExecObject(&inputTestValue),
                                                 TestExecObject(&outputTestValue));
295

296
  using TaskSingular1 = vtkm::exec::internal::TaskSingular<TestWorkletErrorProxy, InvocationType1>;
297 298 299 300
  TestWorkletErrorProxy worklet;
  InvocationType1 invocation(execObjects);
  TaskSingular1 taskInvokeWorklet1 = TaskSingular1(worklet, invocation);

301 302 303 304 305 306 307
  char message[1024];
  message[0] = '\0';
  vtkm::exec::internal::ErrorMessageBuffer errorMessage(message, 1024);
  taskInvokeWorklet1.SetErrorMessageBuffer(errorMessage);
  taskInvokeWorklet1(1);

  VTKM_TEST_ASSERT(errorMessage.IsErrorRaised(), "Error not raised correctly.");
308
  VTKM_TEST_ASSERT(message == std::string(ERROR_MESSAGE), "Got wrong error message.");
309 310 311 312 313 314 315 316 317 318
}

void TestTaskSingular()
{
  TestNormalFunctorInvoke();
  TestErrorFunctorInvoke();
}

} // anonymous namespace

319
int UnitTestTaskSingular(int argc, char* argv[])
320
{
321
  return vtkm::testing::Testing::Run(TestTaskSingular, argc, argv);
322
}