blob: 1f6ecb7df413f9a95f82de4647e8d3f3d320a999 [file]
// 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;
}