/* Copyright (c) 2024 - 2026 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; } rocjpeg_status = rocJpegDestroy(rocjpeg_handle_); if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { std::cerr << "Expected ROCJPEG_STATUS_SUCCESS 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; } rocjpeg_status = rocJpegDestroy(rocjpeg_handle_); if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { std::cerr << "Expected ROCJPEG_STATUS_SUCCESS 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; } rocjpeg_status = rocJpegDestroy(rocjpeg_handle_); if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { std::cerr << "Expected ROCJPEG_STATUS_SUCCESS 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; } rocjpeg_status = rocJpegDestroy(rocjpeg_handle_); if (rocjpeg_status != ROCJPEG_STATUS_SUCCESS) { std::cerr << "Expected ROCJPEG_STATUS_SUCCESS 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 >= ROCJPEG_STATUS_MAX_VALUE; 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; } }