Commit 5121d1ae for xz

commit 5121d1aed8107a113d7665b6d4f26b6187949f3d
Author: Lasse Collin <lasse.collin@tukaani.org>
Date:   Mon Aug 31 20:35:04 2026 +0300

    Tests: Add tests for single-call .xz encoding

    Thanks-to: GitHub user androvonx95
    Closes: https://github.com/tukaani-project/xz/pull/231

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dd1d4056..d5e63e7f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,6 +40,7 @@ check_PROGRAMS = \
 	test_check \
 	test_hardware \
 	test_stream_buffer_decode \
+	test_stream_buffer_encode \
 	test_stream_flags \
 	test_filter_flags \
 	test_filter_str \
@@ -55,6 +56,7 @@ TESTS = \
 	test_check \
 	test_hardware \
 	test_stream_buffer_decode \
+	test_stream_buffer_encode \
 	test_stream_flags \
 	test_filter_flags \
 	test_filter_str \
diff --git a/tests/test_stream_buffer_encode.c b/tests/test_stream_buffer_encode.c
new file mode 100644
index 00000000..019115fc
--- /dev/null
+++ b/tests/test_stream_buffer_encode.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file       test_stream_buffer_encode.c
+/// \brief      Test single-call .xz encoding
+//
+//  Author:     Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "tests.h"
+
+
+/// LZMA2 preset to use
+#define PRESET 1
+
+/// Memory usage limit for decompression
+#define MEMLIMIT (2U << 20)
+
+
+#ifdef HAVE_ENCODERS
+/// Compressible data (also with delta:dist=1)
+static const uint8_t compressible[]
+		= { 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 };
+
+// Incompressible data (also with delta:dist=1)
+static const uint8_t incompressible[] = { 1, 3, 7 };
+#endif
+
+
+static void
+test_stream_buffer_bound(void)
+{
+#ifndef HAVE_ENCODERS
+	assert_skip("Encoder support is disabled");
+#else
+	size_t bound;
+
+	bound = lzma_stream_buffer_bound(0);
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(bound, <, 222);
+
+	bound = lzma_stream_buffer_bound(123);
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE + 123);
+	assert_uint(bound, <, 333);
+
+	bound = lzma_stream_buffer_bound(SIZE_MAX - 123);
+	assert_uint_eq(bound, 0);
+
+	bound = lzma_stream_buffer_bound(SIZE_MAX);
+	assert_uint_eq(bound, 0);
+#endif
+}
+
+
+#ifdef HAVE_ENCODERS
+static void
+decompress_and_compare(const uint8_t *in, size_t in_size,
+		const uint8_t *uncomp, size_t uncomp_size)
+{
+#ifndef HAVE_DECODERS
+	(void)in;
+	(void)in_size;
+	(void)uncomp;
+	(void)uncomp_size;
+#else
+	uint8_t out[512] = { 0 };
+	assert_uint(uncomp_size, <=, sizeof(out));
+
+	uint64_t memlimit = MEMLIMIT;
+	size_t in_pos = 0;
+	size_t out_pos = 0;
+	assert_lzma_ret(lzma_stream_buffer_decode(&memlimit, 0, NULL,
+			in, &in_pos, in_size, out, &out_pos, sizeof(out)),
+			LZMA_OK);
+	assert_uint_eq(in_pos, in_size);
+	assert_uint_eq(out_pos, uncomp_size);
+	assert_array_eq(out, uncomp, uncomp_size);
+#endif
+}
+#endif
+
+
+static void
+test_easy_buffer_encode(void)
+{
+#ifndef HAVE_ENCODERS
+	assert_skip("Encoder support is disabled");
+#else
+	uint8_t out[512] = { 0 };
+	size_t out_pos;
+	size_t bound;
+
+	// Test with compressible data.
+	bound = lzma_stream_buffer_bound(sizeof(compressible));
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(bound, <, sizeof(out));
+	out_pos = 0;
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			compressible, sizeof(compressible),
+			out, &out_pos, bound), LZMA_OK);
+	assert_uint(out_pos, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	// It's compressible, thus <.
+	assert_uint(out_pos, <, bound);
+	decompress_and_compare(out, out_pos,
+			compressible, sizeof(compressible));
+
+	// Test with incompressible data.
+	bound = lzma_stream_buffer_bound(sizeof(incompressible));
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(bound, <, sizeof(out));
+	out_pos = 0;
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_OK);
+	assert_uint(out_pos, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(out_pos, <=, bound);
+	decompress_and_compare(out, out_pos,
+			incompressible, sizeof(incompressible));
+
+	// Test with too little output space.
+	bound = out_pos;
+	out_pos = 1;
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_BUF_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	// Test with unsupported or invalid arguments.
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			555, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_OPTIONS_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, INVALID_LZMA_CHECK_ID, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, (lzma_check)15, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_UNSUPPORTED_CHECK);
+	assert_uint_eq(out_pos, 1);
+
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			NULL, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			NULL, &out_pos, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, NULL, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	out_pos = bound + 1;
+	assert_lzma_ret(lzma_easy_buffer_encode(
+			PRESET, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, bound + 1);
+#endif
+}
+
+
+static void
+test_stream_buffer_encode(void)
+{
+#if !defined(HAVE_ENCODER_DELTA) || !defined(HAVE_DECODER_DELTA)
+	assert_skip("Delta encoder or decoder support is disabled");
+#else
+	uint8_t out[512] = { 0 };
+	size_t out_pos;
+	size_t bound;
+
+	lzma_options_delta opt_delta = { .dist = 1 };
+	lzma_options_lzma opt_lzma2;
+	assert_uint_eq(lzma_lzma_preset(&opt_lzma2, PRESET), false);
+	lzma_filter filters[] = {
+		{ LZMA_FILTER_DELTA, &opt_delta },
+		{ LZMA_FILTER_LZMA2, &opt_lzma2 },
+		{ LZMA_VLI_UNKNOWN, NULL }
+	};
+
+	// Test with compressible data.
+	bound = lzma_stream_buffer_bound(sizeof(compressible));
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(bound, <, sizeof(out));
+	out_pos = 0;
+	assert_lzma_ret(lzma_stream_buffer_encode(
+			filters, LZMA_CHECK_CRC32, NULL,
+			compressible, sizeof(compressible),
+			out, &out_pos, bound), LZMA_OK);
+	assert_uint(out_pos, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	// It's compressible even with the Delta filter, thus <.
+	assert_uint(out_pos, <, bound);
+	decompress_and_compare(out, out_pos,
+			compressible, sizeof(compressible));
+
+	// Test with incompressible data.
+	bound = lzma_stream_buffer_bound(sizeof(incompressible));
+	assert_uint(bound, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(bound, <, sizeof(out));
+	out_pos = 0;
+	assert_lzma_ret(lzma_stream_buffer_encode(
+			filters, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_OK);
+	assert_uint(out_pos, >, 2 * LZMA_STREAM_HEADER_SIZE);
+	assert_uint(out_pos, <=, bound);
+	decompress_and_compare(out, out_pos,
+			incompressible, sizeof(incompressible));
+
+	// Test with too little output space.
+	bound = out_pos;
+	out_pos = 1;
+	assert_lzma_ret(lzma_stream_buffer_encode(
+			filters, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_BUF_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	// Test with unsupported or invalid arguments.
+	assert_lzma_ret(lzma_stream_buffer_encode(
+			NULL, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_PROG_ERROR);
+	assert_uint_eq(out_pos, 1);
+
+	filters[0].id = 5555;
+	assert_lzma_ret(lzma_stream_buffer_encode(
+			filters, LZMA_CHECK_CRC32, NULL,
+			incompressible, sizeof(incompressible),
+			out, &out_pos, bound), LZMA_OPTIONS_ERROR);
+	assert_uint_eq(out_pos, 1);
+#endif
+}
+
+
+extern int
+main(int argc, char **argv)
+{
+	tuktest_start(argc, argv);
+
+	tuktest_run(test_stream_buffer_bound);
+	tuktest_run(test_easy_buffer_encode);
+	tuktest_run(test_stream_buffer_encode);
+
+	return tuktest_end();
+}
diff --git a/tests/tests.cmake b/tests/tests.cmake
index e60d1a2d..d67fbc39 100644
--- a/tests/tests.cmake
+++ b/tests/tests.cmake
@@ -62,6 +62,7 @@ if(BUILD_TESTING)
         test_lzip_decoder
         test_memlimit
         test_stream_buffer_decode
+        test_stream_buffer_encode
         test_stream_flags
         test_vli
     )