Unit tests for cl::Semaphore (#220)

* Unit tests for cl::Semaphore

Fix pointer param for clGetSemaphoreInfoKHR.
Add more params supported by clGetSemaphoreInfoKHR.

* UT for cl::Semaphore: rebase and fix build error

* UT for cl::Semaphore: use one big "#if defined(cl_khr_semaphore)" for tests
diff --git a/include/CL/opencl.hpp b/include/CL/opencl.hpp
index d8d227e..d470a36 100644
--- a/include/CL/opencl.hpp
+++ b/include/CL/opencl.hpp
@@ -1520,7 +1520,12 @@
     F(cl_device_info, CL_DEVICE_OPENCL_C_NUMERIC_VERSION_KHR, cl_version_khr)
 
 #define CL_HPP_PARAM_NAME_CL_KHR_SEMAPHORE_(F) \
+    F(cl_semaphore_info_khr, CL_SEMAPHORE_CONTEXT_KHR, cl::Context) \
+    F(cl_semaphore_info_khr, CL_SEMAPHORE_REFERENCE_COUNT_KHR, cl_uint) \
     F(cl_semaphore_info_khr, CL_SEMAPHORE_PROPERTIES_KHR, cl::vector<cl_semaphore_properties_khr>) \
+    F(cl_semaphore_info_khr, CL_SEMAPHORE_TYPE_KHR, cl_semaphore_type_khr) \
+    F(cl_semaphore_info_khr, CL_SEMAPHORE_PAYLOAD_KHR, cl_semaphore_payload_khr) \
+    F(cl_semaphore_info_khr, CL_DEVICE_HANDLE_LIST_KHR, cl::vector<cl::Device>) \
     F(cl_platform_info, CL_PLATFORM_SEMAPHORE_TYPES_KHR,  cl::vector<cl_semaphore_type_khr>) \
     F(cl_device_info, CL_DEVICE_SEMAPHORE_TYPES_KHR,      cl::vector<cl_semaphore_type_khr>) \
 
@@ -10447,7 +10452,7 @@
         }
 
         return detail::errHandler(
-            detail::getInfo(&pfn_clGetSemaphoreInfoKHR, object_, name, param),
+            detail::getInfo(pfn_clGetSemaphoreInfoKHR, object_, name, param),
             __GET_SEMAPHORE_KHR_INFO_ERR);
     }
     template <cl_semaphore_info_khr name> typename
diff --git a/tests/test_openclhpp.cpp b/tests/test_openclhpp.cpp
index ed387ee..9e9eef6 100644
--- a/tests/test_openclhpp.cpp
+++ b/tests/test_openclhpp.cpp
@@ -59,6 +59,18 @@
     return (cl_command_buffer_khr)(size_t)(0x8f8f8f8f + index);
 }
 
