//
// Copyright (c) 2017 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "testBase.h"

#if !defined(_WIN32)
#include "unistd.h" // for "sleep" used in the "while (1)" busy wait loop in
#endif
// test_event_flush

const char *sample_long_test_kernel[] = {
    "__kernel void sample_test(__global float *src, __global int *dst)\n"
    "{\n"
    "    int  tid = get_global_id(0);\n"
    "     int  i;\n"
    "\n"
    "    for( i = 0; i < 10000; i++ )\n"
    "    {\n"
    "        dst[tid] = (int)src[tid] * 3;\n"
    "    }\n"
    "\n"
    "}\n"
};

int create_and_execute_kernel(cl_context inContext, cl_command_queue inQueue,
                              cl_program *outProgram, cl_kernel *outKernel,
                              cl_mem *streams, unsigned int lineCount,
                              const char **lines, const char *kernelName,
                              cl_event *outEvent)
{
    size_t threads[1] = { 1000 }, localThreads[1];
    int error;

    if (create_single_kernel_helper(inContext, outProgram, outKernel, lineCount,
                                    lines, kernelName))
    {
        return -1;
    }

    error = get_max_common_work_group_size(inContext, *outKernel, threads[0],
                                           &localThreads[0]);
    test_error(error, "Unable to get work group size to use");

    streams[0] = clCreateBuffer(inContext, CL_MEM_READ_WRITE,
                                sizeof(cl_float) * 1000, NULL, &error);
    test_error(error, "Creating test array failed");
    streams[1] = clCreateBuffer(inContext, CL_MEM_READ_WRITE,
                                sizeof(cl_int) * 1000, NULL, &error);
    test_error(error, "Creating test array failed");

    /* Set the arguments */
    error = clSetKernelArg(*outKernel, 0, sizeof(streams[0]), &streams[0]);
    test_error(error, "Unable to set kernel arguments");
    error = clSetKernelArg(*outKernel, 1, sizeof(streams[1]), &streams[1]);
    test_error(error, "Unable to set kernel arguments");

    error = clEnqueueNDRangeKernel(inQueue, *outKernel, 1, NULL, threads,
                                   localThreads, 0, NULL, outEvent);
    test_error(error, "Unable to execute test kernel");

    return 0;
}

#define SETUP_EVENT(c, q)                                                      \
    clProgramWrapper program;                                                  \
    clKernelWrapper kernel;                                                    \
    clMemWrapper streams[2];                                                   \
    clEventWrapper event;                                                      \
    int error;                                                                 \
    if (create_and_execute_kernel(c, q, &program, &kernel, &streams[0], 1,     \
                                  sample_long_test_kernel, "sample_test",      \
                                  &event))                                     \
        return -1;

#define FINISH_EVENT(_q) clFinish(_q)

const char *IGetStatusString(cl_int status)
{
    static char tempString[128];
    switch (status)
    {
        case CL_COMPLETE: return "CL_COMPLETE";
        case CL_RUNNING: return "CL_RUNNING";
        case CL_QUEUED: return "CL_QUEUED";
        case CL_SUBMITTED: return "CL_SUBMITTED";
        default:
            sprintf(tempString, "<unknown: %d>", (int)status);
            return tempString;
    }
}

/* Note: tests clGetEventStatus and clReleaseEvent (implicitly) */
int test_event_get_execute_status(cl_device_id deviceID, cl_context context,
                                  cl_command_queue queue, int num_elements)
{
    cl_int status;
    SETUP_EVENT(context, queue);

    /* Now wait for it to be done */
    error = clWaitForEvents(1, &event);
    test_error(error, "Unable to wait for event");

    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error,
               "Calling clGetEventStatus to wait for event completion failed");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after event complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    FINISH_EVENT(queue);
    return 0;
}

