From 232a9c6906510a2b24bebcf3f8cfd629e873f3cb Mon Sep 17 00:00:00 2001 From: Aryan Salmanpour Date: Fri, 18 Apr 2025 13:02:12 -0400 Subject: [PATCH] CTest: Add a test to perform negative testing on all rocJPEG APIs (#143) * CTest: Add a test to perform negative testing on all rocJPEG APIs * clean up --- CMakeLists.txt | 1 + src/rocjpeg_decoder.cpp | 2 + test/CMakeLists.txt | 13 +- .../rocjpeg_negative_api_tests/CMakeLists.txt | 93 +++++ test/rocjpeg_negative_api_tests/README.md | 22 ++ .../rocjpeg_api_negative_tests.cpp | 365 ++++++++++++++++++ .../rocjpeg_api_negative_tests.h | 56 +++ .../rocjpegnegativetest.cpp | 33 ++ 8 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 test/rocjpeg_negative_api_tests/CMakeLists.txt create mode 100644 test/rocjpeg_negative_api_tests/README.md create mode 100644 test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.cpp create mode 100644 test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.h create mode 100644 test/rocjpeg_negative_api_tests/rocjpegnegativetest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a965d25ed..65032747f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,7 @@ if(HIP_FOUND AND Libva_FOUND) install(FILES ${CPACK_RESOURCE_FILE_LICENSE} DESTINATION ${CMAKE_INSTALL_DOCDIR}-asan COMPONENT asan) # install test cmake install(FILES test/CMakeLists.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test COMPONENT test) + install(FILES test/rocjpeg_negative_api_tests/CMakeLists.txt test/rocjpeg_negative_api_tests/README.md test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.cpp test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.h test/rocjpeg_negative_api_tests/rocjpegnegativetest.cpp DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test/rocjpeg_negative_api_tests COMPONENT test) message("-- ${White}AMD ROCm rocJPEG -- CMAKE_CXX_FLAGS:${CMAKE_CXX_FLAGS}${ColourReset}") message("-- ${White}AMD ROCm rocJPEG -- Link Libraries: ${LINK_LIBRARY_LIST}${ColourReset}") diff --git a/src/rocjpeg_decoder.cpp b/src/rocjpeg_decoder.cpp index 1ac3aef9a0..c09cf13c97 100644 --- a/src/rocjpeg_decoder.cpp +++ b/src/rocjpeg_decoder.cpp @@ -89,6 +89,8 @@ RocJpegStatus RocJpegDecoder::InitializeDecoder() { } } else if (backend_ == ROCJPEG_BACKEND_HYBRID) { return ROCJPEG_STATUS_NOT_IMPLEMENTED; + } else { + return ROCJPEG_STATUS_INVALID_PARAMETER; } return rocjpeg_status; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c65005b977..8412369557 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -56,7 +56,7 @@ include(CTest) # add ROCm find Config location list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/lib/cmake) -# find rocDecode +# find rocjpeg find_package(ROCJPEG QUIET) if(ROCJPEG_FOUND) @@ -324,4 +324,15 @@ add_test( --build-generator "${CMAKE_GENERATOR}" --test-command "jpegdecodeperf" -i ${ROCM_PATH}/share/rocjpeg/images/ +) + +add_test( + NAME + jpeg-negative-api-tests + COMMAND + "${CMAKE_CTEST_COMMAND}" + --build-and-test "${ROCM_PATH}/share/rocjpeg/test/rocjpeg_negative_api_tests" + "${CMAKE_CURRENT_BINARY_DIR}/rocjpeg_negative_api_tests" + --build-generator "${CMAKE_GENERATOR}" + --test-command "rocjpegnegativetest" ) \ No newline at end of file diff --git a/test/rocjpeg_negative_api_tests/CMakeLists.txt b/test/rocjpeg_negative_api_tests/CMakeLists.txt new file mode 100644 index 0000000000..34a095eb52 --- /dev/null +++ b/test/rocjpeg_negative_api_tests/CMakeLists.txt @@ -0,0 +1,93 @@ +################################################################################ +# Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +################################################################################ + +cmake_minimum_required(VERSION 3.10) +# ROCM Path +if(DEFINED ENV{ROCM_PATH}) + set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "Default ROCm installation path") +elseif(ROCM_PATH) + message("-- INFO:ROCM_PATH Set -- ${ROCM_PATH}") +else() + set(ROCM_PATH /opt/rocm CACHE PATH "Default ROCm installation path") +endif() +# Set AMD Clang as default compiler +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED On) +set(CMAKE_CXX_EXTENSIONS ON) +if (NOT DEFINED CMAKE_CXX_COMPILER) + set(CMAKE_C_COMPILER ${ROCM_PATH}/bin/amdclang) + set(CMAKE_CXX_COMPILER ${ROCM_PATH}/bin/amdclang++) +endif() + +project(rocjpegnegativetest) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake) +list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/lib/cmake) + +# rocJPEG sample build type +set(DEFAULT_BUILD_TYPE "Release") +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "rocJPEG Default Build Type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") +endif() +if(CMAKE_BUILD_TYPE MATCHES Debug) + # -O0 -- Don't Optimize output file + # -gdwarf-4 -- generate debugging information, dwarf-4 for making valgrind work + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -gdwarf-4") +else() + # -O3 -- Optimize output file + # -DNDEBUG -- turn off asserts + # -fPIC -- Generate position-independent code if possible + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG -fPIC") +endif() + +find_package(HIP QUIET) +find_package(rocjpeg QUIET) +find_package(rocprofiler-register QUIET) + +if(HIP_FOUND AND rocjpeg_FOUND AND rocprofiler-register_FOUND) + # HIP + set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} hip::host) + # rocJPEG and utils + include_directories (${rocjpeg_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${rocjpeg_LIBRARY}) + # rocprofiler-register + set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} rocprofiler-register::rocprofiler-register) + # sample app exe + list(APPEND SOURCES ${PROJECT_SOURCE_DIR} rocjpegnegativetest.cpp rocjpeg_api_negative_tests.cpp) + # --all-warnings/-Wall Enable most warning messages + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + add_executable(${PROJECT_NAME} ${SOURCES}) + target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST}) +else() + message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!") + if (NOT HIP_FOUND) + message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!") + endif() + if (NOT rocjpeg_FOUND) + message(FATAL_ERROR "-- ERROR!: rocJPEG Not Found! - please install rocJPEG!") + endif() + if (NOT rocprofiler-register_FOUND) + message(FATAL_ERROR "-- ERROR!: rocprofiler-register Not Found! - please install rocprofiler-register!") + endif() +endif() \ No newline at end of file diff --git a/test/rocjpeg_negative_api_tests/README.md b/test/rocjpeg_negative_api_tests/README.md new file mode 100644 index 0000000000..f87a9eb004 --- /dev/null +++ b/test/rocjpeg_negative_api_tests/README.md @@ -0,0 +1,22 @@ +# rocJPEG API Negative tests + +This test suite is designed to perform negative testing on all rocJPEG APIs. The purpose of these tests is to validate the robustness and error-handling mechanisms of the rocJPEG APIs +by providing invalid inputs, unexpected scenarios, or edge cases to ensure the APIs respond with appropriate error messages or behaviors. + +## Prerequisites: + +* Install [rocJPEG](../../README.md#build-and-install-instructions) + +## Build + +```shell +mkdir build && cd build +cmake ../ +make -j +``` + +## Run + +```shell +./rocjpegnegativetest +``` \ No newline at end of file diff --git a/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.cpp b/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.cpp new file mode 100644 index 0000000000..f077072ad6 --- /dev/null +++ b/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.cpp @@ -0,0 +1,365 @@ + +/* +Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "rocjpeg_api_negative_tests.h" + +RocJpegApiNegativeTests:: RocJpegApiNegativeTests() {}; + +RocJpegApiNegativeTests::~RocJpegApiNegativeTests() { + RocJpegStatus rocjpeg_status = rocJpegDestroy(rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { + std::cerr << "Failed to destroy rocjpeg handle: " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + } + rocjpeg_status = rocJpegStreamDestroy(rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { + std::cerr << "Failed to destroy rocjpeg stream handle " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + } +} + +int RocJpegApiNegativeTests::TestInvalidStreamCreate() { + std::cout << "info: Executing negative test cases for the rocJpegStreamCreate API" << std::endl; + //Scenario 1: Pass nullptr for jpeg_stream_handle + RocJpegStatus rocjpeg_status = rocJpegStreamCreate(nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + // Create a valid rocJPEG stream handle - This step ensures a valid rocjpeg_stream_handle_ is available for subsequent negative testing of other rocJPEG parser APIs. + rocjpeg_status = rocJpegStreamCreate(&rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { + std::cerr << "Expected ROCJPEG_STATUS_SUCCESS but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidStreamParse() { + std::cout << "info: Executing negative test cases for the rocJpegStreamParse API" << std::endl; + + // Scenario 1: Pass nullptr for data and jpeg_stream_handle + RocJpegStatus rocjpeg_status = rocJpegStreamParse(nullptr, 0, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 2: Pass a valid jpeg_stream_handle but nullptr for data + rocjpeg_status = rocJpegStreamParse(nullptr, 0, rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 3: Invalid SOI marker + std::vector invalid_soi_data = {0xFF, 0x00}; // Invalid SOI marker + rocjpeg_status = rocJpegStreamParse(invalid_soi_data.data(), invalid_soi_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 4: Invalid DRI marker + std::vector invalid_dri_data = {0xFF, 0xD8, 0xFF, 0xDD, 0x00, 0x03}; // Invalid DRI marker length + rocjpeg_status = rocJpegStreamParse(invalid_dri_data.data(), invalid_dri_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 5: Invalid SOS marker - provide an invalid number of components (e.g., the number of components cannot exceed 3, but 4 is provided) + std::vector invalid_sos_data = {0xFF, 0xD8, 0xFF, 0xDA, 0x00, 0x01, 0x04}; // Invalid number of component + rocjpeg_status = rocJpegStreamParse(invalid_sos_data.data(), invalid_sos_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 6: Invalid number of quantization tables in the DQT marker + std::vector invalid_quantization_data = {0xFF, 0xD8, 0xFF, 0xDB, 0x00, 0x03, 0x1F}; // Invalid quantization table + rocjpeg_status = rocJpegStreamParse(invalid_quantization_data.data(), invalid_quantization_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 7: Invalid number of Huffman tables in the DHT marker + std::vector invalid_huffman_table_data = {0xFF, 0xD8, 0xFF, 0xC4, 0x00, 0x03, 0x02}; // Too many Huffman tables + rocjpeg_status = rocJpegStreamParse(invalid_huffman_table_data.data(), invalid_huffman_table_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG for invalid number of Huffman tables but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 8: Invalid AC Huffman table in the DHT marker + std::vector invalid_ac_huffman_table_data = { + 0xFF, 0xD8, //SOI + 0xFF, 0xC4, 0x00, 0x03, 0x10, // DHT with AC Hufman table + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0xA3 // Array of the invalid number of AC codes - the count of values cannot exceed 0xA2, but 0xA3 is provided + }; + rocjpeg_status = rocJpegStreamParse(invalid_ac_huffman_table_data.data(), invalid_ac_huffman_table_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 9: Invalid DC Huffman table in the DHT marker + std::vector invalid_dc_huffman_table_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xC4, 0x00, 0x03, 0x01, // DHT with DC Hufman table + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0x0D // Array of the invalid number of DC codes - the count of values cannot exceed 0x0C, but 0x0D is provided + }; // Invalid DC Huffman table + rocjpeg_status = rocJpegStreamParse(invalid_dc_huffman_table_data.data(), invalid_dc_huffman_table_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 10: invalid number of JPEG component in the SOF marker + std::vector invalid_num_component_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xC0, 0x00, 0x08, // Invalid SOF with the number of component is set to 4 + 0x08, 0x00, 0x10, 0x00, 0x10, 0x04 + }; + rocjpeg_status = rocJpegStreamParse(invalid_num_component_data.data(), invalid_num_component_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 11: Invalid quantization table selector specified in the SOF marker + std::vector Invalid_quantization_table_selector_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xC0, 0x00, 0x0B, // SOF with 3 components with invalid quantization table selector is set to 4 + 0x08, 0x00, 0x10, 0x00, 0x10, 0x03, 0x00, 0x00, 0x04 + }; + rocjpeg_status = rocJpegStreamParse(Invalid_quantization_table_selector_data.data(), Invalid_quantization_table_selector_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 11: Mismatch in the number of components between the SOS and SOF markers + std::vector component_mismatch_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xC0, 0x00, 0x11, // SOF with 3 components + 0x08, 0x00, 0x10, 0x00, 0x10, 0x03, 0x01, 0xFF, 0x00, 0x02, 0xFF, 0x01, 0x03, 0xFF, 0x02, + 0xFF, 0xDA, 0x00, 0x07, // SOS with 2 components (mismatch) + 0x01, 0x00, 0x02, 0x11, 0x00 + }; + rocjpeg_status = rocJpegStreamParse(component_mismatch_data.data(), component_mismatch_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 12: Invalid AC Huffman table selector in the SOS marker + std::vector invalid_ac_huffman_sos_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xDA, 0x00, 0x07, // SOS with invalid number of AC Huffman table + 0x01, 0x00, 0x04, 0x11, 0x00 + }; + rocjpeg_status = rocJpegStreamParse(invalid_ac_huffman_sos_data.data(), invalid_ac_huffman_sos_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 13: Invalid DC Huffman table selector in the SOS marker + std::vector invalid_dc_huffman_sos_data = { + 0xFF, 0xD8, // SOI + 0xFF, 0xDA, 0x00, 0x07, // SOS with invalid number of DC Huffman table + 0x01, 0x00, 0x44, 0x11, 0x00 + }; + rocjpeg_status = rocJpegStreamParse(invalid_dc_huffman_sos_data.data(), invalid_dc_huffman_sos_data.size(), rocjpeg_stream_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_BAD_JPEG) { + std::cerr << "Expected ROCJPEG_STATUS_BAD_JPEG but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidStreamDestroy() { + std::cout << "info: Executing negative test cases for the rocJpegStreamDestroy API" << std::endl; + //Scenario 1: Pass nullptr for jpeg_stream_handle + RocJpegStatus rocjpeg_status = rocJpegStreamDestroy(nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidCreate() { + std::cout << "info: Executing negative test cases for the rocJpegCreate API" << std::endl; + // Scenario 1: Pass nullptr for decoder_handle and decoder_create_info + RocJpegStatus rocjpeg_status = rocJpegCreate(ROCJPEG_BACKEND_HARDWARE, 0, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 2: Pass valid pointer for handle but invalid negative device_id + int device_id = -1; // Invalid device ID + rocjpeg_status = rocJpegCreate(ROCJPEG_BACKEND_HARDWARE, device_id, &rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_EXECUTION_FAILED) { + std::cerr << "Expected ROCJPEG_STATUS_EXECUTION_FAILED but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 3: Pass valid pointer for handle but invalid device_id + device_id = 255; // Invalid device ID + rocjpeg_status = rocJpegCreate(ROCJPEG_BACKEND_HARDWARE, device_id, &rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 4: Pass valid pointer for handle but unsupported backend + device_id = 0; + rocjpeg_status = rocJpegCreate(ROCJPEG_BACKEND_HYBRID, device_id, &rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_NOT_IMPLEMENTED) { + std::cerr << "Expected ROCJPEG_STATUS_NOT_IMPLEMENTED but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 5: Use an unsupported backend + RocJpegBackend backend = static_cast(-1); + rocjpeg_status = rocJpegCreate(backend, device_id, &rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Create a valid rocJPEG handle - This step ensures a valid rocjpeg_handle_ is available for subsequent negative testing of other rocJPEG APIs. + rocjpeg_status = rocJpegCreate(ROCJPEG_BACKEND_HARDWARE, device_id, &rocjpeg_handle_); + if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { + std::cerr << "Expected ROCJPEG_STATUS_SUCCESS but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidDestroy() { + std::cout << "info: Executing negative test cases for the rocJpegDestroy API" << std::endl; + //Scenario 1: Pass nullptr for decoder_handle + RocJpegStatus rocjpeg_status = rocJpegDestroy(nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidGetImageInfo() { + std::cout << "info: Executing negative test cases for the rocJpegGetImageInfo API" << std::endl; + // Scenario 1: Pass nullptr for all parameters + RocJpegStatus rocjpeg_status = rocJpegGetImageInfo(rocjpeg_handle_, nullptr, nullptr, nullptr, nullptr, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidDecode() { + std::cout << "info: Executing negative test cases for the rocJpegDecode API" << std::endl; + // Scenario 1: Pass nullptr for all parameters + RocJpegStatus rocjpeg_status = rocJpegDecode(nullptr, nullptr, nullptr, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 2: Pass valid handle but nullptr for other parameters + rocjpeg_status = rocJpegDecode(rocjpeg_handle_, nullptr, nullptr, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 3: Pass valid handle and stream but nullptr for decode_params and destination + rocjpeg_status = rocJpegDecode(rocjpeg_handle_, rocjpeg_stream_handle_, nullptr, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + // Scenario 4: Pass valid handle, stream, and decode_params but nullptr for destination + RocJpegDecodeParams decode_params = {}; // Assume this is initialized with valid data + rocjpeg_status = rocJpegDecode(rocjpeg_handle_, rocjpeg_stream_handle_, &decode_params, nullptr); + if (rocjpeg_status != ROCJPEG_STATUS_INVALID_PARAMETER) { + std::cerr << "Expected ROCJPEG_STATUS_INVALID_PARAMETER but got " << rocJpegGetErrorName(rocjpeg_status) << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidDecodeBatched() { + std::cout << "info: Executing negative test cases for the rocJpegDecodeBatched API" << std::endl; + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::TestInvalidGetErrorName() { + std::cout << "info: Executing negative test cases for the rocJpegGetErrorName API" << std::endl; + // Scenario 1: Pass an invalid error code + RocJpegStatus invalid_status = static_cast(-999); // Invalid error code + const char *error_name = rocJpegGetErrorName(invalid_status); + if (error_name == nullptr) { + std::cerr << "Expected a valid error but got nullptr" << std::endl; + return EXIT_FAILURE; + } + + // Scenario 2: Pass a valid error code and ensure it returns a non-null name + for (int i = 0; i >= -8; i--) { + RocJpegStatus valid_status = static_cast(i);; + error_name = rocJpegGetErrorName(valid_status); + if (error_name == nullptr) { + std::cerr << "Expected a valid error but got nullptr" << std::endl; + return EXIT_FAILURE; + } + } + + // Scenario 3: Pass a boundary value (e.g., maximum enum value + 1) + RocJpegStatus boundary_status = static_cast(ROCJPEG_STATUS_SUCCESS + 1); + error_name = rocJpegGetErrorName(boundary_status); + if (error_name == nullptr) { + std::cerr << "Expected a valid error but got nullptr" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int RocJpegApiNegativeTests::RunTests() { + if (TestInvalidStreamCreate() || TestInvalidStreamParse () || TestInvalidStreamDestroy() || TestInvalidCreate() || TestInvalidDestroy() || + TestInvalidGetImageInfo() || TestInvalidDecode() || TestInvalidDecodeBatched() || TestInvalidGetErrorName()) { + std::cerr << "One or more negative tests failed." << std::endl; + return EXIT_FAILURE; + } else { + return EXIT_SUCCESS; + } +} diff --git a/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.h b/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.h new file mode 100644 index 0000000000..09face55f4 --- /dev/null +++ b/test/rocjpeg_negative_api_tests/rocjpeg_api_negative_tests.h @@ -0,0 +1,56 @@ +/* +Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +#ifndef ROCJPEG_API_NEGATIVE_TESTS_H +#define ROCJPEG_API_NEGATIVE_TESTS_H + +#include +#include +#include + +/** + * @class RocJpegApiNegativeTests + * @brief A class to perform negative API tests for the rocJPEG library. + * + * This class contains a set of test cases designed to validate the behavior + * of the rocJPEG library when invalid or unexpected inputs are provided. + * It ensures the robustness and error handling capabilities of the library. + */ +class RocJpegApiNegativeTests { + public: + RocJpegApiNegativeTests(); + ~RocJpegApiNegativeTests(); + int RunTests(); + private: + int TestInvalidStreamCreate(); + int TestInvalidStreamParse(); + int TestInvalidStreamDestroy(); + int TestInvalidCreate(); + int TestInvalidDestroy(); + int TestInvalidGetImageInfo(); + int TestInvalidDecode(); + int TestInvalidDecodeBatched(); + int TestInvalidGetErrorName(); + RocJpegHandle rocjpeg_handle_; + RocJpegStreamHandle rocjpeg_stream_handle_; +}; + +#endif // ROCJPEG_API_NEGATIVE_TESTS_H \ No newline at end of file diff --git a/test/rocjpeg_negative_api_tests/rocjpegnegativetest.cpp b/test/rocjpeg_negative_api_tests/rocjpegnegativetest.cpp new file mode 100644 index 0000000000..16a1b7ac25 --- /dev/null +++ b/test/rocjpeg_negative_api_tests/rocjpegnegativetest.cpp @@ -0,0 +1,33 @@ +/* +Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "rocjpeg_api_negative_tests.h" + +int main(int argc, char **argv) { + RocJpegApiNegativeTests rocjpeg_negative_test; + if (rocjpeg_negative_test.RunTests()) { + std::cout << "Test Failed!" << std::endl; + return EXIT_FAILURE; + } + std::cout << "Test Passed!" << std::endl; + return EXIT_SUCCESS; +} \ No newline at end of file