| // SPDX-License-Identifier: BSD-2-Clause | |
| #include "avif/internal.h" | |
| #include <stdio.h> | |
| #include <string.h> | |
| static int failures; | |
| #define CHECK(condition) \ | |
| do { \ | |
| if (!(condition)) { \ | |
| fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #condition); \ | |
| ++failures; \ | |
| return; \ | |
| } \ | |
| } while (0) | |
| static void checkOption(const avifCodecSpecificOptions * options, uint32_t index, const char * key, const char * value) | |
| { | |
| CHECK(index < options->count); | |
| CHECK(!strcmp(options->entries[index].key, key)); | |
| CHECK(!strcmp(options->entries[index].value, value)); | |
| } | |
| static void testNormalOperations(void) | |
| { | |
| avifCodecSpecificOptions * options = avifCodecSpecificOptionsCreate(); | |
| CHECK(options != NULL); | |
| const uint32_t initialCapacity = options->capacity; | |
| CHECK(initialCapacity == 4); | |
| CHECK(avifCodecSpecificOptionsSet(options, "a", "one") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "b", "two") == AVIF_RESULT_OK); | |
| CHECK(options->count == 2); | |
| checkOption(options, 0, "a", "one"); | |
| checkOption(options, 1, "b", "two"); | |
| CHECK(avifCodecSpecificOptionsSet(options, "a", "updated") == AVIF_RESULT_OK); | |
| CHECK(options->count == 2); | |
| checkOption(options, 0, "a", "updated"); | |
| CHECK(avifCodecSpecificOptionsSet(options, "b", NULL) == AVIF_RESULT_OK); | |
| CHECK(options->count == 1); | |
| checkOption(options, 0, "a", "updated"); | |
| CHECK(avifCodecSpecificOptionsSet(options, "c", "three") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "d", "four") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "e", "five") == AVIF_RESULT_OK); | |
| CHECK(options->count == initialCapacity); | |
| CHECK(avifCodecSpecificOptionsSet(options, "f", "six") == AVIF_RESULT_OK); | |
| CHECK(options->count == initialCapacity + 1); | |
| CHECK(options->capacity == initialCapacity * 2); | |
| checkOption(options, 4, "f", "six"); | |
| avifCodecSpecificOptionsClear(options); | |
| CHECK(options->count == 0); | |
| CHECK(options->capacity == initialCapacity * 2); | |
| avifCodecSpecificOptionsDestroy(options); | |
| } | |
| #if defined(AVIF_OPTIONSTEST_WRAP_MALLOC) | |
| extern void * __real_malloc(size_t size); | |
| // A nonnegative value permits that many allocations before the next one fails. | |
| static int mallocFailuresAfter = -1; | |
| void * __wrap_malloc(size_t size) | |
| { | |
| if (mallocFailuresAfter == 0) { | |
| return NULL; | |
| } | |
| if (mallocFailuresAfter > 0) { | |
| --mallocFailuresAfter; | |
| } | |
| return __real_malloc(size); | |
| } | |
| typedef struct optionState | |
| { | |
| avifCodecSpecificOption * entries; | |
| uint32_t count; | |
| uint32_t capacity; | |
| char * keys[4]; | |
| char * values[4]; | |
| } optionState; | |
| static optionState saveOptionState(const avifCodecSpecificOptions * options) | |
| { | |
| optionState state; | |
| memset(&state, 0, sizeof(state)); | |
| state.entries = options->entries; | |
| state.count = options->count; | |
| state.capacity = options->capacity; | |
| if (state.count > 4) { | |
| fprintf(stderr, "%s:%d: state.count <= 4\n", __FILE__, __LINE__); | |
| ++failures; | |
| state.count = 0; | |
| return state; | |
| } | |
| for (uint32_t i = 0; i < state.count; ++i) { | |
| state.keys[i] = options->entries[i].key; | |
| state.values[i] = options->entries[i].value; | |
| } | |
| return state; | |
| } | |
| static void checkOptionState(const avifCodecSpecificOptions * options, const optionState * state) | |
| { | |
| CHECK(options->entries == state->entries); | |
| CHECK(options->count == state->count); | |
| CHECK(options->capacity == state->capacity); | |
| for (uint32_t i = 0; i < state->count; ++i) { | |
| CHECK(options->entries[i].key == state->keys[i]); | |
| CHECK(options->entries[i].value == state->values[i]); | |
| } | |
| } | |
| static void testReplaceAllocationFailure(void) | |
| { | |
| avifCodecSpecificOptions * options = avifCodecSpecificOptionsCreate(); | |
| CHECK(options != NULL); | |
| CHECK(avifCodecSpecificOptionsSet(options, "key", "old") == AVIF_RESULT_OK); | |
| const optionState state = saveOptionState(options); | |
| mallocFailuresAfter = 0; | |
| const avifResult result = avifCodecSpecificOptionsSet(options, "key", "new"); | |
| mallocFailuresAfter = -1; | |
| CHECK(result == AVIF_RESULT_OUT_OF_MEMORY); | |
| checkOptionState(options, &state); | |
| checkOption(options, 0, "key", "old"); | |
| avifCodecSpecificOptionsDestroy(options); | |
| } | |
| static void testAddKeyAndValueAllocationFailures(void) | |
| { | |
| avifCodecSpecificOptions * options = avifCodecSpecificOptionsCreate(); | |
| CHECK(options != NULL); | |
| CHECK(avifCodecSpecificOptionsSet(options, "base", "value") == AVIF_RESULT_OK); | |
| const optionState state = saveOptionState(options); | |
| mallocFailuresAfter = 0; | |
| avifResult result = avifCodecSpecificOptionsSet(options, "new", "value"); | |
| mallocFailuresAfter = -1; | |
| CHECK(result == AVIF_RESULT_OUT_OF_MEMORY); | |
| checkOptionState(options, &state); | |
| mallocFailuresAfter = 1; | |
| result = avifCodecSpecificOptionsSet(options, "new", "value"); | |
| mallocFailuresAfter = -1; | |
| CHECK(result == AVIF_RESULT_OUT_OF_MEMORY); | |
| checkOptionState(options, &state); | |
| checkOption(options, 0, "base", "value"); | |
| avifCodecSpecificOptionsDestroy(options); | |
| } | |
| static void testGrowthAllocationFailure(void) | |
| { | |
| avifCodecSpecificOptions * options = avifCodecSpecificOptionsCreate(); | |
| CHECK(options != NULL); | |
| CHECK(options->capacity == 4); | |
| CHECK(avifCodecSpecificOptionsSet(options, "a", "1") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "b", "2") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "c", "3") == AVIF_RESULT_OK); | |
| CHECK(avifCodecSpecificOptionsSet(options, "d", "4") == AVIF_RESULT_OK); | |
| const optionState state = saveOptionState(options); | |
| mallocFailuresAfter = 2; | |
| const avifResult result = avifCodecSpecificOptionsSet(options, "e", "5"); | |
| mallocFailuresAfter = -1; | |
| CHECK(result == AVIF_RESULT_OUT_OF_MEMORY); | |
| checkOptionState(options, &state); | |
| checkOption(options, 0, "a", "1"); | |
| checkOption(options, 3, "d", "4"); | |
| avifCodecSpecificOptionsDestroy(options); | |
| } | |
| #endif | |
| int main(void) | |
| { | |
| testNormalOperations(); | |
| #if defined(AVIF_OPTIONSTEST_WRAP_MALLOC) | |
| testReplaceAllocationFailure(); | |
| testAddKeyAndValueAllocationFailures(); | |
| testGrowthAllocationFailure(); | |
| #endif | |
| return failures != 0; | |
| } |