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;
+}