diff options
Diffstat (limited to 'subprojects/exess/test/test_base64.c')
-rw-r--r-- | subprojects/exess/test/test_base64.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/subprojects/exess/test/test_base64.c b/subprojects/exess/test/test_base64.c new file mode 100644 index 00000000..760f44fe --- /dev/null +++ b/subprojects/exess/test/test_base64.c @@ -0,0 +1,196 @@ +/* + Copyright 2011-2021 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#undef NDEBUG + +#include "exess/exess.h" + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +static void +check_read(const char* const string, + const ExessStatus expected_status, + const size_t expected_value_length, + const char* const expected_value, + const size_t expected_value_size, + const size_t expected_count) +{ + char buf[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + ExessBlob blob = {9, buf}; + + ExessResult r = exess_read_base64(&blob, string); + assert(r.status == expected_status); + assert(r.count == expected_count); + assert(r.status || blob.size == expected_value_size); + if (expected_value_length > 0) { + assert(!strncmp(buf, expected_value, expected_value_length)); + assert(blob.size <= exess_base64_decoded_size(strlen(string))); + } +} + +static void +test_rfc4648_cases(void) +{ + char buf[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + + assert(!exess_write_base64(6, "foobar", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zm9vYmFy")); + + assert(!exess_write_base64(5, "fooba", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zm9vYmE=")); + + assert(!exess_write_base64(4, "foob", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zm9vYg==")); + + assert(!exess_write_base64(3, "foo", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zm9v")); + + assert(!exess_write_base64(2, "fo", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zm8=")); + + assert(!exess_write_base64(1, "f", sizeof(buf), buf).status); + assert(!strcmp(buf, "Zg==")); +} + +static void +test_whitespace(void) +{ + check_read("Zm9vYmFy", EXESS_SUCCESS, 6, "foobar", 6, 8); + check_read(" Zm9vYmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read("Z\fm9vYmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read("Zm\n9vYmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read("Zm9\rvYmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read("Zm9v\tYmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read("Zm9vY\vmFy", EXESS_SUCCESS, 6, "foobar", 6, 9); + check_read(" \f\n\r\t\vZm9vYmFy", EXESS_SUCCESS, 6, "foobar", 6, 14); + check_read("Zm9vYmFy \f\n\r\t\v", EXESS_SUCCESS, 6, "foobar", 6, 14); +} + +static void +test_syntax_errors(void) +{ + check_read("Z", EXESS_EXPECTED_BASE64, 0, NULL, 0, 1); + check_read("ZZ", EXESS_EXPECTED_BASE64, 0, NULL, 0, 2); + check_read("ZZZ", EXESS_EXPECTED_BASE64, 0, NULL, 0, 3); + + check_read("=ZZZ", EXESS_BAD_VALUE, 0, NULL, 0, 4); + check_read("Z=ZZ", EXESS_BAD_VALUE, 0, NULL, 0, 4); + check_read("ZZ=Z", EXESS_BAD_VALUE, 0, NULL, 0, 4); + + check_read("!m9vYmFy", EXESS_EXPECTED_BASE64, 0, NULL, 0, 0); + check_read("Z!9vYmFy", EXESS_EXPECTED_BASE64, 0, NULL, 0, 1); + check_read("Zm!vYmFy", EXESS_EXPECTED_BASE64, 0, NULL, 0, 2); + check_read("Zm9!YmFy", EXESS_EXPECTED_BASE64, 0, NULL, 0, 3); + check_read("Zm9v!mFy", EXESS_EXPECTED_BASE64, 0, NULL, 3, 4); + check_read("Zm9vY!Fy", EXESS_EXPECTED_BASE64, 0, NULL, 3, 5); + check_read("Zm9vYm!y", EXESS_EXPECTED_BASE64, 0, NULL, 3, 6); + check_read("Zm9vYmF!", EXESS_EXPECTED_BASE64, 0, NULL, 3, 7); +} + +static void +test_read_overflow(void) +{ + char buf[3] = {0, 0, 0}; + ExessBlob blob0 = {0, buf}; + ExessBlob blob1 = {1, buf}; + ExessBlob blob2 = {2, buf}; + ExessBlob blob3 = {3, buf}; + + ExessResult r = exess_read_base64(&blob0, "Zm9v"); + assert(r.status == EXESS_NO_SPACE); + assert(r.count == 4); + assert(blob0.size == 0); + + r = exess_read_base64(&blob1, "Zm9v"); + assert(r.status == EXESS_NO_SPACE); + assert(r.count == 4); + assert(!buf[0]); + + r = exess_read_base64(&blob2, "Zm9v"); + assert(r.status == EXESS_NO_SPACE); + assert(r.count == 4); + assert(!buf[0]); + + r = exess_read_base64(&blob3, "Zm9v"); + assert(r.status == EXESS_SUCCESS); + assert(r.count == 4); + assert(blob3.size == 3); + assert(!strncmp(buf, "foo", 3)); +} + +static void +test_write_overflow(void) +{ + char buf[5] = {1, 2, 3, 4, 5}; + + assert(exess_write_base64(3, "foo", 0, buf).status == EXESS_NO_SPACE); + assert(exess_write_base64(3, "foo", 1, buf).status == EXESS_NO_SPACE); + assert(exess_write_base64(3, "foo", 2, buf).status == EXESS_NO_SPACE); + assert(exess_write_base64(3, "foo", 3, buf).status == EXESS_NO_SPACE); + assert(exess_write_base64(3, "foo", 4, buf).status == EXESS_NO_SPACE); + assert(exess_write_base64(3, "foo", 5, buf).status == EXESS_SUCCESS); +} + +static void +test_round_trip(void) +{ + for (size_t size = 1; size < 256; ++size) { + // Allocate and generate data + uint8_t* const data = (uint8_t*)malloc(size); + for (size_t i = 0; i < size; ++i) { + data[i] = (uint8_t)((size + i) % 256); + } + + // Allocate buffer for encoding with minimum required size + const size_t str_len = exess_write_base64(size, data, 0, NULL).count; + char* const str = (char*)malloc(str_len + 1); + + // Encode data to string buffer + assert(!exess_write_base64(size, data, str_len + 1, str).status); + assert(strlen(str) == str_len); + assert(str_len % 4 == 0); + + // Allocate buffer for decoded data with the same size as the input + uint8_t* const decoded = (uint8_t*)malloc(size); + ExessBlob decoded_blob = {size, decoded}; + + // Decode and check that data matches the original input + assert(!exess_read_base64(&decoded_blob, str).status); + assert(decoded_blob.size == size); + assert(!memcmp(decoded, data, size)); + + free(decoded); + free(str); + free(data); + } +} + +int +main(void) +{ + test_rfc4648_cases(); + test_whitespace(); + test_syntax_errors(); + test_read_overflow(); + test_write_overflow(); + test_round_trip(); + + return 0; +} |