diff --git a/test_common/harness/parseParameters.cpp b/test_common/harness/parseParameters.cpp index 65167116..97aedb7c 100644 --- a/test_common/harness/parseParameters.cpp +++ b/test_common/harness/parseParameters.cpp @@ -58,6 +58,13 @@ void helpInfo() with a very small subset of the tests. This option should not be used for conformance submission (default: disabled). + --invalid-object-scenarios=,.... + Specify different scenarios to use when + testing for object validity. Options can be: + nullptr To use a nullptr (default) + valid_object_wrong_type To use a valid_object which is not the correct type + NOTE: valid_object_wrong_type option is not required for OpenCL conformance. + For offline compilation (binary and spir-v modes) only: --compilation-cache-mode Specify a compilation caching mode: @@ -104,6 +111,7 @@ int parseCustomParam(int argc, const char *argv[], const char *ignore) } delArg = 0; + size_t i_object_length = strlen("--invalid-object-scenarios="); if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { @@ -264,6 +272,32 @@ int parseCustomParam(int argc, const char *argv[], const char *ignore) return -1; } } + else if (!strncmp(argv[i], + "--invalid-object-scenarios=", i_object_length)) + { + if (strlen(argv[i]) > i_object_length) + { + delArg++; + gInvalidObject = 0; + std::string invalid_objects(argv[i]); + + if (invalid_objects.find("nullptr") != std::string::npos) + { + gInvalidObject |= InvalidObject::Nullptr; + } + if (invalid_objects.find("valid_object_wrong_type") + != std::string::npos) + { + gInvalidObject |= InvalidObject::ValidObjectWrongType; + } + } + else + { + log_error("Program argument for --invalid-object-scenarios was " + "not specified.\n"); + return -1; + } + } // cleaning parameters from argv tab for (int j = i; j < argc - delArg; j++) argv[j] = argv[j + delArg]; diff --git a/test_common/harness/testHarness.cpp b/test_common/harness/testHarness.cpp index c745a639..301b86d0 100644 --- a/test_common/harness/testHarness.cpp +++ b/test_common/harness/testHarness.cpp @@ -60,6 +60,7 @@ int gInfNanSupport = 1; int gIsEmbedded = 0; int gHasLong = 1; bool gCoreILProgram = true; +int gInvalidObject = InvalidObject::Nullptr; #define DEFAULT_NUM_ELEMENTS 0x4000 diff --git a/test_common/harness/testHarness.h b/test_common/harness/testHarness.h index cc9d8212..76fda76f 100644 --- a/test_common/harness/testHarness.h +++ b/test_common/harness/testHarness.h @@ -22,6 +22,7 @@ #include #include +#include class Version { public: @@ -257,6 +258,37 @@ extern std::string get_platform_info_string(cl_platform_id platform, cl_platform_info param_name); extern bool is_platform_extension_available(cl_platform_id platform, const char *extensionName); +enum InvalidObject +{ + Nullptr = 1 << 0, + ValidObjectWrongType = 1 << 1, +}; + +extern int gInvalidObject; + + +template std::vector get_invalid_objects(cl_device_id device) +{ + std::vector ret; + if ((gInvalidObject & InvalidObject::Nullptr) + && !(std::is_same::value)) + { + ret.push_back(nullptr); + } + if (gInvalidObject & InvalidObject::ValidObjectWrongType) + { + if (std::is_same::value) + { + cl_platform_id platform = getPlatformFromDevice(device); + ret.push_back(reinterpret_cast(platform)); + } + else + { + ret.push_back(reinterpret_cast(device)); + } + } + return ret; +} #if !defined(__APPLE__) void memset_pattern4(void *, const void *, size_t); diff --git a/test_conformance/api/CMakeLists.txt b/test_conformance/api/CMakeLists.txt index c0ab77b5..942fef89 100644 --- a/test_conformance/api/CMakeLists.txt +++ b/test_conformance/api/CMakeLists.txt @@ -8,6 +8,7 @@ set(${MODULE_NAME}_SOURCES negative_queue.cpp negative_enqueue_marker.cpp negative_enqueue_map_image.cpp + negative_device.cpp test_api_consistency.cpp test_bool.cpp test_retain.cpp diff --git a/test_conformance/api/negative_device.cpp b/test_conformance/api/negative_device.cpp new file mode 100644 index 00000000..d51c685c --- /dev/null +++ b/test_conformance/api/negative_device.cpp @@ -0,0 +1,526 @@ +// +// Copyright (c) 2021 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" +#include "harness/testHarness.h" +#include + +/* Negative Tests for clGetDeviceInfo */ +REGISTER_TEST(negative_get_device_info) +{ + + cl_device_type device_type = 0; + cl_int err(CL_SUCCESS); + for (auto invalid_device : get_invalid_objects(device)) + { + err = clGetDeviceInfo(invalid_device, CL_DEVICE_TYPE, + sizeof(device_type), &device_type, nullptr); + test_failure_error_ret(err, CL_INVALID_DEVICE, + "clGetDeviceInfo should return " + "CL_INVALID_DEVICE when: \"device is not " + "a valid device\"", + TEST_FAIL); + } + + constexpr cl_device_info INVALID_PARAM_VALUE = 0; + err = clGetDeviceInfo(device, INVALID_PARAM_VALUE, 0, nullptr, nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetDeviceInfo should return CL_INVALID_VALUE when: \"param_name is " + "not one of the supported values\"", + TEST_FAIL); + + err = clGetDeviceInfo(device, CL_DEVICE_TYPE, 0, &device_type, nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetDeviceInfo should return CL_INVALID_VALUE when: \"size in bytes " + "specified by param_value_size is < size of return type and " + "param_value is not a NULL value\"", + TEST_FAIL); + + return TEST_PASS; +} + +/* Negative Tests for clGetDeviceIDs */ +REGISTER_TEST(negative_get_device_ids) +{ + cl_platform_id platform = getPlatformFromDevice(device); + + cl_device_id devices = nullptr; + + cl_int err(CL_SUCCESS); + for (auto invalid_platform : get_invalid_objects(device)) + { + err = clGetDeviceIDs(invalid_platform, CL_DEVICE_TYPE_DEFAULT, 1, + &devices, nullptr); + test_failure_error_ret(err, CL_INVALID_PLATFORM, + "clGetDeviceIDs should return " + "CL_INVALID_PLATFORM when: \"platform is " + "not a valid platform\"", + TEST_FAIL); + } + + cl_device_type INVALID_DEVICE_TYPE = 0; + err = clGetDeviceIDs(platform, INVALID_DEVICE_TYPE, 1, &devices, nullptr); + test_failure_error_ret( + err, CL_INVALID_DEVICE_TYPE, + "clGetDeviceIDs should return CL_INVALID_DEVICE_TYPE when: " + "\"device_type is not a valid value\"", + TEST_FAIL); + + err = + clGetDeviceIDs(platform, CL_DEVICE_TYPE_DEFAULT, 0, &devices, nullptr); + test_failure_error_ret(err, CL_INVALID_VALUE, + "clGetDeviceIDs should return when: \"num_entries " + "is equal to zero and devices is not NULL\"", + TEST_FAIL); + + err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_DEFAULT, 1, nullptr, nullptr); + test_failure_error_ret(err, CL_INVALID_VALUE, + "clGetDeviceIDs should return CL_INVALID_VALUE " + "when: \"both num_devices and devices are NULL\"", + TEST_FAIL); + + devices = nullptr; + std::vector device_types{ CL_DEVICE_TYPE_CPU, + CL_DEVICE_TYPE_GPU, + CL_DEVICE_TYPE_ACCELERATOR }; + if (get_device_cl_version(device) >= Version(1, 2)) + { + device_types.push_back(CL_DEVICE_TYPE_CUSTOM); + } + + bool platform_supports_all_device_types = true; + for (auto device_type : device_types) + { + err = clGetDeviceIDs(platform, device_type, 1, &devices, nullptr); + if (err == CL_SUCCESS) + { + continue; + } + platform_supports_all_device_types = false; + break; + } + if (platform_supports_all_device_types) + { + log_info("Platform has every Device Type... Skipping Test\n"); + } + else + { + test_failure_error_ret( + err, CL_DEVICE_NOT_FOUND, + "clGetDeviceIDs should return CL_DEVICE_NOT_FOUND when: \"no " + "OpenCL devices that matched device_type were found\"", + TEST_FAIL); + } + + return TEST_PASS; +} + +/* Negative Tests for clGetDeviceAndHostTimer */ +REGISTER_TEST_VERSION(negative_get_device_and_host_timer, Version(2, 1)) +{ + cl_ulong *device_timestamp = nullptr, *host_timestamp = nullptr; + cl_int err = CL_SUCCESS; + for (auto invalid_device : get_invalid_objects(device)) + { + err = clGetDeviceAndHostTimer(invalid_device, device_timestamp, + host_timestamp); + test_failure_error_ret( + err, CL_INVALID_DEVICE, + "clGetDeviceAndHostTimer should return CL_INVALID_DEVICE when: " + "\"device is not a valid device\"", + TEST_FAIL); + } + + cl_platform_id platform = getPlatformFromDevice(device); + + // Initialise timer_resolution to a Non-0 value as CL2.1/2 devices must + // support timer synchronisation + cl_ulong timer_resolution = 1; + auto device_version = get_device_cl_version(device); + err = + clGetPlatformInfo(platform, CL_PLATFORM_HOST_TIMER_RESOLUTION, + sizeof(timer_resolution), &timer_resolution, nullptr); + test_error(err, "clGetPlatformInfo failed"); + if (timer_resolution == 0 + && (device_version == Version(2, 1) || device_version == Version(2, 2))) + { + log_error("Support for device and host timer synchronization is " + "required for platforms supporting OpenCL 2.1 or 2.2."); + return TEST_FAIL; + } + + if (timer_resolution != 0) + { + log_info("Platform Supports Timers\n"); + log_info("Skipping CL_INVALID_OPERATION tests\n"); + + err = clGetDeviceAndHostTimer(device, nullptr, host_timestamp); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetDeviceAndHostTimer should return CL_INVALID_VALUE when: " + "\"host_timestamp or device_timestamp is NULL\" using nullptr for " + "device_timestamp ", + TEST_FAIL); + + err = clGetDeviceAndHostTimer(device, device_timestamp, nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clGetDeviceAndHostTimer should return CL_INVALID_VALUE when: " + "\"host_timestamp or device_timestamp is NULL\" using nullptr for " + "host_timestamp ", + TEST_FAIL); + } + else + { + log_info("Platform does not Support Timers\n"); + log_info("Skipping CL_INVALID_VALUE tests\n"); + + err = clGetDeviceAndHostTimer(device, device_timestamp, host_timestamp); + test_failure_error_ret( + err, CL_INVALID_OPERATION, + "clGetDeviceAndHostTimer should return CL_INVALID_OPERATION when: " + "\"the platform associated with device does not support device and " + "host timer synchronization\"", + TEST_FAIL); + } + + return TEST_PASS; +} + +/* Negative Tests for clGetHostTimer */ +REGISTER_TEST_VERSION(negative_get_host_timer, Version(2, 1)) +{ + cl_ulong host_timestamp = 0; + cl_int err = CL_SUCCESS; + for (auto invalid_device : get_invalid_objects(device)) + { + err = clGetHostTimer(invalid_device, &host_timestamp); + test_failure_error_ret(err, CL_INVALID_DEVICE, + "clGetHostTimer should return CL_INVALID_DEVICE " + "when: \"device is not " + "a valid device\"", + TEST_FAIL); + } + + cl_platform_id platform = getPlatformFromDevice(device); + // Initialise timer_resolution to a Non-0 value as CL2.1/2 devices must + // support timer synchronisation + cl_ulong timer_resolution = 1; + auto device_version = get_device_cl_version(device); + err = + clGetPlatformInfo(platform, CL_PLATFORM_HOST_TIMER_RESOLUTION, + sizeof(timer_resolution), &timer_resolution, nullptr); + test_error(err, "clGetPlatformInfo failed"); + if (timer_resolution == 0 + && (device_version == Version(2, 1) || device_version == Version(2, 2))) + { + log_error("Support for device and host timer synchronization is " + "required for platforms supporting OpenCL 2.1 or 2.2."); + return TEST_FAIL; + } + + if (timer_resolution != 0) + { + log_info("Platform Supports Timers\n"); + log_info("Skipping CL_INVALID_OPERATION tests\n"); + + err = clGetHostTimer(device, nullptr); + test_failure_error_ret(err, CL_INVALID_VALUE, + "clGetHostTimer should return CL_INVALID_VALUE " + "when: \"host_timestamp is NULL\"", + TEST_FAIL); + } + else + { + log_info("Platform does not Support Timers\n"); + log_info("Skipping CL_INVALID_VALUE tests\n"); + + err = clGetHostTimer(device, &host_timestamp); + test_failure_error_ret( + err, CL_INVALID_OPERATION, + "clGetHostTimer should return CL_INVALID_OPERATION when: \"the " + "platform associated with device does not support device and host " + "timer synchronization\"", + TEST_FAIL); + } + + return TEST_PASS; +} + +/* Negative Tests for clCreateSubDevices */ +enum SupportedPartitionSchemes +{ + None = 0, + Equally = 1 << 0, + Counts = 1 << 1, + Affinity = 1 << 2, + All_Schemes = Affinity | Counts | Equally, +}; + +static int get_supported_properties(cl_device_id device) +{ + size_t number_of_properties = 0; + int err = clGetDeviceInfo(device, CL_DEVICE_PARTITION_PROPERTIES, 0, + nullptr, &number_of_properties); + test_error(err, "clGetDeviceInfo"); + std::vector supported_properties( + number_of_properties / sizeof(cl_device_partition_property)); + err = clGetDeviceInfo(device, CL_DEVICE_PARTITION_PROPERTIES, + number_of_properties, &supported_properties.front(), + nullptr); + test_error(err, "clGetDeviceInfo"); + int ret = SupportedPartitionSchemes::None; + for (auto property : supported_properties) + { + switch (property) + { + case CL_DEVICE_PARTITION_EQUALLY: + ret |= SupportedPartitionSchemes::Equally; + break; + case CL_DEVICE_PARTITION_BY_COUNTS: + ret |= SupportedPartitionSchemes::Counts; + break; + case CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN: + ret |= SupportedPartitionSchemes::Affinity; + break; + default: break; + } + } + return ret; +} + +static std::vector +get_invalid_properties(int unsupported_properties) +{ + if (unsupported_properties & SupportedPartitionSchemes::Equally) + { + return { CL_DEVICE_PARTITION_EQUALLY, 1, 0 }; + } + else if (unsupported_properties & SupportedPartitionSchemes::Counts) + { + return { CL_DEVICE_PARTITION_BY_COUNTS, 1, + CL_DEVICE_PARTITION_BY_COUNTS_LIST_END }; + } + else if (unsupported_properties & SupportedPartitionSchemes::Affinity) + { + return { CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN, + CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE, 0 }; + } + else + { + return {}; + } +} + +static cl_uint get_uint_device_info(const cl_device_id device, + const cl_device_info param_name) +{ + cl_uint ret = 0; + cl_int err = + clGetDeviceInfo(device, param_name, sizeof(ret), &ret, nullptr); + test_error(err, "clGetDeviceInfo"); + return ret; +} + +REGISTER_TEST_VERSION(negative_create_sub_devices, Version(1, 2)) +{ + int supported_properties = get_supported_properties(device); + if (supported_properties == SupportedPartitionSchemes::None) + { + printf("Device does not support creating subdevices... Skipping\n"); + return TEST_SKIPPED_ITSELF; + } + + cl_device_partition_property properties[4] = {}; + cl_uint max_compute_units = + get_uint_device_info(device, CL_DEVICE_MAX_COMPUTE_UNITS); + cl_uint max_sub_devices = + get_uint_device_info(device, CL_DEVICE_PARTITION_MAX_SUB_DEVICES); + std::vector out_devices; + cl_uint max_for_partition = 0; + if (supported_properties & SupportedPartitionSchemes::Equally) + { + properties[0] = CL_DEVICE_PARTITION_EQUALLY; + properties[1] = 1; + properties[2] = 0; + out_devices.resize(static_cast(max_compute_units)); + max_for_partition = max_compute_units; + } + else if (supported_properties & SupportedPartitionSchemes::Counts) + { + properties[0] = CL_DEVICE_PARTITION_BY_COUNTS; + properties[1] = 1; + properties[2] = CL_DEVICE_PARTITION_BY_COUNTS_LIST_END; + out_devices.resize(static_cast(max_sub_devices)); + max_for_partition = max_sub_devices; + } + else + { + properties[0] = CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN; + properties[1] = CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE; + properties[2] = 0; + } + + properties[3] = 0; + + cl_int err(CL_SUCCESS); + for (auto invalid_device : get_invalid_objects(device)) + { + err = clCreateSubDevices(invalid_device, properties, out_devices.size(), + out_devices.data(), nullptr); + test_failure_error_ret(err, CL_INVALID_DEVICE, + "clCreateSubDevices should return " + "CL_INVALID_DEVICE when: \"in_device " + "is not a valid device\"", + TEST_FAIL); + } + + err = clCreateSubDevices(device, nullptr, out_devices.size(), + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clCreateSubDevices should return CL_INVALID_VALUE when: \"values " + "specified in properties are not valid\" using a nullptr", + TEST_FAIL); + + err = + clCreateSubDevices(device, properties, 0, out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clCreateSubDevices should return CL_INVALID_VALUE when: \"out_devices " + "is not NULL and num_devices is less than the number of sub-devices " + "created by the partition scheme\"", + TEST_FAIL); + + if (supported_properties != SupportedPartitionSchemes::All_Schemes) + { + std::vector invalid_properties = + get_invalid_properties(supported_properties + ^ SupportedPartitionSchemes::All_Schemes); + err = + clCreateSubDevices(device, invalid_properties.data(), + out_devices.size(), out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clCreateSubDevices should return CL_INVALID_VALUE when: \"values " + "specified in properties are valid but not supported by the " + "device\"", + TEST_FAIL); + } + + if (supported_properties & SupportedPartitionSchemes::Equally) + { + properties[1] = max_compute_units; + err = clCreateSubDevices(device, properties, max_for_partition, + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_DEVICE_PARTITION_FAILED, + "clCreateSubDevices should return CL_DEVICE_PARTITION_FAILED when: " + "\"the partition name is supported by the implementation but " + "in_device could not be further partitioned\"", + TEST_FAIL); + } + + constexpr cl_device_partition_property INVALID_PARTITION_PROPERTY = + -1; // Aribitrary Invalid number + properties[0] = INVALID_PARTITION_PROPERTY; + err = clCreateSubDevices(device, properties, out_devices.size(), + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_VALUE, + "clCreateSubDevices should return CL_INVALID_VALUE when: \"values " + "specified in properties are not valid\" using an invalid property", + TEST_FAIL); + + if (supported_properties & SupportedPartitionSchemes::Counts) + { + properties[0] = CL_DEVICE_PARTITION_BY_COUNTS; + properties[1] = max_sub_devices + 1; + err = clCreateSubDevices(device, properties, max_sub_devices + 1, + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_DEVICE_PARTITION_COUNT, + "clCreateSubDevices should return " + "CL_INVALID_DEVICE_PARTITION_COUNT when: \"the partition name " + "specified in properties is CL_DEVICE_ PARTITION_BY_COUNTS and the " + "number of sub-devices requested exceeds " + "CL_DEVICE_PARTITION_MAX_SUB_DEVICES\"", + TEST_FAIL); + + properties[1] = -1; + err = clCreateSubDevices(device, properties, out_devices.size(), + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_DEVICE_PARTITION_COUNT, + "clCreateSubDevices should return " + "CL_INVALID_DEVICE_PARTITION_COUNT when: \"the number of compute " + "units requested for one or more sub-devices is less than zero\"", + TEST_FAIL); + } + + if (supported_properties & SupportedPartitionSchemes::Equally) + { + properties[0] = CL_DEVICE_PARTITION_EQUALLY; + properties[1] = max_compute_units + 1; + err = clCreateSubDevices(device, properties, max_compute_units + 1, + out_devices.data(), nullptr); + test_failure_error_ret( + err, CL_INVALID_DEVICE_PARTITION_COUNT, + "clCreateSubDevices should return " + "CL_INVALID_DEVICE_PARTITION_COUNT when: \"the total number of " + "compute units requested exceeds CL_DEVICE_MAX_COMPUTE_UNITS for " + "in_device\"", + TEST_FAIL); + } + + return TEST_PASS; +} + +/* Negative Tests for clRetainDevice */ +REGISTER_TEST_VERSION(negative_retain_device, Version(1, 2)) +{ + cl_int err(CL_SUCCESS); + for (auto invalid_device : get_invalid_objects(device)) + { + err = clRetainDevice(invalid_device); + test_failure_error_ret(err, CL_INVALID_DEVICE, + "clRetainDevice should return CL_INVALID_DEVICE " + "when: \"device is not " + "a valid device\"", + TEST_FAIL); + } + + return TEST_PASS; +} + +/* Negative Tests for clReleaseDevice */ +REGISTER_TEST_VERSION(negative_release_device, Version(1, 2)) +{ + cl_int err(CL_SUCCESS); + for (auto invalid_device : get_invalid_objects(device)) + { + err = clReleaseDevice(invalid_device); + test_failure_error_ret(err, CL_INVALID_DEVICE, + "clReleaseDevice should return " + "CL_INVALID_DEVICE when: \"device is not " + "a valid device\"", + TEST_FAIL); + } + + return TEST_PASS; +} diff --git a/test_conformance/api/negative_platform.cpp b/test_conformance/api/negative_platform.cpp index f98ec13a..6c5922c5 100644 --- a/test_conformance/api/negative_platform.cpp +++ b/test_conformance/api/negative_platform.cpp @@ -40,9 +40,20 @@ REGISTER_TEST(negative_get_platform_info) { cl_platform_id platform = getPlatformFromDevice(device); + cl_int err(CL_SUCCESS); + for (auto invalid_platform : get_invalid_objects(device)) + { + err = clGetPlatformInfo(invalid_platform, CL_PLATFORM_VERSION, + sizeof(char*), nullptr, nullptr); + test_failure_error_ret(err, CL_INVALID_PLATFORM, + "clGetPlatformInfo should return " + "CL_INVALID_PLATFORM when: \"platform " + "is not a valid platform\"", + TEST_FAIL); + } + constexpr cl_platform_info INVALID_PARAM_VALUE = 0; - cl_int err = - clGetPlatformInfo(platform, INVALID_PARAM_VALUE, 0, nullptr, nullptr); + err = clGetPlatformInfo(platform, INVALID_PARAM_VALUE, 0, nullptr, nullptr); test_failure_error_ret( err, CL_INVALID_VALUE, "clGetPlatformInfo should return CL_INVALID_VALUE when: \"param_name "