+static inline cl_event make_event(int index)
+{
+    return (cl_event)(size_t)(0xd0d0d0d0 + index);
+}
+
+#if defined(cl_khr_semaphore)
+static inline cl_semaphore_khr make_semaphore_khr(int index)
+{
+    return (cl_semaphore_khr)(size_t)(0xa0b0c0e0 + index);
+}
+#endif
+
 /* Pools of pre-allocated wrapped objects for tests. There is no device pool,
  * because there is no way to know whether the test wants the device to be
  * reference countable or not.
@@ -75,6 +87,9 @@
 #if defined(cl_khr_command_buffer)
 static cl::CommandBufferKhr commandBufferKhrPool[POOL_MAX];
 #endif
+#if defined(cl_khr_semaphore)
+static cl::Semaphore semaphorePool[POOL_MAX];
+#endif
 
 /****************************************************************************
  * Stub functions shared by multiple tests
@@ -394,6 +409,14 @@
     cl::pfn_clReleaseCommandBufferKHR = ::clReleaseCommandBufferKHR;
     cl::pfn_clGetCommandBufferInfoKHR = ::clGetCommandBufferInfoKHR;
 #endif
+#if defined(cl_khr_semaphore)
+    cl::pfn_clCreateSemaphoreWithPropertiesKHR = ::clCreateSemaphoreWithPropertiesKHR;
+    cl::pfn_clReleaseSemaphoreKHR = ::clReleaseSemaphoreKHR;
+    cl::pfn_clRetainSemaphoreKHR = ::clRetainSemaphoreKHR;
+    cl::pfn_clEnqueueWaitSemaphoresKHR = ::clEnqueueWaitSemaphoresKHR;
+    cl::pfn_clEnqueueSignalSemaphoresKHR = ::clEnqueueSignalSemaphoresKHR;
+    cl::pfn_clGetSemaphoreInfoKHR = ::clGetSemaphoreInfoKHR;
+#endif
 
     /* We reach directly into the objects rather than using assignment to
      * avoid the reference counting functions from being called.
@@ -411,6 +434,9 @@
 #if defined(cl_khr_command_buffer)
         commandBufferKhrPool[i]() = make_command_buffer_khr(i);
 #endif
+#if defined(cl_khr_semaphore)
+        semaphorePool[i]() = make_semaphore_khr(i);
+#endif
     }
 
     programRefcounts.reset();
@@ -435,6 +461,9 @@
 #if defined(cl_khr_command_buffer)
         commandBufferKhrPool[i]() = nullptr;
 #endif
+#if defined(cl_khr_semaphore)
+        semaphorePool[i]() = nullptr;
+#endif
     }
 
 #if defined(cl_khr_command_buffer)
@@ -444,6 +473,14 @@
     cl::pfn_clReleaseCommandBufferKHR = nullptr;
     cl::pfn_clGetCommandBufferInfoKHR = nullptr;
 #endif
+#if defined(cl_khr_semaphore)
+    cl::pfn_clCreateSemaphoreWithPropertiesKHR = nullptr;
+    cl::pfn_clReleaseSemaphoreKHR = nullptr;
+    cl::pfn_clRetainSemaphoreKHR = nullptr;
+    cl::pfn_clEnqueueWaitSemaphoresKHR = nullptr;
+    cl::pfn_clEnqueueSignalSemaphoresKHR = nullptr;
+    cl::pfn_clGetSemaphoreInfoKHR = nullptr;
+#endif
 }
 
 /****************************************************************************
@@ -3385,4 +3422,388 @@
 #endif
 }
 
+/****************************************************************************
+ * Tests for cl::Semaphore
+ ****************************************************************************/
+#if defined(cl_khr_semaphore)
+void testMoveAssignSemaphoreNonNull(void);
+void testMoveAssignSemaphoreNull(void);
+void testMoveConstructSemaphoreNonNull(void);
+void testMoveConstructSemaphoreNull(void);
+MAKE_MOVE_TESTS(Semaphore, make_semaphore_khr, clReleaseSemaphoreKHR, semaphorePool);
+#else
+void testMoveAssignSemaphoreNonNull(void) {}
+void testMoveAssignSemaphoreNull(void) {}
+void testMoveConstructSemaphoreNonNull(void) {}
+void testMoveConstructSemaphoreNull(void) {}
+#endif
+
+#if defined(cl_khr_semaphore)
+static cl_int clEnqueueWaitSemaphoresKHR_testEnqueueWaitSemaphores(
+    cl_command_queue command_queue,
+    cl_uint num_sema_objects,
+    const cl_semaphore_khr* sema_objects,
+    const cl_semaphore_payload_khr* sema_payload_list,
+    cl_uint num_events_in_wait_list,
+    const cl_event* event_wait_list,
+    cl_event* event,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL(0, num_calls);
+    TEST_ASSERT_EQUAL_PTR(commandQueuePool[1](), command_queue);
+    TEST_ASSERT_EQUAL(1, num_sema_objects);
+    TEST_ASSERT_NOT_NULL(sema_objects);
+    TEST_ASSERT_EQUAL(make_semaphore_khr(1), *sema_objects);
+    TEST_ASSERT_NOT_NULL(sema_payload_list);
+    TEST_ASSERT_EQUAL(0, num_events_in_wait_list);
+    TEST_ASSERT_NULL(event_wait_list);
+
+    if (event != nullptr)
+    {
+        *event = make_event(1);
+    }
+
+    return CL_SUCCESS;
+}
+
+void testEnqueueWaitSemaphores(void)
+{
+    clEnqueueWaitSemaphoresKHR_StubWithCallback(clEnqueueWaitSemaphoresKHR_testEnqueueWaitSemaphores);
+
+    VECTOR_CLASS<cl::Semaphore> sema_objects;
+    sema_objects.emplace_back(make_semaphore_khr(1));
+    VECTOR_CLASS<cl_semaphore_payload_khr> sema_payloads(1);
+    cl::Event event;
+
+    cl_int status = commandQueuePool[1].enqueueWaitSemaphores(sema_objects, sema_payloads, nullptr, &event);
+    TEST_ASSERT_EQUAL(CL_SUCCESS, status);
+    TEST_ASSERT_EQUAL_PTR(make_event(1), event());
+
+    // prevent destructor from interfering with the test
+    event() = nullptr;
+    sema_objects[0]() = nullptr;
+}
+
+static cl_int clEnqueueSignalSemaphoresKHR_testEnqueueSignalSemaphores(
+    cl_command_queue command_queue,
+    cl_uint num_sema_objects,
+    const cl_semaphore_khr* sema_objects,
+    const cl_semaphore_payload_khr* sema_payload_list,
+    cl_uint num_events_in_wait_list,
+    const cl_event* event_wait_list,
+    cl_event* event,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL(0, num_calls);
+    TEST_ASSERT_EQUAL_PTR(commandQueuePool[1](), command_queue);
+    TEST_ASSERT_EQUAL(1, num_sema_objects);
+    TEST_ASSERT_NOT_NULL(sema_objects);
+    TEST_ASSERT_EQUAL(make_semaphore_khr(2), *sema_objects);
+    TEST_ASSERT_NOT_NULL(sema_payload_list);
+    TEST_ASSERT_EQUAL(0, num_events_in_wait_list);
+    TEST_ASSERT_NULL(event_wait_list);
+
+    if (event != nullptr)
+    {
+        *event = make_event(2);
+    }
+
+    return CL_SUCCESS;
+}
+
+void testEnqueueSignalSemaphores(void)
+{
+    clEnqueueSignalSemaphoresKHR_StubWithCallback(clEnqueueSignalSemaphoresKHR_testEnqueueSignalSemaphores);
+
+    VECTOR_CLASS<cl::Semaphore> sema_objects;
+    sema_objects.emplace_back(make_semaphore_khr(2));
+    VECTOR_CLASS<cl_semaphore_payload_khr> sema_payloads(1);
+    cl::Event event;
+
+    cl_int status = commandQueuePool[1].enqueueSignalSemaphores(sema_objects, sema_payloads, nullptr, &event);
+    TEST_ASSERT_EQUAL(CL_SUCCESS, status);
+    TEST_ASSERT_EQUAL_PTR(make_event(2), event());
+
+    // prevent destructor from interfering with the test
+    event() = nullptr;
+    sema_objects[0]() = nullptr;
+}
+
+cl_semaphore_khr clCreateSemaphoreWithProperties_testSemaphoreWithProperties(
+    cl_context context,
+    const cl_semaphore_properties_khr* sema_props,
+    cl_int* errcode_ret,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL(0, num_calls);
+    TEST_ASSERT_EQUAL_PTR(context, contextPool[0]());
+    TEST_ASSERT_NOT_NULL(sema_props);
+    TEST_ASSERT_EQUAL(CL_SEMAPHORE_TYPE_KHR, *sema_props);
+    TEST_ASSERT_NOT_NULL(errcode_ret);
+    *errcode_ret = CL_SUCCESS;
+    return make_semaphore_khr(1);
+}
+
+void testSemaphoreWithProperties(void)
+{
+    cl_device_id expected_device = make_device_id(0);
+    int device_refcount = 1;
+
+    clGetContextInfo_StubWithCallback(clGetContextInfo_device);
+    clGetDeviceInfo_StubWithCallback(clGetDeviceInfo_platform);
+    clGetPlatformInfo_StubWithCallback(clGetPlatformInfo_version_1_1);
+    prepare_deviceRefcounts(1, &expected_device, &device_refcount);
+
+    clCreateSemaphoreWithPropertiesKHR_StubWithCallback(clCreateSemaphoreWithProperties_testSemaphoreWithProperties);
+
+    VECTOR_CLASS<cl_semaphore_properties_khr> sema_props{CL_SEMAPHORE_TYPE_KHR};
+    cl_int err = CL_INVALID_OPERATION;
+    cl::Semaphore sem(contextPool[0], sema_props, &err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL_PTR(make_semaphore_khr(1), sem());
+
+    // prevent destructor from interfering with the test
+    sem() = nullptr;
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetContext(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_SEMAPHORE_CONTEXT_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(cl_context));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(cl_context);
+    if (param_value != nullptr)
+        *static_cast<cl_context *>(param_value) = make_context(0);
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoContext(void)
+{
+    cl_context expected_context = make_context(0);
+    int context_refcount = 1;
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetContext);
+    prepare_contextRefcounts(1, &expected_context, &context_refcount);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    cl::Context ctx = semaphorePool[0].getInfo<CL_SEMAPHORE_CONTEXT_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL_PTR(make_context(0), ctx());
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetReferenceCount(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_SEMAPHORE_REFERENCE_COUNT_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(cl_uint));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(cl_uint);
+    if (param_value != nullptr)
+        *static_cast<cl_uint *>(param_value) = 1;
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoReferenceCount(void)
+{
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetReferenceCount);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    cl_uint ret = semaphorePool[0].getInfo<CL_SEMAPHORE_REFERENCE_COUNT_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL(1, ret);
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetProperties(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    static const cl_semaphore_properties_khr test_properties[] =
+        {CL_SEMAPHORE_TYPE_KHR,
+         CL_SEMAPHORE_TYPE_BINARY_KHR};
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_SEMAPHORE_PROPERTIES_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(test_properties));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(test_properties);
+    if (param_value != nullptr) {
+        static_cast<cl_semaphore_properties_khr *>(param_value)[0] = test_properties[0];
+        static_cast<cl_semaphore_properties_khr *>(param_value)[1] = test_properties[1];
+    }
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoProperties(void)
+{
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetProperties);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    VECTOR_CLASS<cl_semaphore_properties_khr> ret = semaphorePool[0].getInfo<CL_SEMAPHORE_PROPERTIES_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL(2, ret.size());
+    TEST_ASSERT_EQUAL(CL_SEMAPHORE_TYPE_KHR, ret[0]);
+    TEST_ASSERT_EQUAL(CL_SEMAPHORE_TYPE_BINARY_KHR, ret[1]);
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetType(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_SEMAPHORE_TYPE_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(cl_semaphore_type_khr));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(cl_semaphore_type_khr);
+    if (param_value != nullptr)
+        *static_cast<cl_semaphore_type_khr *>(param_value) = CL_SEMAPHORE_TYPE_BINARY_KHR;
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoType(void)
+{
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetType);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    cl_semaphore_type_khr ret = semaphorePool[0].getInfo<CL_SEMAPHORE_TYPE_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL(CL_SEMAPHORE_TYPE_BINARY_KHR, ret);
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetPayload(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_SEMAPHORE_PAYLOAD_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(cl_semaphore_payload_khr));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(cl_semaphore_payload_khr);
+    if (param_value != nullptr)
+        *static_cast<cl_semaphore_payload_khr *>(param_value) = 1;
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoPayload(void)
+{
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetPayload);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    cl_semaphore_payload_khr ret = semaphorePool[0].getInfo<CL_SEMAPHORE_PAYLOAD_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL(1, ret);
+}
+
+static cl_int clGetSemaphoreInfoKHR_testSemaphoreGetDevices(
+    cl_semaphore_khr sema_object,
+    cl_semaphore_info_khr param_name,
+    size_t param_value_size,
+    void* param_value,
+    size_t* param_value_size_ret,
+    int num_calls)
+{
+    static const cl_device_id test_devices[] =
+        {make_device_id(0), make_device_id(1)};
+    TEST_ASSERT_EQUAL_PTR(semaphorePool[0](), sema_object);
+    TEST_ASSERT_EQUAL_HEX(CL_DEVICE_HANDLE_LIST_KHR, param_name);
+    TEST_ASSERT(param_value == nullptr || param_value_size >= sizeof(test_devices));
+    if (param_value_size_ret != nullptr)
+        *param_value_size_ret = sizeof(test_devices);
+    if (param_value != nullptr) {
+        static_cast<cl_device_id *>(param_value)[0] = test_devices[0];
+        static_cast<cl_device_id *>(param_value)[1] = test_devices[1];
+    }
+
+    return CL_SUCCESS;
+}
+
+void testSemaphoreGetInfoDevicesList(void)
+{
+    cl_device_id expected_devices[] = {make_device_id(0), make_device_id(1)};
+    int device_refcounts[] = {1, 1};
+
+    clGetDeviceInfo_StubWithCallback(clGetDeviceInfo_platform);
+    clGetPlatformInfo_StubWithCallback(clGetPlatformInfo_version_1_1);
+    prepare_deviceRefcounts(ARRAY_SIZE(expected_devices), expected_devices, device_refcounts);
+
+    clGetSemaphoreInfoKHR_StubWithCallback(clGetSemaphoreInfoKHR_testSemaphoreGetDevices);
+
+    cl_int err = CL_INVALID_OPERATION;
+
+    VECTOR_CLASS<cl::Device> ret = semaphorePool[0].getInfo<CL_DEVICE_HANDLE_LIST_KHR>(&err);
+
+    TEST_ASSERT_EQUAL(CL_SUCCESS, err);
+    TEST_ASSERT_EQUAL(2, ret.size());
+    TEST_ASSERT_EQUAL(make_device_id(0), ret[0]());
+    TEST_ASSERT_EQUAL(make_device_id(1), ret[1]());
+}
+
+void testSemaphoreRetain(void)
+{
+    clRetainSemaphoreKHR_ExpectAndReturn(semaphorePool[0](), CL_SUCCESS);
+
+    cl_int status = semaphorePool[0].retain();
+    TEST_ASSERT_EQUAL(CL_SUCCESS, status);
+}
+
+void testSemaphoreRelease(void)
+{
+    clReleaseSemaphoreKHR_ExpectAndReturn(semaphorePool[0](), CL_SUCCESS);
+
+    cl_int status = semaphorePool[0].release();
+    TEST_ASSERT_EQUAL(CL_SUCCESS, status);
+}
+
+#else
+void testEnqueueWaitSemaphores(void) {}
+void testEnqueueSignalSemaphores(void) {}
+void testSemaphoreWithProperties(void) {}
+void testSemaphoreGetInfoContext(void) {}
+void testSemaphoreGetInfoReferenceCount(void) {}
+void testSemaphoreGetInfoProperties(void) {}
+void testSemaphoreGetInfoType(void) {}
+void testSemaphoreGetInfoPayload(void) {}
+void testSemaphoreGetInfoDevicesList(void) {}
+void testSemaphoreRetain(void) {}
+void testSemaphoreRelease(void) {}
+#endif // cl_khr_semaphore
+
 } // extern "C"