diff --git a/meson.build b/meson.build index bff69b4..364b660 100644 --- a/meson.build +++ b/meson.build @@ -93,6 +93,17 @@ test_read_io = executable('test_read_io', dependencies: m_dep, ) +test_write_io = executable('test_write_io', + 'test-write-io.c', + 'bmp-common.c', + 'bmp-read.c', + 'huffman.c', + 'logging.c', + 'test-fileio-pipes.c', + huff_codes[0], + dependencies: m_dep, +) + test('read - s_s2_13_to_float', test_read_conversions, args : ['s_s2_13_to_float']) test('read - s_convert64', test_read_conversions, args : ['s_convert64']) test('read - s_float_to_s2_13', test_read_conversions, args : ['s_float_to_s2_13']) @@ -108,3 +119,7 @@ test('read - read_u16_le', test_read_io, args : ['read_u16_le' test('read - read_s16_le', test_read_io, args : ['read_s16_le']) test('read - read_u32_le', test_read_io, args : ['read_u32_le']) test('read - read_s32_le', test_read_io, args : ['read_s32_le']) +test('write - write_u32_le', test_write_io, args : ['write_u32_le']) +test('write - write_s32_le', test_write_io, args : ['write_s32_le']) +test('write - write_u16_le', test_write_io, args : ['write_u16_le']) +test('write - write_s16_le', test_write_io, args : ['write_s16_le']) diff --git a/test-fileio-pipes.c b/test-fileio-pipes.c index 6af63d9..d2f81da 100644 --- a/test-fileio-pipes.c +++ b/test-fileio-pipes.c @@ -69,3 +69,77 @@ abort: return NULL; } + + + +FILE* open_write_pipe(struct WritePipe **hwp) +{ + int fd[2] = { -1, -1 }; + FILE *file_read = NULL, *file_write = NULL; + struct WritePipe *wp = NULL; + + if (!hwp) + return NULL; + *hwp = NULL; + + if (!(wp = malloc(sizeof *wp))) { + perror(__func__); + goto abort; + } + memset(wp, 0, sizeof *wp); + + if (pipe(fd)) { + perror("pipe"); + goto abort; + } + + if (!(file_read = fdopen(fd[0], "rb"))) { + perror("fdopen read pipe"); + goto abort; + } + + if (!(file_write = fdopen(fd[1], "w"))) { + perror("fdopen write pipe"); + goto abort; + } + + wp->file_read = file_read; + *hwp = wp; + + return file_write; + +abort: + if (file_write) + fclose(file_write); + else if (fd[1] != -1) + close(fd[1]); + + if (file_read) + fclose(file_read); + else if (fd[0] != -1) + close(fd[0]); + + if (wp) + free(wp); + + return NULL; +} + +int data_from_write_pipe(struct WritePipe *wp, unsigned char *buffer, int size) +{ + int nread; + + if (!(wp && buffer)) { + fprintf(stderr, "%s(): invalid NULL argument(s)\n", __func__); + exit(3); + } + if (!wp->file_read) { + fprintf(stderr, "%s(): FILE* is NULL\n", __func__); + exit(3); + } + nread = fread(buffer, 1, size, wp->file_read); + fclose(wp->file_read); + free(wp); + + return nread; +} diff --git a/test-fileio-pipes.h b/test-fileio-pipes.h index e472f77..6f92434 100644 --- a/test-fileio-pipes.h +++ b/test-fileio-pipes.h @@ -18,4 +18,12 @@ * If not, see */ +struct WritePipe { + FILE *file_read; +}; + + FILE* provide_as_file(const unsigned char *data, size_t size); + +FILE* open_write_pipe(struct WritePipe **hwp); +int data_from_write_pipe(struct WritePipe *wp, unsigned char *buffer, int size); diff --git a/test-write-io.c b/test-write-io.c new file mode 100644 index 0000000..9f61acd --- /dev/null +++ b/test-write-io.c @@ -0,0 +1,235 @@ +/* bmplib - test-read-io.c + * + * Copyright (c) 2025, Rupert Weber. + * + * This file is part of bmplib. + * bmplib is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. + * If not, see + */ + +#include "bmp-write.c" + +#include "test-fileio-pipes.h" + +#define ARRAY_LEN(a) ((int)(sizeof (a) / sizeof ((a)[0]))) + +int main(int argc, const char **argv) +{ + const char *func /*, *subtest =""*/; + + if (argc < 2) { + fprintf(stderr, "Invalid invocation\n"); + return 2; + } + func = argv[1]; + /*if (argc >= 3) + subtest = argv[2];*/ + + + + if (!strcmp(func, "write_u32_le")) { + const int expected_len = 4; + + struct { + uint32_t value; + unsigned char *expected; + } data[] = { + { .expected = (unsigned char[]){0x00,0x00,0x00,0x00}, .value = 0 }, + { .expected = (unsigned char[]){0x01,0x00,0x00,0x00}, .value = 1 }, + { .expected = (unsigned char[]){0xfe,0xff,0xff,0xff}, .value = 0xfffffffeUL }, + { .expected = (unsigned char[]){0xff,0xff,0xff,0xff}, .value = 0xffffffffUL }, + { .expected = (unsigned char[]){0x12,0x34,0x56,0x78}, .value = 0x78563412UL }, + }; + + for (int i = 0; i < ARRAY_LEN(data); i++) { + struct WritePipe *wp = NULL; + unsigned char buf[expected_len]; + + FILE *file = open_write_pipe(&wp); + if (!file) + return 2; + + if (!write_u32_le(file, data[i].value)) { + perror(func); + return 3; + } + fflush(file); /* important, otherwise read will get stuck */ + + int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf); + fclose(file); + if (nbytes != expected_len) { + printf("%s() failed on dataset %d (%lu):\n", func, i, (unsigned long)data[i].value); + printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes); + return 1; + } + if (memcmp(data[i].expected, buf, expected_len) != 0) { + printf("%s() failed on dataset %d (%lu):\n", func, i, (unsigned long)data[i].value); + printf("expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x\n", + (unsigned)data[i].expected[0], (unsigned)data[i].expected[1], + (unsigned)data[i].expected[2], (unsigned)data[i].expected[3], + (unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]); + return 1; + } + } + return 0; + } + + + if (!strcmp(func, "write_s32_le")) { + const int expected_len = 4; + + struct { + int32_t value; + unsigned char *expected; + } data[] = { + { .expected = (unsigned char[]){0x00,0x00,0x00,0x00}, .value = 0 }, + { .expected = (unsigned char[]){0x01,0x00,0x00,0x00}, .value = 1 }, + { .expected = (unsigned char[]){0xff,0xff,0xff,0xff}, .value = -1 }, + { .expected = (unsigned char[]){0x00,0x00,0x00,0x80}, .value = -2147483648L }, + { .expected = (unsigned char[]){0x01,0x00,0x00,0x80}, .value = -2147483647L }, + { .expected = (unsigned char[]){0xfe,0xff,0xff,0x7f}, .value = 2147483646L }, + { .expected = (unsigned char[]){0xff,0xff,0xff,0x7f}, .value = 2147483647L }, + { .expected = (unsigned char[]){0x12,0x34,0x56,0x78}, .value = 2018915346L }, + }; + + for (int i = 0; i < ARRAY_LEN(data); i++) { + struct WritePipe *wp = NULL; + unsigned char buf[expected_len]; + + FILE *file = open_write_pipe(&wp); + if (!file) + return 2; + + if (!write_s32_le(file, data[i].value)) { + perror(func); + return 3; + } + fflush(file); /* important, otherwise read will get stuck */ + + int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf); + fclose(file); + if (nbytes != expected_len) { + printf("%s() failed on dataset %d (%ld):\n", func, i, (long)data[i].value); + printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes); + return 1; + } + if (memcmp(data[i].expected, buf, expected_len) != 0) { + printf("%s() failed on dataset %d (%ld):\n", func, i, (long)data[i].value); + printf("expected 0x%02x%02x%02x%02x, got 0x%02x%02x%02x%02x\n", + (unsigned)data[i].expected[0], (unsigned)data[i].expected[1], + (unsigned)data[i].expected[2], (unsigned)data[i].expected[3], + (unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]); + return 1; + } + } + return 0; + } + + + if (!strcmp(func, "write_u16_le")) { + const int expected_len = 2; + + struct { + uint16_t value; + unsigned char *expected; + } data[] = { + { .expected = (unsigned char[]){0x00,0x00}, .value = 0 }, + { .expected = (unsigned char[]){0x01,0x00}, .value = 1 }, + { .expected = (unsigned char[]){0xfe,0xff}, .value = 65534 }, + { .expected = (unsigned char[]){0xff,0xff}, .value = 65535 }, + }; + + for (int i = 0; i < ARRAY_LEN(data); i++) { + struct WritePipe *wp = NULL; + unsigned char buf[expected_len]; + + FILE *file = open_write_pipe(&wp); + if (!file) + return 2; + + if (!write_u16_le(file, data[i].value)) { + perror(func); + return 3; + } + fflush(file); /* important, otherwise read will get stuck */ + + int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf); + fclose(file); + if (nbytes != expected_len) { + printf("%s() failed on dataset %d (%u):\n", func, i, (unsigned)data[i].value); + printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes); + return 1; + } + if (memcmp(data[i].expected, buf, expected_len) != 0) { + printf("%s() failed on dataset %d (%u):\n", func, i, (unsigned)data[i].value); + printf("expected 0x%02x%02x, got 0x%02x%02x\n", + (unsigned)data[i].expected[0], (unsigned)data[i].expected[1], + (unsigned)buf[0], (unsigned)buf[1]); + return 1; + } + } + return 0; + } + + + if (!strcmp(func, "write_s16_le")) { + const int expected_len = 2; + + struct { + int16_t value; + unsigned char *expected; + } data[] = { + { .expected = (unsigned char[]){0x00,0x00}, .value = 0 }, + { .expected = (unsigned char[]){0x01,0x00}, .value = 1 }, + { .expected = (unsigned char[]){0x00,0x80}, .value = -32768 }, + { .expected = (unsigned char[]){0x01,0x80}, .value = -32767 }, + { .expected = (unsigned char[]){0xfe,0x7f}, .value = 32766 }, + { .expected = (unsigned char[]){0xff,0x7f}, .value = 32767 }, + }; + + for (int i = 0; i < ARRAY_LEN(data); i++) { + struct WritePipe *wp = NULL; + unsigned char buf[expected_len]; + + FILE *file = open_write_pipe(&wp); + if (!file) + return 2; + + if (!write_s16_le(file, data[i].value)) { + perror(func); + return 3; + } + fflush(file); /* important, otherwise read will get stuck */ + + int nbytes = data_from_write_pipe(wp, buf, (int) sizeof buf); + fclose(file); + if (nbytes != expected_len) { + printf("%s() failed on dataset %d (%d):\n", func, i, (int)data[i].value); + printf("expected to read %d bytes, got %d bytes\n", expected_len, nbytes); + return 1; + } + if (memcmp(data[i].expected, buf, expected_len) != 0) { + printf("%s() failed on dataset %d (%d):\n", func, i, (int)data[i].value); + printf("expected 0x%02x%02x, got 0x%02x%02x\n", + (unsigned)data[i].expected[0], (unsigned)data[i].expected[1], + (unsigned)buf[0], (unsigned)buf[1]); + return 1; + } + } + return 0; + } + + fprintf(stderr, "Invalid test '%s'\n", func); + return 2; +}