int test_event_get_info(cl_device_id deviceID, cl_context context,
                        cl_command_queue queue, int num_elements)
{
    SETUP_EVENT(context, queue);

    /* Verify parameters of clGetEventInfo not already tested by other tests */
    cl_command_queue otherQueue;
    size_t size;

    error = clGetEventInfo(event, CL_EVENT_COMMAND_QUEUE, sizeof(otherQueue),
                           &otherQueue, &size);
    test_error(error, "Unable to get event info!");
    // We can not check if this is the right queue because this is an opaque
    // object.
    if (size != sizeof(queue))
    {
        log_error("ERROR: Returned command queue size does not validate "
                  "(expected %d, got %d)\n",
                  (int)sizeof(queue), (int)size);
        return -1;
    }

    cl_command_type type;
    error = clGetEventInfo(event, CL_EVENT_COMMAND_TYPE, sizeof(type), &type,
                           &size);
    test_error(error, "Unable to get event info!");
    if (type != CL_COMMAND_NDRANGE_KERNEL)
    {
        log_error("ERROR: Returned command type does not validate (expected "
                  "%d, got %d)\n",
                  (int)CL_COMMAND_NDRANGE_KERNEL, (int)type);
        return -1;
    }
    if (size != sizeof(type))
    {
        log_error("ERROR: Returned command type size does not validate "
                  "(expected %d, got %d)\n",
                  (int)sizeof(type), (int)size);
        return -1;
    }

    cl_uint count;
    error = clGetEventInfo(event, CL_EVENT_REFERENCE_COUNT, sizeof(count),
                           &count, &size);
    test_error(error, "Unable to get event info for CL_EVENT_REFERENCE_COUNT!");
    if (size != sizeof(count))
    {
        log_error("ERROR: Returned command type size does not validate "
                  "(expected %d, got %d)\n",
                  (int)sizeof(type), (int)size);
        return -1;
    }

    cl_context testCtx;
    error = clGetEventInfo(event, CL_EVENT_CONTEXT, sizeof(testCtx), &testCtx,
                           &size);
    test_error(error, "Unable to get event context info!");
    if (size != sizeof(context))
    {
        log_error("ERROR: Returned context size does not validate (expected "
                  "%d, got %d)\n",
                  (int)sizeof(context), (int)size);
        return -1;
    }
    if (testCtx != context)
    {
        log_error(
            "ERROR: Returned context does not match (expected %p, got %p)\n",
            (void *)context, (void *)testCtx);
        return -1;
    }

    FINISH_EVENT(queue);
    return 0;
}

int test_event_get_write_array_status(cl_device_id deviceID, cl_context context,
                                      cl_command_queue queue, int num_elements)
{
    cl_mem stream;
    cl_float testArray[1024 * 32];
    cl_event event;
    int error;
    cl_int status;


    stream = clCreateBuffer(context, CL_MEM_READ_WRITE,
                            sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");

    error = clEnqueueWriteBuffer(queue, stream, CL_FALSE, 0,
                                 sizeof(cl_float) * 1024 * 32,
                                 (void *)testArray, 0, NULL, &event);
    test_error(error, "Unable to set testing kernel data");

    /* Now wait for it to be done */
    error = clWaitForEvents(1, &event);
    test_error(error, "Unable to wait for event");

    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error,
               "Calling clGetEventStatus to wait for event completion failed");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array write complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }


    clReleaseMemObject(stream);
    clReleaseEvent(event);

    return 0;
}

int test_event_get_read_array_status(cl_device_id deviceID, cl_context context,
                                     cl_command_queue queue, int num_elements)
{
    cl_mem stream;
    cl_float testArray[1024 * 32];
    cl_event event;
    int error;
    cl_int status;


    stream = clCreateBuffer(context, CL_MEM_READ_WRITE,
                            sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");

    error = clEnqueueReadBuffer(queue, stream, CL_FALSE, 0,
                                sizeof(cl_float) * 1024 * 32, (void *)testArray,
                                0, NULL, &event);
    test_error(error, "Unable to get testing kernel data");


    /* It should still be running... */
    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");

    if (status != CL_RUNNING && status != CL_QUEUED && status != CL_SUBMITTED
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "during array read (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    /* Now wait for it to be done */
    error = clWaitForEvents(1, &event);
    test_error(error, "Unable to wait for event");

    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error,
               "Calling clGetEventStatus to wait for event completion failed");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array read complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }


    clReleaseMemObject(stream);
    clReleaseEvent(event);

    return 0;
}

/* clGetEventStatus not implemented yet */

int test_event_wait_for_execute(cl_device_id deviceID, cl_context context,
                                cl_command_queue queue, int num_elements)
{
    cl_int status;
    SETUP_EVENT(context, queue);

    /* Now we wait for it to be done, then test the status again */
    error = clWaitForEvents(1, &event);
    test_error(error, "Unable to wait for execute event");

    /* Make sure it worked */
    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after event complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    FINISH_EVENT(queue);
    return 0;
}

