Commit 13fc7563 for xz
commit 13fc756371c5e84f11a3ad9d0472dff8dc0cb2ed
Author: Lasse Collin <lasse.collin@tukaani.org>
Date: Wed Nov 4 19:30:21 2026 +0200
Tests: Add decode_byte_by_byte and use it from test_files.sh
It exercises some corner cases. Without the previous commit, this fails
on good-known_size-with_eopm.lzma.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b29967e7..5327cd00 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -37,6 +37,7 @@ endif
check_PROGRAMS = \
create_compress_files \
+ decode_byte_by_byte \
test_check \
test_hardware \
test_stream_buffer_decode \
diff --git a/tests/decode_byte_by_byte.c b/tests/decode_byte_by_byte.c
new file mode 100644
index 00000000..e2a1b369
--- /dev/null
+++ b/tests/decode_byte_by_byte.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file decode_byte_by_byte.c
+/// \brief Decodes a file one byte at a time
+//
+// Author: Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "sysdefs.h"
+#include "lzma.h"
+#include <stdio.h>
+
+
+#define MEMLIMIT (UINT64_C(5) << 20)
+
+#define FAIL(msg) \
+ do { \
+ fprintf(stderr, "%s: %s: %s\n", argv[0], argv[1], msg); \
+ return EXIT_FAILURE; \
+ } while (0)
+
+
+int
+main(int argc, char **argv)
+{
+ if (argc != 2)
+ FAIL("One filename is required");
+
+ FILE *f = fopen(argv[1], "rb");
+ if (f == NULL)
+ FAIL("Opening the file failed");
+
+ lzma_stream strm = LZMA_STREAM_INIT;
+ if (lzma_auto_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED) != LZMA_OK)
+ FAIL("Decoder initialization failed");
+
+ bool eof = false;
+
+ while (true) {
+ uint8_t in[1];
+ uint8_t out[1];
+
+ if (!eof) {
+ const int b = fgetc(f);
+ if (b == EOF) {
+ if (ferror(f))
+ FAIL("Error reading the input file");
+
+ eof = true;
+ } else {
+ in[0] = (uint8_t)b;
+ strm.next_in = in;
+ strm.avail_in = 1;
+ }
+ }
+
+ do {
+ strm.next_out = out;
+ strm.avail_out = 1;
+
+ const lzma_action action
+ = eof ? LZMA_FINISH : LZMA_RUN;
+ const lzma_ret ret = lzma_code(&strm, action);
+
+ if (strm.avail_out == 0) {
+ if (fputc(out[0], stdout) == EOF)
+ FAIL("Error writing to stdout");
+ }
+
+ if (ret == LZMA_STREAM_END)
+ goto end;
+
+ if (ret != LZMA_OK) {
+ fprintf(stderr, "%s: %s: "
+ "Decoder error (ret = %d)\n",
+ argv[0], argv[1], ret);
+ return EXIT_FAILURE;
+ }
+ } while (strm.avail_out == 0);
+ }
+
+end:
+ // We must have consumed the entire file.
+ if (fgetc(f) != EOF)
+ FAIL("Garbage at the end of the file");
+
+ if (ferror(f))
+ FAIL("Error reading the input file (end)");
+
+ lzma_end(&strm);
+ (void)fclose(f);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_files.sh b/tests/test_files.sh
index 9312de94..1cfe27be 100755
--- a/tests/test_files.sh
+++ b/tests/test_files.sh
@@ -105,6 +105,8 @@ do
echo "Good file failed: $I"
exit 1
fi
+
+ ./decode_byte_by_byte "$I" > /dev/null || exit 1
done
for I in "$srcdir"/files/bad-*.xz
@@ -185,6 +187,8 @@ do
echo "Good file failed: $I"
exit 1
fi
+
+ ./decode_byte_by_byte "$I" > /dev/null || exit 1
done
for I in "$srcdir"/files/bad-*.lzma
@@ -209,6 +213,11 @@ if have_feature LZIP_DECODER ".lz files" ; then
echo "Good file failed: $I"
exit 1
fi
+
+ case $I in
+ *trailing*) ;;
+ *) ./decode_byte_by_byte "$I" > /dev/null || exit 1 ;;
+ esac
done
for I in "$srcdir"/files/bad-*.lz "$srcdir"/files/unsupported-*.lz
diff --git a/tests/tests.cmake b/tests/tests.cmake
index 5035f7b0..f2abc089 100644
--- a/tests/tests.cmake
+++ b/tests/tests.cmake
@@ -216,6 +216,18 @@ if(BUILD_TESTING)
# files outside the build directory!
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_files")
+ # test_files.sh needs the decode_byte_by_byte program.
+ # Put the executable in the above-created directory so that
+ # test_files.sh can run it with ./decode_byte_by_byte.
+ add_executable(decode_byte_by_byte tests/decode_byte_by_byte.c)
+ target_include_directories(decode_byte_by_byte PRIVATE
+ src/common
+ src/liblzma/api
+ )
+ target_link_libraries(decode_byte_by_byte PRIVATE liblzma tests_w32res)
+ set_target_properties(decode_byte_by_byte PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY test_files)
+
add_test(NAME test_files.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_files.sh" ".."
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_files"