//
// 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_flags)(CL_MEM_READ_WRITE),  sizeof(cl_float) * 1000, NULL, &error);
    test_error( error, "Creating test array failed" );
    streams[1] = clCreateBuffer(inContext, (cl_mem_flags)(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_flags)(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_flags)(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_flags)(CL_MEM_READ_WRITE),  sizeof(cl_float) * 1024 * 32, NULL, &error );
    test_error( error, "Creating test array failed" );
    streams[1] = clCreateBuffer( context, (cl_mem_flags)(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)
{
    int loopCount = 0;
    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
        ++loopCount;
          }

/*
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_flags)(CL_MEM_READ_WRITE),  sizeof(cl_float) * 1024 * 32, NULL, &error );
    test_error( error, "Creating test array failed" );
    streams[1] = clCreateBuffer( context, (cl_mem_flags)(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_flags)(CL_MEM_READ_WRITE),  sizeof(cl_float) * threads[0], NULL, &error );
        streams[i][1] = clCreateBuffer( context, (cl_mem_flags)(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)
{

    cl_int status;
    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)
{

    cl_int status;
    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