int test_event_wait_for_array(cl_device_id deviceID, cl_context context,
                              cl_command_queue queue, int num_elements)
{
    cl_mem streams[2];
    cl_float readArray[1024 * 32];
    cl_float writeArray[1024 * 32];
    cl_event events[2];
    int error;
    cl_int status;


    streams[0] = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");
    streams[1] = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");

    error = clEnqueueReadBuffer(queue, streams[0], CL_FALSE, 0,
                                sizeof(cl_float) * 1024 * 32, (void *)readArray,
                                0, NULL, &events[0]);
    test_error(error, "Unable to read testing kernel data");

    error = clEnqueueWriteBuffer(queue, streams[1], CL_FALSE, 0,
                                 sizeof(cl_float) * 1024 * 32,
                                 (void *)writeArray, 0, NULL, &events[1]);
    test_error(error, "Unable to write testing kernel data");

    /* Both should still be running */
    error = clGetEventInfo(events[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_RUNNING && status != CL_QUEUED && status != CL_SUBMITTED
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "during array read (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    error = clGetEventInfo(events[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_RUNNING && status != CL_QUEUED && status != CL_SUBMITTED
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "during array write (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    /* Now try waiting for both */
    error = clWaitForEvents(2, events);
    test_error(error, "Unable to wait for array events");

    /* Double check status on both */
    error = clGetEventInfo(events[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array read complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    error = clGetEventInfo(events[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array write complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    clReleaseMemObject(streams[0]);
    clReleaseMemObject(streams[1]);
    clReleaseEvent(events[0]);
    clReleaseEvent(events[1]);

    return 0;
}

int test_event_flush(cl_device_id deviceID, cl_context context,
                     cl_command_queue queue, int num_elements)
{
    cl_int status;
    SETUP_EVENT(context, queue);

    /* Now flush. Note that we can't guarantee this actually lets the op finish,
     * but we can guarantee it's no longer queued */
    error = clFlush(queue);
    test_error(error, "Unable to flush events");

    /* Make sure it worked */
    while (1)
    {
        error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                               sizeof(status), &status, NULL);
        test_error(error, "Calling clGetEventStatus didn't work!");

        if (status != CL_QUEUED) break;

#if !defined(_WIN32)
        sleep(1); // give it some time here.
#else // _WIN32
        Sleep(1000);
#endif
    }

    /*
    CL_QUEUED (command has been enqueued in the command-queue),
    CL_SUBMITTED (enqueued command has been submitted by the host to the device
    associated with the command-queue), CL_RUNNING (device is currently
    executing this command), CL_COMPLETE (the command has completed), or Error
    code given by a negative integer value. (command was abnormally terminated –
    this may be caused by a bad memory access etc.).
    */
    if (status != CL_COMPLETE && status != CL_SUBMITTED && status != CL_RUNNING
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after event flush (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    /* Now wait */
    error = clFinish(queue);
    test_error(error, "Unable to finish events");

    FINISH_EVENT(queue);
    return 0;
}


int test_event_finish_execute(cl_device_id deviceID, cl_context context,
                              cl_command_queue queue, int num_elements)
{
    cl_int status;
    SETUP_EVENT(context, queue);

    /* Now flush and finish all ops */
    error = clFinish(queue);
    test_error(error, "Unable to finish all events");

    /* Make sure it worked */
    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after event complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    FINISH_EVENT(queue);
    return 0;
}

int test_event_finish_array(cl_device_id deviceID, cl_context context,
                            cl_command_queue queue, int num_elements)
{
    cl_mem streams[2];
    cl_float readArray[1024 * 32];
    cl_float writeArray[1024 * 32];
    cl_event events[2];
    int error;
    cl_int status;


    streams[0] = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");
    streams[1] = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                sizeof(cl_float) * 1024 * 32, NULL, &error);
    test_error(error, "Creating test array failed");

    error = clEnqueueReadBuffer(queue, streams[0], CL_FALSE, 0,
                                sizeof(cl_float) * 1024 * 32, (void *)readArray,
                                0, NULL, &events[0]);
    test_error(error, "Unable to read testing kernel data");

    error = clEnqueueWriteBuffer(queue, streams[1], CL_FALSE, 0,
                                 sizeof(cl_float) * 1024 * 32,
                                 (void *)writeArray, 0, NULL, &events[1]);
    test_error(error, "Unable to write testing kernel data");

    /* Both should still be running */
    error = clGetEventInfo(events[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_RUNNING && status != CL_QUEUED && status != CL_SUBMITTED
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "during array read (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    error = clGetEventInfo(events[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_RUNNING && status != CL_QUEUED && status != CL_SUBMITTED
        && status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "during array write (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    /* Now try finishing all ops */
    error = clFinish(queue);
    test_error(error, "Unable to finish all events");

    /* Double check status on both */
    error = clGetEventInfo(events[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array read complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    error = clGetEventInfo(events[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventStatus didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetErrorStatus "
                  "after array write complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    clReleaseMemObject(streams[0]);
    clReleaseMemObject(streams[1]);
    clReleaseEvent(events[0]);
    clReleaseEvent(events[1]);

    return 0;
}


#define NUM_EVENT_RUNS 100

int test_event_release_before_done(cl_device_id deviceID, cl_context context,
                                   cl_command_queue queue, int num_elements)
{
    // Create a kernel to run
    clProgramWrapper program;
    clKernelWrapper kernel[NUM_EVENT_RUNS];
    size_t threads[1] = { 1000 };
    cl_event events[NUM_EVENT_RUNS];
    cl_int status;
    clMemWrapper streams[NUM_EVENT_RUNS][2];
    int error, i;

    // Create a kernel
    if (create_single_kernel_helper(context, &program, &kernel[0], 1,
                                    sample_long_test_kernel, "sample_test"))
    {
        return -1;
    }

    for (i = 1; i < NUM_EVENT_RUNS; i++)
    {
        kernel[i] = clCreateKernel(program, "sample_test", &error);
        test_error(error, "Unable to create kernel");
    }

    error =
        get_max_common_work_group_size(context, kernel[0], 1024, &threads[0]);
    test_error(error, "Unable to get work group size to use");

    // Create a set of streams to use as arguments
    for (i = 0; i < NUM_EVENT_RUNS; i++)
    {
        streams[i][0] =
            clCreateBuffer(context, CL_MEM_READ_WRITE,
                           sizeof(cl_float) * threads[0], NULL, &error);
        streams[i][1] =
            clCreateBuffer(context, CL_MEM_READ_WRITE,
                           sizeof(cl_int) * threads[0], NULL, &error);
        if ((streams[i][0] == NULL) || (streams[i][1] == NULL))
        {
            log_error("ERROR: Unable to allocate testing streams");
            return -1;
        }
    }

    // Execute the kernels one by one, hopefully making sure they won't be done
    // by the time we get to the end
    for (i = 0; i < NUM_EVENT_RUNS; i++)
    {
        error = clSetKernelArg(kernel[i], 0, sizeof(cl_mem), &streams[i][0]);
        error |= clSetKernelArg(kernel[i], 1, sizeof(cl_mem), &streams[i][1]);
        test_error(error, "Unable to set kernel arguments");

        error = clEnqueueNDRangeKernel(queue, kernel[i], 1, NULL, threads,
                                       threads, 0, NULL, &events[i]);
        test_error(error, "Unable to execute test kernel");
    }

    // Free all but the last event
    for (i = 0; i < NUM_EVENT_RUNS - 1; i++)
    {
        clReleaseEvent(events[i]);
    }

    // Get status on the last one, then free it
    error = clGetEventInfo(events[NUM_EVENT_RUNS - 1],
                           CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(status),
                           &status, NULL);
    test_error(error, "Unable to get event status");

    clReleaseEvent(events[NUM_EVENT_RUNS - 1]);

    // Was the status still-running?
    if (status == CL_COMPLETE)
    {
        log_info("WARNING: Events completed before they could be released, so "
                 "test is a null-op. Increase workload and try again.");
    }
    else if (status == CL_RUNNING || status == CL_QUEUED
             || status == CL_SUBMITTED)
    {
        log_info("Note: Event status was running or queued when released, so "
                 "test was good.\n");
    }

    // If we didn't crash by now, the test succeeded
    clFinish(queue);

    return 0;
}

int test_event_enqueue_marker(cl_device_id deviceID, cl_context context,
                              cl_command_queue queue, int num_elements)
{
    cl_int status;
    SETUP_EVENT(context, queue);

    /* Now we queue a marker and wait for that, which--since it queues
     * afterwards--should guarantee the execute finishes too */
    clEventWrapper markerEvent;
    // error = clEnqueueMarker( queue, &markerEvent );

#ifdef CL_VERSION_1_2
    error = clEnqueueMarkerWithWaitList(queue, 0, NULL, &markerEvent);
#else
    error = clEnqueueMarker(queue, &markerEvent);
#endif
    test_error(error, "Unable to queue marker");
    /* Now we wait for it to be done, then test the status again */
    error = clWaitForEvents(1, &markerEvent);
    test_error(error, "Unable to wait for marker event");

    /* Check the status of the first event */
    error = clGetEventInfo(event, CL_EVENT_COMMAND_EXECUTION_STATUS,
                           sizeof(status), &status, NULL);
    test_error(error, "Calling clGetEventInfo didn't work!");
    if (status != CL_COMPLETE)
    {
        log_error("ERROR: Incorrect status returned from clGetEventInfo after "
                  "event complete (%d:%s)\n",
                  status, IGetStatusString(status));
        return -1;
    }

    FINISH_EVENT(queue);
    return 0;
}

#ifdef CL_VERSION_1_2
int test_event_enqueue_marker_with_event_list(cl_device_id deviceID,
                                              cl_context context,
                                              cl_command_queue queue,
                                              int num_elements)
{
    SETUP_EVENT(context, queue);
    cl_event event_list[3] = { NULL, NULL, NULL };

    size_t threads[1] = { 10 }, localThreads[1] = { 1 };
    cl_uint event_count = 2;
    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[0]);
    test_error(error, " clEnqueueMarkerWithWaitList   1 ");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[1]);
    test_error(error, " clEnqueueMarkerWithWaitList 2");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, NULL);
    test_error(error, " clEnqueueMarkerWithWaitList  3");

    // test the case event returned
    error = clEnqueueMarkerWithWaitList(queue, event_count, event_list,
                                        &event_list[2]);
    test_error(error, " clEnqueueMarkerWithWaitList ");

    error = clReleaseEvent(event_list[0]);
    error |= clReleaseEvent(event_list[1]);
    test_error(error, "clReleaseEvent");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[0]);
    test_error(error, " clEnqueueMarkerWithWaitList   1 -1 ");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[1]);
    test_error(error, " clEnqueueMarkerWithWaitList  2-2");

    // test the case event =NULL,   caused [CL_INVALID_VALUE] : OpenCL Error :
    // clEnqueueMarkerWithWaitList failed: event is a NULL value
    error = clEnqueueMarkerWithWaitList(queue, event_count, event_list, NULL);
    test_error(error, " clEnqueueMarkerWithWaitList ");

    error = clReleaseEvent(event_list[0]);
    error |= clReleaseEvent(event_list[1]);
    error |= clReleaseEvent(event_list[2]);
    test_error(error, "clReleaseEvent");

    FINISH_EVENT(queue);
    return 0;
}

int test_event_enqueue_barrier_with_event_list(cl_device_id deviceID,
                                               cl_context context,
                                               cl_command_queue queue,
                                               int num_elements)
{
    SETUP_EVENT(context, queue);
    cl_event event_list[3] = { NULL, NULL, NULL };

    size_t threads[1] = { 10 }, localThreads[1] = { 1 };
    cl_uint event_count = 2;
    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[0]);
    test_error(error, " clEnqueueBarrierWithWaitList   1 ");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[1]);
    test_error(error, " clEnqueueBarrierWithWaitList 2");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, NULL);
    test_error(error, " clEnqueueBarrierWithWaitList  20");

    // test the case event returned
    error = clEnqueueBarrierWithWaitList(queue, event_count, event_list,
                                         &event_list[2]);
    test_error(error, " clEnqueueBarrierWithWaitList ");

    clReleaseEvent(event_list[0]);
    clReleaseEvent(event_list[1]);

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[0]);
    test_error(error, " clEnqueueBarrierWithWaitList   1 ");

    error = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, threads,
                                   localThreads, 0, NULL, &event_list[1]);
    test_error(error, " clEnqueueBarrierWithWaitList 2");

    // test the case event =NULL,   caused [CL_INVALID_VALUE] : OpenCL Error :
    // clEnqueueMarkerWithWaitList failed: event is a NULL value
    error = clEnqueueBarrierWithWaitList(queue, event_count, event_list, NULL);
    test_error(error, " clEnqueueBarrierWithWaitList ");

    clReleaseEvent(event_list[0]);
    clReleaseEvent(event_list[1]);
    clReleaseEvent(event_list[2]);

    FINISH_EVENT(queue);
    return 0;
}
#endif
