diff options
Diffstat (limited to 'tests')
47 files changed, 1709 insertions, 424 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index ebc33a7..d85328d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,26 +1,19 @@ -## +## SPDX-License-Identifier: 0BSD ## Author: Lasse Collin -## -## This file has been put into the public domain. -## You can do whatever you want with this file. -## EXTRA_DIST = \ files \ ossfuzz \ + tests.cmake \ tuktest.h \ tests.h \ test_files.sh \ test_compress.sh \ - test_compress_prepared_bcj_sparc \ - test_compress_prepared_bcj_x86 \ test_compress_generated_abc \ test_compress_generated_random \ test_compress_generated_text \ test_scripts.sh \ - bcj_test.c \ - compress_prepared_bcj_sparc \ - compress_prepared_bcj_x86 \ + test_suffix.sh \ xzgrep_expected_output AM_CPPFLAGS = \ @@ -61,12 +54,16 @@ TESTS = \ test_lzip_decoder \ test_vli \ test_files.sh \ - test_compress_prepared_bcj_sparc \ - test_compress_prepared_bcj_x86 \ + test_suffix.sh \ test_compress_generated_abc \ test_compress_generated_random \ test_compress_generated_text +if COND_MICROLZMA +check_PROGRAMS += test_microlzma +TESTS += test_microlzma +endif + if COND_SCRIPTS TESTS += test_scripts.sh endif diff --git a/tests/Makefile.in b/tests/Makefile.in index 46cf0f0..5c80f0b 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -93,22 +93,23 @@ check_PROGRAMS = create_compress_files$(EXEEXT) test_check$(EXEEXT) \ test_block_header$(EXEEXT) test_index$(EXEEXT) \ test_index_hash$(EXEEXT) test_bcj_exact_size$(EXEEXT) \ test_memlimit$(EXEEXT) test_lzip_decoder$(EXEEXT) \ - test_vli$(EXEEXT) + test_vli$(EXEEXT) $(am__EXEEXT_1) TESTS = test_check$(EXEEXT) test_hardware$(EXEEXT) \ test_stream_flags$(EXEEXT) test_filter_flags$(EXEEXT) \ test_filter_str$(EXEEXT) test_block_header$(EXEEXT) \ test_index$(EXEEXT) test_index_hash$(EXEEXT) \ test_bcj_exact_size$(EXEEXT) test_memlimit$(EXEEXT) \ test_lzip_decoder$(EXEEXT) test_vli$(EXEEXT) test_files.sh \ - test_compress_prepared_bcj_sparc \ - test_compress_prepared_bcj_x86 test_compress_generated_abc \ + test_suffix.sh test_compress_generated_abc \ test_compress_generated_random test_compress_generated_text \ - $(am__append_1) -@COND_SCRIPTS_TRUE@am__append_1 = test_scripts.sh + $(am__EXEEXT_1) $(am__append_3) +@COND_MICROLZMA_TRUE@am__append_1 = test_microlzma +@COND_MICROLZMA_TRUE@am__append_2 = test_microlzma +@COND_SCRIPTS_TRUE@am__append_3 = test_scripts.sh subdir = tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_capsicum.m4 \ - $(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/getopt.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/build-to-host.m4 $(top_srcdir)/m4/getopt.m4 \ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/host-cpu-c-abi.m4 \ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ @@ -131,6 +132,7 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +@COND_MICROLZMA_TRUE@am__EXEEXT_1 = test_microlzma$(EXEEXT) create_compress_files_SOURCES = create_compress_files.c create_compress_files_OBJECTS = create_compress_files.$(OBJEXT) create_compress_files_LDADD = $(LDADD) @@ -191,6 +193,11 @@ test_memlimit_OBJECTS = test_memlimit.$(OBJEXT) test_memlimit_LDADD = $(LDADD) test_memlimit_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ $(am__DEPENDENCIES_1) +test_microlzma_SOURCES = test_microlzma.c +test_microlzma_OBJECTS = test_microlzma.$(OBJEXT) +test_microlzma_LDADD = $(LDADD) +test_microlzma_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) test_stream_flags_SOURCES = test_stream_flags.c test_stream_flags_OBJECTS = test_stream_flags.$(OBJEXT) test_stream_flags_LDADD = $(LDADD) @@ -223,7 +230,8 @@ am__depfiles_remade = ./$(DEPDIR)/create_compress_files.Po \ ./$(DEPDIR)/test_filter_str.Po ./$(DEPDIR)/test_hardware.Po \ ./$(DEPDIR)/test_index.Po ./$(DEPDIR)/test_index_hash.Po \ ./$(DEPDIR)/test_lzip_decoder.Po ./$(DEPDIR)/test_memlimit.Po \ - ./$(DEPDIR)/test_stream_flags.Po ./$(DEPDIR)/test_vli.Po + ./$(DEPDIR)/test_microlzma.Po ./$(DEPDIR)/test_stream_flags.Po \ + ./$(DEPDIR)/test_vli.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -247,12 +255,12 @@ SOURCES = create_compress_files.c test_bcj_exact_size.c \ test_block_header.c test_check.c test_filter_flags.c \ test_filter_str.c test_hardware.c test_index.c \ test_index_hash.c test_lzip_decoder.c test_memlimit.c \ - test_stream_flags.c test_vli.c + test_microlzma.c test_stream_flags.c test_vli.c DIST_SOURCES = create_compress_files.c test_bcj_exact_size.c \ test_block_header.c test_check.c test_filter_flags.c \ test_filter_str.c test_hardware.c test_index.c \ test_index_hash.c test_lzip_decoder.c test_memlimit.c \ - test_stream_flags.c test_vli.c + test_microlzma.c test_stream_flags.c test_vli.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -494,7 +502,6 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ -CAPSICUM_LIB = @CAPSICUM_LIB@ CC = @CC@ CCAS = @CCAS@ CCASDEPMODE = @CCASDEPMODE@ @@ -623,6 +630,8 @@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ +localedir_c = @localedir_c@ +localedir_c_make = @localedir_c_make@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ @@ -644,19 +653,16 @@ xz = @xz@ EXTRA_DIST = \ files \ ossfuzz \ + tests.cmake \ tuktest.h \ tests.h \ test_files.sh \ test_compress.sh \ - test_compress_prepared_bcj_sparc \ - test_compress_prepared_bcj_x86 \ test_compress_generated_abc \ test_compress_generated_random \ test_compress_generated_text \ test_scripts.sh \ - bcj_test.c \ - compress_prepared_bcj_sparc \ - compress_prepared_bcj_x86 \ + test_suffix.sh \ xzgrep_expected_output AM_CPPFLAGS = \ @@ -752,6 +758,10 @@ test_memlimit$(EXEEXT): $(test_memlimit_OBJECTS) $(test_memlimit_DEPENDENCIES) $ @rm -f test_memlimit$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_memlimit_OBJECTS) $(test_memlimit_LDADD) $(LIBS) +test_microlzma$(EXEEXT): $(test_microlzma_OBJECTS) $(test_microlzma_DEPENDENCIES) $(EXTRA_test_microlzma_DEPENDENCIES) + @rm -f test_microlzma$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_microlzma_OBJECTS) $(test_microlzma_LDADD) $(LIBS) + test_stream_flags$(EXEEXT): $(test_stream_flags_OBJECTS) $(test_stream_flags_DEPENDENCIES) $(EXTRA_test_stream_flags_DEPENDENCIES) @rm -f test_stream_flags$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_stream_flags_OBJECTS) $(test_stream_flags_LDADD) $(LIBS) @@ -777,6 +787,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_index_hash.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_lzip_decoder.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_memlimit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_microlzma.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream_flags.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_vli.Po@am__quote@ # am--include-marker @@ -1097,16 +1108,9 @@ test_files.sh.log: test_files.sh --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) -test_compress_prepared_bcj_sparc.log: test_compress_prepared_bcj_sparc - @p='test_compress_prepared_bcj_sparc'; \ - b='test_compress_prepared_bcj_sparc'; \ - $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ - --log-file $$b.log --trs-file $$b.trs \ - $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ - "$$tst" $(AM_TESTS_FD_REDIRECT) -test_compress_prepared_bcj_x86.log: test_compress_prepared_bcj_x86 - @p='test_compress_prepared_bcj_x86'; \ - b='test_compress_prepared_bcj_x86'; \ +test_suffix.sh.log: test_suffix.sh + @p='test_suffix.sh'; \ + b='test_suffix.sh'; \ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ @@ -1132,6 +1136,13 @@ test_compress_generated_text.log: test_compress_generated_text --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +test_microlzma.log: test_microlzma$(EXEEXT) + @p='test_microlzma$(EXEEXT)'; \ + b='test_microlzma'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) test_scripts.sh.log: test_scripts.sh @p='test_scripts.sh'; \ b='test_scripts.sh'; \ @@ -1242,6 +1253,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/test_index_hash.Po -rm -f ./$(DEPDIR)/test_lzip_decoder.Po -rm -f ./$(DEPDIR)/test_memlimit.Po + -rm -f ./$(DEPDIR)/test_microlzma.Po -rm -f ./$(DEPDIR)/test_stream_flags.Po -rm -f ./$(DEPDIR)/test_vli.Po -rm -f Makefile @@ -1300,6 +1312,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/test_index_hash.Po -rm -f ./$(DEPDIR)/test_lzip_decoder.Po -rm -f ./$(DEPDIR)/test_memlimit.Po + -rm -f ./$(DEPDIR)/test_microlzma.Po -rm -f ./$(DEPDIR)/test_stream_flags.Po -rm -f ./$(DEPDIR)/test_vli.Po -rm -f Makefile diff --git a/tests/bcj_test.c b/tests/bcj_test.c deleted file mode 100644 index 05de38a..0000000 --- a/tests/bcj_test.c +++ /dev/null @@ -1,65 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -/// \file bcj_test.c -/// \brief Source code of compress_prepared_bcj_* -/// -/// This is a simple program that should make the compiler to generate -/// PC-relative branches, jumps, and calls. The compiled files can then -/// be used to test the branch conversion filters. Note that this program -/// itself does nothing useful. -/// -/// Compiling: gcc -std=c99 -fPIC -c bcj_test.c -/// Don't optimize or strip. -// -// Author: Lasse Collin -// -// This file has been put into the public domain. -// You can do whatever you want with this file. -// -/////////////////////////////////////////////////////////////////////////////// - -extern int jump(int a, int b); - - -extern int -call(int a, int b) -{ - if (a < b) - a = jump(a, b); - - return a; -} - - -extern int -jump(int a, int b) -{ - // The loop generates conditional jump backwards. - while (1) { - if (a < b) { - a *= 2; - a += 3 * b; - break; - } else { - // Put enough code here to prevent JMP SHORT on x86. - a += b; - a /= 2; - b += b % 5; - a -= b / 3; - b = 2 * b + a - 1; - a *= b + a + 1; - b += a - 1; - a += b * 2 - a / 5; - } - } - - return a; -} - - -int -main(int argc, char **argv) -{ - int a = call(argc, argc + 1); - return a == 0; -} diff --git a/tests/compress_prepared_bcj_sparc b/tests/compress_prepared_bcj_sparc Binary files differdeleted file mode 100644 index 86ea7dd..0000000 --- a/tests/compress_prepared_bcj_sparc +++ /dev/null diff --git a/tests/compress_prepared_bcj_x86 b/tests/compress_prepared_bcj_x86 Binary files differdeleted file mode 100644 index bcc546f..0000000 --- a/tests/compress_prepared_bcj_x86 +++ /dev/null diff --git a/tests/create_compress_files.c b/tests/create_compress_files.c index 76aa3e3..2106ac8 100644 --- a/tests/create_compress_files.c +++ b/tests/create_compress_files.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file create_compress_files.c @@ -8,9 +10,6 @@ // // Author: Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "sysdefs.h" diff --git a/tests/files/README b/tests/files/README index 119cac4..584e6ba 100644 --- a/tests/files/README +++ b/tests/files/README @@ -1,6 +1,6 @@ -.xz and .lzma Test Files ------------------------- +.xz, .lzma, and .lz Test Files +------------------------------ 0. Introduction @@ -8,7 +8,8 @@ .lzma (LZMA_Alone), and .lz (lzip) files in decoder implementations. Many of the files have been created by hand with a hex editor, thus there is no better "source code" than the files themselves. All the - test files and this README have been put into the public domain. + test files and this README may be distributed under the terms of + the BSD Zero Clause License (0BSD). 1. File Types @@ -65,20 +66,25 @@ good-1-delta-lzma2.tiff.xz is an image file that compresses better with Delta+LZMA2 than with plain LZMA2. - good-1-x86-lzma2.xz uses the x86 filter (BCJ) and LZMA2. The - uncompressed file is compress_prepared_bcj_x86 found from the tests - directory. - - good-1-sparc-lzma2.xz uses the SPARC filter and LZMA2. The - uncompressed file is compress_prepared_bcj_sparc found from the tests - directory. - good-1-arm64-lzma2-1.xz uses the ARM64 filter and LZMA2. The uncompressed data is constructed so that it tests integer - wrap around and sign extension. + wrap around and sign extension. To recreate the file, compress + using XZ Utils 5.4.x (newer may or may not work too): + + ./debug/testfilegen-arm64 \ + | xz -T1 -Ccrc32 --arm64 \ + --lzma2=dict=64KiB,lp=2,lc=2 \ + > good-1-arm64-lzma2-1.xz good-1-arm64-lzma2-2.xz is like good-1-arm64-lzma2-1.xz but with non-zero start offset. XZ Embedded doesn't support this file. + To recreate the file, compress using XZ Utils 5.4.x (newer may or + may not work too): + + ./debug/testfilegen-arm64 \ + | xz -T1 -Ccrc32 --arm64=start=4294963200 \ + --lzma2=dict=64KiB,lp=2,lc=2 \ + > good-1-arm64-lzma2-2.xz good-1-lzma2-1.xz has two LZMA2 chunks, of which the second sets new properties. diff --git a/tests/files/good-1-sparc-lzma2.xz b/tests/files/good-1-sparc-lzma2.xz Binary files differdeleted file mode 100644 index 4532bc6..0000000 --- a/tests/files/good-1-sparc-lzma2.xz +++ /dev/null diff --git a/tests/files/good-1-x86-lzma2.xz b/tests/files/good-1-x86-lzma2.xz Binary files differdeleted file mode 100644 index 8053917..0000000 --- a/tests/files/good-1-x86-lzma2.xz +++ /dev/null diff --git a/tests/ossfuzz/Makefile b/tests/ossfuzz/Makefile index 747fb66..742e063 100644 --- a/tests/ossfuzz/Makefile +++ b/tests/ossfuzz/Makefile @@ -1,7 +1,17 @@ -fuzz: fuzz.c - $(CC) $(CFLAGS) -c fuzz.c -I ../../src/liblzma/api/ - $(CXX) $(CXXFLAGS) $(LIB_FUZZING_ENGINE) fuzz.o -o $(OUT)/fuzz \ - ../../src/liblzma/.libs/liblzma.a +# SPDX-License-Identifier: 0BSD +FUZZ_TARGET_SRCS = $(wildcard *.c) +FUZZ_TARGET_BINS = $(FUZZ_TARGET_SRCS:.c=) + +all: $(FUZZ_TARGET_BINS) + +%: %.c + $(CC) $(CFLAGS) -c $< -I ../../src/liblzma/api/ ; + $(CXX) $(CXXFLAGS) $(LIB_FUZZING_ENGINE) $(<:.c=.o) -o $(OUT)/$@ \ + ../../src/liblzma/.libs/liblzma.a ; + +# The generated binaries are not removed, just the object files. The +# binaries are created to the $(OUT) directory and must be removed by the +# fuzzing framework. clean: rm -f *.o diff --git a/tests/ossfuzz/config/fuzz.dict b/tests/ossfuzz/config/fuzz.dict deleted file mode 100644 index 932d67c..0000000 --- a/tests/ossfuzz/config/fuzz.dict +++ /dev/null @@ -1,2 +0,0 @@ -"\xFD7zXZ\x00" -"YZ" diff --git a/tests/ossfuzz/config/fuzz.options b/tests/ossfuzz/config/fuzz.options deleted file mode 100644 index d59dfc1..0000000 --- a/tests/ossfuzz/config/fuzz.options +++ /dev/null @@ -1,2 +0,0 @@ -[libfuzzer] -dict = fuzz.dict diff --git a/tests/ossfuzz/config/fuzz_decode_alone.options b/tests/ossfuzz/config/fuzz_decode_alone.options new file mode 100644 index 0000000..1351d96 --- /dev/null +++ b/tests/ossfuzz/config/fuzz_decode_alone.options @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +max_len = 4096 +dict = fuzz_lzma.dict diff --git a/tests/ossfuzz/config/fuzz_decode_stream.options b/tests/ossfuzz/config/fuzz_decode_stream.options new file mode 100644 index 0000000..bbf43ac --- /dev/null +++ b/tests/ossfuzz/config/fuzz_decode_stream.options @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +dict = fuzz_xz.dict diff --git a/tests/ossfuzz/config/fuzz_encode_stream.options b/tests/ossfuzz/config/fuzz_encode_stream.options new file mode 100644 index 0000000..86d4f0c --- /dev/null +++ b/tests/ossfuzz/config/fuzz_encode_stream.options @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +[libfuzzer] +max_len = 4096 diff --git a/tests/ossfuzz/config/fuzz_lzma.dict b/tests/ossfuzz/config/fuzz_lzma.dict new file mode 100644 index 0000000..b9d5dff --- /dev/null +++ b/tests/ossfuzz/config/fuzz_lzma.dict @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: 0BSD + +# first 5 header bytes of .lzma archives based on the info from +# /doc/lzma-file-format.txt + +# byte 0 is created by encoding LZMA property values (lc, lp, pb) +# using the algorithm described in the documentation above. + +# lc=3, lp=0, pb=2 and dictionary size = 0x00100000 +"\x5d\x00\x00\x10\x00" + +# lc=3, lp=1, pb=3 and dictionary size = 0x00100000 +"\x93\x00\x00\x10\x00" + +# lc=2, lp=2, pb=4 and dictionary size = 0x00100000 +"\xc8\x00\x00\x10\x00" + +# lc=1, lp=3, pb=1 and dictionary size = 0x00200000 +"\x49\x00\x00\x20\x00" + +# lc=0, lp=4, pb=0 and dictionary size = 0x00200000 +"\x24\x00\x00\x20\x00" diff --git a/tests/ossfuzz/config/fuzz_xz.dict b/tests/ossfuzz/config/fuzz_xz.dict new file mode 100644 index 0000000..6ba4f24 --- /dev/null +++ b/tests/ossfuzz/config/fuzz_xz.dict @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: 0BSD + +"\xFD7zXZ\x00" +"YZ" diff --git a/tests/ossfuzz/fuzz.c b/tests/ossfuzz/fuzz.c deleted file mode 100644 index 6d89930..0000000 --- a/tests/ossfuzz/fuzz.c +++ /dev/null @@ -1,82 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -/// \file fuzz.c -/// \brief Fuzz test program for liblzma -// -// Author: Lasse Collin -// -// This file has been put into the public domain. -// You can do whatever you want with this file. -// -/////////////////////////////////////////////////////////////////////////////// - -#include <inttypes.h> -#include <stdlib.h> -#include <stdio.h> -#include "lzma.h" - - -// Output buffer for decompressed data. This is write only; nothing cares -// about the actual data written here. -static uint8_t outbuf[4096]; - - -extern int -LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) -{ - // Some header values can make liblzma allocate a lot of RAM - // (up to about 4 GiB with liblzma 5.2.x). We set a limit here to - // prevent extreme allocations when fuzzing. - const uint64_t memlimit = 300 << 20; // 300 MiB - - // Initialize a .xz decoder using the above memory usage limit. - // Enable support for concatenated .xz files which is used when - // decompressing regular .xz files (instead of data embedded inside - // some other file format). Integrity checks on the uncompressed - // data are ignored to make fuzzing more effective (incorrect check - // values won't prevent the decoder from processing more input). - // - // The flag LZMA_IGNORE_CHECK doesn't disable verification of header - // CRC32 values. Those checks are disabled when liblzma is built - // with the #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION. - lzma_stream strm = LZMA_STREAM_INIT; - lzma_ret ret = lzma_stream_decoder(&strm, memlimit, - LZMA_CONCATENATED | LZMA_IGNORE_CHECK); - if (ret != LZMA_OK) { - // This should never happen unless the system has - // no free memory or address space to allow the small - // allocations that the initialization requires. - fprintf(stderr, "lzma_stream_decoder() failed (%d)\n", ret); - abort(); - } - - // Give the whole input buffer at once to liblzma. - // Output buffer isn't initialized as liblzma only writes to it. - strm.next_in = inbuf; - strm.avail_in = inbuf_size; - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - - while ((ret = lzma_code(&strm, LZMA_FINISH)) == LZMA_OK) { - if (strm.avail_out == 0) { - // outbuf became full. We don't care about the - // uncompressed data there, so we simply reuse - // the outbuf and overwrite the old data. - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - } - } - - // LZMA_PROG_ERROR should never happen as long as the code calling - // the liblzma functions is correct. Thus LZMA_PROG_ERROR is a sign - // of a bug in either this function or in liblzma. - if (ret == LZMA_PROG_ERROR) { - fprintf(stderr, "lzma_code() returned LZMA_PROG_ERROR\n"); - abort(); - } - - // Free the allocated memory. - lzma_end(&strm); - - return 0; -} diff --git a/tests/ossfuzz/fuzz_common.h b/tests/ossfuzz/fuzz_common.h new file mode 100644 index 0000000..4537f1b --- /dev/null +++ b/tests/ossfuzz/fuzz_common.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_common.h +/// \brief Common macros and functions needed by the fuzz targets +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" + +// Some header values can make liblzma allocate a lot of RAM +// (up to about 4 GiB with liblzma 5.2.x). We set a limit here to +// prevent extreme allocations when fuzzing. +#define MEM_LIMIT (300 << 20) // 300 MiB + + +static void +fuzz_code(lzma_stream *stream, const uint8_t *inbuf, size_t inbuf_size) { + // Output buffer for decompressed data. This is write only; nothing + // cares about the actual data written here. + uint8_t outbuf[4096]; + + // Give the whole input buffer at once to liblzma. + // Output buffer isn't initialized as liblzma only writes to it. + stream->next_in = inbuf; + stream->avail_in = inbuf_size; + stream->next_out = outbuf; + stream->avail_out = sizeof(outbuf); + + lzma_ret ret; + while ((ret = lzma_code(stream, LZMA_FINISH)) == LZMA_OK) { + if (stream->avail_out == 0) { + // outbuf became full. We don't care about the + // uncompressed data there, so we simply reuse + // the outbuf and overwrite the old data. + stream->next_out = outbuf; + stream->avail_out = sizeof(outbuf); + } + } + + // LZMA_PROG_ERROR should never happen as long as the code calling + // the liblzma functions is correct. Thus LZMA_PROG_ERROR is a sign + // of a bug in either this function or in liblzma. + if (ret == LZMA_PROG_ERROR) { + fprintf(stderr, "lzma_code() returned LZMA_PROG_ERROR\n"); + abort(); + } +} diff --git a/tests/ossfuzz/fuzz_decode_alone.c b/tests/ossfuzz/fuzz_decode_alone.c new file mode 100644 index 0000000..1ef2f9e --- /dev/null +++ b/tests/ossfuzz/fuzz_decode_alone.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_decode_alone.c +/// \brief Fuzz test program for liblzma .lzma decoding +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + lzma_stream strm = LZMA_STREAM_INIT; + // Initialize a LZMA alone decoder using the memory usage limit + // defined in fuzz_common.h + lzma_ret ret = lzma_alone_decoder(&strm, MEM_LIMIT); + + if (ret != LZMA_OK) { + // This should never happen unless the system has + // no free memory or address space to allow the small + // allocations that the initialization requires. + fprintf(stderr, "lzma_alone_decoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf, inbuf_size); + + // Free the allocated memory. + lzma_end(&strm); + return 0; +} diff --git a/tests/ossfuzz/fuzz_decode_stream.c b/tests/ossfuzz/fuzz_decode_stream.c new file mode 100644 index 0000000..d786061 --- /dev/null +++ b/tests/ossfuzz/fuzz_decode_stream.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_decode_stream.c +/// \brief Fuzz test program for single threaded .xz decoding +// +// Authors: Lasse Collin +// Maksym Vatsyk +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + lzma_stream strm = LZMA_STREAM_INIT; + // Initialize a .xz decoder using the memory usage limit + // defined in fuzz_common.h + // + // Enable support for concatenated .xz files which is used when + // decompressing regular .xz files (instead of data embedded inside + // some other file format). Integrity checks on the uncompressed + // data are ignored to make fuzzing more effective (incorrect check + // values won't prevent the decoder from processing more input). + // + // The flag LZMA_IGNORE_CHECK doesn't disable verification of + // header CRC32 values. Those checks are disabled when liblzma is + // built with the #define FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION. + lzma_ret ret = lzma_stream_decoder(&strm, MEM_LIMIT, + LZMA_CONCATENATED | LZMA_IGNORE_CHECK); + + if (ret != LZMA_OK) { + // This should never happen unless the system has + // no free memory or address space to allow the small + // allocations that the initialization requires. + fprintf(stderr, "lzma_stream_decoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf, inbuf_size); + + // Free the allocated memory. + lzma_end(&strm); + + return 0; +} diff --git a/tests/ossfuzz/fuzz_encode_stream.c b/tests/ossfuzz/fuzz_encode_stream.c new file mode 100644 index 0000000..9438263 --- /dev/null +++ b/tests/ossfuzz/fuzz_encode_stream.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file fuzz_encode_stream.c +/// \brief Fuzz test program for .xz encoding +// +// Authors: Maksym Vatsyk +// Lasse Collin +// +/////////////////////////////////////////////////////////////////////////////// + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "lzma.h" +#include "fuzz_common.h" + + +extern int +LLVMFuzzerTestOneInput(const uint8_t *inbuf, size_t inbuf_size) +{ + if (inbuf_size == 0) { + fprintf(stderr, "no input data provided\n"); + return 0; + } + + // Set the LZMA options based on the first input byte. The fuzzer + // will learn through its mutational genetic algorithm with the + // code coverage feedback that the first byte must be one of the + // values with a switch case label. This allows us to have one fuzz + // target cover many critical code paths so the fuzz resources can + // be used efficiently. + uint32_t preset_level; + const uint8_t decider = inbuf[0]; + + switch (decider) { + case 0: + case 1: + case 5: + preset_level = (uint32_t)decider; + break; + case 6: + preset_level = 0 | LZMA_PRESET_EXTREME; + break; + case 7: + preset_level = 3 | LZMA_PRESET_EXTREME; + break; + default: + return 0; + } + + // Initialize lzma_options with the above preset level + lzma_options_lzma opt_lzma; + if (lzma_lzma_preset(&opt_lzma, preset_level)){ + fprintf(stderr, "lzma_lzma_preset() failed\n"); + abort(); + } + + // Set the filter chain as only LZMA2. + lzma_filter filters[2] = { + { + .id = LZMA_FILTER_LZMA2, + .options = &opt_lzma, + }, { + .id = LZMA_VLI_UNKNOWN, + } + }; + + // initialize empty LZMA stream + lzma_stream strm = LZMA_STREAM_INIT; + + // Initialize the stream encoder using the above + // stream, filter chain and CRC64. + lzma_ret ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC64); + if (ret != LZMA_OK) { + fprintf(stderr, "lzma_stream_encoder() failed (%d)\n", ret); + abort(); + } + + fuzz_code(&strm, inbuf + 1, inbuf_size - 1); + + // Free the allocated memory. + lzma_end(&strm); + return 0; +} diff --git a/tests/test_bcj_exact_size.c b/tests/test_bcj_exact_size.c index 551166c..fa82f0d 100644 --- a/tests/test_bcj_exact_size.c +++ b/tests/test_bcj_exact_size.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_bcj_exact_size.c @@ -7,9 +9,6 @@ // // Author: Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_block_header.c b/tests/test_block_header.c index b310135..d8e6221 100644 --- a/tests/test_block_header.c +++ b/tests/test_block_header.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_block_header.c @@ -6,9 +8,6 @@ // Authors: Lasse Collin // Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" @@ -320,7 +319,7 @@ test_lzma_block_header_encode(void) uint8_t flags = out[1]; // Should have number of filters = 1 - assert_uint_eq((flags & 0x3) + 1, 1); + assert_uint_eq((flags & 0x3) + 1U, 1); // Bits 2-7 must be empty not set assert_uint_eq(flags & (0xFF - 0x3), 0); diff --git a/tests/test_check.c b/tests/test_check.c index cb1ad25..9d51aa0 100644 --- a/tests/test_check.c +++ b/tests/test_check.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_check.c @@ -6,9 +8,6 @@ // Authors: Lasse Collin // Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_compress.sh b/tests/test_compress.sh index ff13cef..a10343a 100755 --- a/tests/test_compress.sh +++ b/tests/test_compress.sh @@ -1,26 +1,37 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD ############################################################################### # # Author: Lasse Collin # -# This file has been put into the public domain. -# You can do whatever you want with this file. -# ############################################################################### +# Mandatory argument: +# $1 = test filename: compress_generated_<foo> or compress_prepared_<foo> +# +# Optional argument: +# $2 = directory of the xz and xzdec executables + +XZ=${2:-../src/xz}/xz +XZDEC=${2:-../src/xzdec}/xzdec + # If xz wasn't built, this test is skipped. -if test -x ../src/xz/xz ; then - : -else +if test ! -x "$XZ"; then + echo "xz was not built, skipping this test." exit 77 fi +# xzdec isn't mandatory for this script. +test -x "$XZDEC" || XZDEC= + # If compression or decompression support is missing, this test is skipped. # This isn't perfect as if only some compressors or decompressors are disabled # then this script can still fail because for now this doesn't check the # availability of each filter. -if grep 'define HAVE_ENCODERS' ../config.h > /dev/null \ +if test ! -f ../config.h ; then + : +elif grep 'define HAVE_ENCODERS' ../config.h > /dev/null \ && grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then : else @@ -76,12 +87,12 @@ test_xz() { fi } -XZ="../src/xz/xz --memlimit-compress=48MiB --memlimit-decompress=5MiB \ - --no-adjust --threads=1 --check=crc32" -grep "define HAVE_CHECK_CRC64" ../config.h > /dev/null \ - && XZ="$XZ --check=crc64" -XZDEC="../src/xzdec/xzdec" # No memory usage limiter available -test -x ../src/xzdec/xzdec || XZDEC= +# Set memory usage limit for xz. xzdec has no memory usage limiter. +# Force single-threaded mode as the test files are small +# (so more than one thread wouldn't be used anyway) and +# the tests are usually run in parallel. +XZ="$XZ --memlimit-compress=48MiB --memlimit-decompress=5MiB \ + --no-adjust --threads=1" # Create the required input file if needed. # @@ -130,21 +141,26 @@ test_xz -4 test_filter() { - grep "define HAVE_ENCODER_$1 1" ../config.h > /dev/null || return - grep "define HAVE_DECODER_$1 1" ../config.h > /dev/null || return + if test -f ../config.h ; then + grep "define HAVE_ENCODER_$1 1" ../config.h > /dev/null \ + || return + grep "define HAVE_DECODER_$1 1" ../config.h > /dev/null \ + || return + fi shift - test_xz "$@" --lzma2=dict=64KiB,nice=32,mode=fast + test_xz --filters="$* lzma2:dict=64KiB,nice=32,mode=fast" } -test_filter DELTA --delta=dist=1 -test_filter DELTA --delta=dist=4 -test_filter DELTA --delta=dist=256 -test_filter X86 --x86 -test_filter POWERPC --power -test_filter IA64 --ia64 -test_filter ARM --arm -test_filter ARMTHUMB --armthumb -test_filter ARM64 --arm64 -test_filter SPARC --sparc +test_filter DELTA delta:dist=1 +test_filter DELTA delta:dist=4 +test_filter DELTA delta:dist=256 +test_filter X86 x86 +test_filter POWERPC powerpc +test_filter IA64 ia64 +test_filter ARM arm +test_filter ARMTHUMB armthumb +test_filter ARM64 arm64 +test_filter SPARC sparc +test_filter RISCV riscv exit 0 diff --git a/tests/test_compress_generated_abc b/tests/test_compress_generated_abc index 43c6951..b31d631 100755 --- a/tests/test_compress_generated_abc +++ b/tests/test_compress_generated_abc @@ -1,3 +1,4 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD exec "$srcdir/test_compress.sh" compress_generated_abc diff --git a/tests/test_compress_generated_random b/tests/test_compress_generated_random index e47555d..c20f3da 100755 --- a/tests/test_compress_generated_random +++ b/tests/test_compress_generated_random @@ -1,3 +1,4 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD exec "$srcdir/test_compress.sh" compress_generated_random diff --git a/tests/test_compress_generated_text b/tests/test_compress_generated_text index 412ae0e..2c8c171 100755 --- a/tests/test_compress_generated_text +++ b/tests/test_compress_generated_text @@ -1,3 +1,4 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD exec "$srcdir/test_compress.sh" compress_generated_text diff --git a/tests/test_compress_prepared_bcj_sparc b/tests/test_compress_prepared_bcj_sparc deleted file mode 100755 index deb76ef..0000000 --- a/tests/test_compress_prepared_bcj_sparc +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec "$srcdir/test_compress.sh" compress_prepared_bcj_sparc diff --git a/tests/test_compress_prepared_bcj_x86 b/tests/test_compress_prepared_bcj_x86 deleted file mode 100755 index 3452d7f..0000000 --- a/tests/test_compress_prepared_bcj_x86 +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -exec "$srcdir/test_compress.sh" compress_prepared_bcj_x86 diff --git a/tests/test_files.sh b/tests/test_files.sh index 60402e7..99c9f2f 100755 --- a/tests/test_files.sh +++ b/tests/test_files.sh @@ -1,17 +1,18 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD ############################################################################### # # Author: Lasse Collin # -# This file has been put into the public domain. -# You can do whatever you want with this file. -# ############################################################################### -# If both xz and xzdec were not build, skip this test. -XZ=../src/xz/xz -XZDEC=../src/xzdec/xzdec +# Optional argument: +# $1 = directory of the xz executable + +# If both xz and xzdec were not built, skip this test. +XZ=${1:-../src/xz}/xz +XZDEC=${2:-../src/xzdec}/xzdec test -x "$XZ" || XZ= test -x "$XZDEC" || XZDEC= if test -z "$XZ$XZDEC"; then @@ -23,7 +24,9 @@ fi # This isn't perfect as if only some decompressors are disabled # then some good files might not decompress and the test fails # for a (kind of) wrong reason. -if grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then +if test ! -f ../config.h ; then + : +elif grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then : else echo "Decompression support is disabled, skipping this test." @@ -35,6 +38,7 @@ fi EXIT_STATUS=0 have_feature() { + test -f ../config.h || return 0 grep "define HAVE_$1 1" ../config.h > /dev/null && return 0 printf '%s: Skipping because HAVE_%s is not enabled\n' "$2" "$1" EXIT_STATUS=77 @@ -49,8 +53,10 @@ have_feature() # If these integrity check types were disabled at build time, # allow the tests to pass still. NO_WARN= -grep 'define HAVE_CHECK_CRC64' ../config.h > /dev/null || NO_WARN=-qQ -grep 'define HAVE_CHECK_SHA256' ../config.h > /dev/null || NO_WARN=-qQ +if test -f ../config.h ; then + grep 'define HAVE_CHECK_CRC64' ../config.h > /dev/null || NO_WARN=-qQ + grep 'define HAVE_CHECK_SHA256' ../config.h > /dev/null || NO_WARN=-qQ +fi for I in "$srcdir"/files/good-*.xz do @@ -80,6 +86,11 @@ do have_feature DECODER_ARM64 "$I" || continue ;; esac + case $I in + */good-1-riscv-lzma2-*.xz) + have_feature DECODER_RISCV "$I" || continue + ;; + esac if test -z "$XZ" || "$XZ" $NO_WARN -dc "$I" > /dev/null; then : diff --git a/tests/test_filter_flags.c b/tests/test_filter_flags.c index 6d9f0b9..9dfc16e 100644 --- a/tests/test_filter_flags.c +++ b/tests/test_filter_flags.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_filter_flags.c @@ -6,9 +8,6 @@ // Authors: Jia Tan // Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" @@ -48,6 +47,10 @@ static lzma_filter bcj_filters_encoders[] = { #ifdef HAVE_ENCODER_SPARC { LZMA_FILTER_SPARC, NULL }, #endif +#ifdef HAVE_ENCODER_RISCV + { LZMA_FILTER_RISCV, NULL }, +#endif + { LZMA_VLI_UNKNOWN, NULL } }; // HAVE_ENCODERS ifdef not terminated here because decoders are @@ -77,6 +80,10 @@ static lzma_filter bcj_filters_decoders[] = { #ifdef HAVE_DECODER_SPARC { LZMA_FILTER_SPARC, NULL }, #endif +#ifdef HAVE_DECODER_RISCV + { LZMA_FILTER_RISCV, NULL }, +#endif + { LZMA_VLI_UNKNOWN, NULL } }; #endif #endif @@ -94,6 +101,7 @@ test_lzma_filter_flags_size(void) // block header. uint32_t size = 0; if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA1)) { + // LZMA1 isn't supported in .xz so we get LZMA_PROG_ERROR. assert_lzma_ret(lzma_filter_flags_size(&size, &lzma1_filter), LZMA_PROG_ERROR); } @@ -104,12 +112,8 @@ test_lzma_filter_flags_size(void) assert_true(size != 0 && size < LZMA_BLOCK_HEADER_SIZE_MAX); } - // Do not use macro ARRAY_SIZE() in the for loop condition directly. - // If the BCJ filters are not configured and built, then ARRAY_SIZE() - // will return 0 and cause a warning because the for loop will never - // execute since any unsigned number cannot be < 0 (-Werror=type-limits). - const uint32_t bcj_array_size = ARRAY_SIZE(bcj_filters_encoders); - for (uint32_t i = 0; i < bcj_array_size; i++) { + for (size_t i = 0; bcj_filters_encoders[i].id != LZMA_VLI_UNKNOWN; + ++i) { assert_lzma_ret(lzma_filter_flags_size(&size, &bcj_filters_encoders[i]), LZMA_OK); assert_true(size != 0 && size < LZMA_BLOCK_HEADER_SIZE_MAX); @@ -153,7 +157,7 @@ verify_filter_flags_encode(lzma_filter *filter, bool should_encode) // First calculate the size of Filter Flags to know how much // memory to allocate to hold the encoded Filter Flags assert_lzma_ret(lzma_filter_flags_size(&size, filter), LZMA_OK); - uint8_t *encoded_out = tuktest_malloc(size * sizeof(uint8_t)); + uint8_t *encoded_out = tuktest_malloc(size); size_t out_pos = 0; if (!should_encode) { assert_false(lzma_filter_flags_encode(filter, encoded_out, @@ -220,8 +224,8 @@ test_lzma_filter_flags_encode(void) .start_offset = 257 }; - const uint32_t bcj_array_size = ARRAY_SIZE(bcj_filters_encoders); - for (uint32_t i = 0; i < bcj_array_size; i++) { + for (size_t i = 0; bcj_filters_encoders[i].id != LZMA_VLI_UNKNOWN; + ++i) { // NULL options should pass for bcj filters verify_filter_flags_encode(&bcj_filters_encoders[i], true); lzma_filter bcj_with_options = { @@ -263,7 +267,6 @@ test_lzma_filter_flags_encode(void) size_t out_size = LZMA_BLOCK_HEADER_SIZE_MAX; uint8_t out[LZMA_BLOCK_HEADER_SIZE_MAX]; - // Filter ID outside of valid range assert_lzma_ret(lzma_filter_flags_encode(&bad_filter, out, &out_pos, out_size), LZMA_PROG_ERROR); @@ -328,7 +331,8 @@ test_lzma_filter_flags_encode(void) // because it is agnostic to the type of options used in the call #if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) static void -verify_filter_flags_decode(lzma_filter *filter_in, lzma_filter *filter_out) +verify_filter_flags_decode(const lzma_filter *filter_in, + lzma_filter *filter_out) { uint32_t total_size = 0; @@ -383,8 +387,8 @@ test_lzma_filter_flags_decode(void) free(decoded); } - const uint32_t bcj_array_size = ARRAY_SIZE(bcj_filters_decoders); - for (uint32_t i = 0; i < bcj_array_size; i++) { + for (size_t i = 0; bcj_filters_decoders[i].id != LZMA_VLI_UNKNOWN; + ++i) { if (lzma_filter_encoder_is_supported( bcj_filters_decoders[i].id)) { lzma_filter bcj_decoded = { @@ -398,7 +402,11 @@ test_lzma_filter_flags_decode(void) &bcj_decoded); assert_true(bcj_decoded.options == NULL); - // Next test with offset + // Next test with start_offset. + // + // NOTE: The encoder and decoder don't verify if + // the start_offset is valid for the filter. Only + // the encoder or decoder initialization does. lzma_options_bcj options = { .start_offset = 257 }; diff --git a/tests/test_filter_str.c b/tests/test_filter_str.c index 15aee55..42d9a22 100644 --- a/tests/test_filter_str.c +++ b/tests/test_filter_str.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_filter_str.c @@ -5,9 +7,6 @@ // // Author: Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" @@ -20,37 +19,56 @@ test_lzma_str_to_filters(void) int error_pos; // Test with NULL string. + error_pos = -1; assert_true(lzma_str_to_filters(NULL, &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 0); // Test with NULL filter array. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2", &error_pos, NULL, 0, NULL) != NULL); + assert_int_eq(error_pos, 0); // Test with unsupported flags. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, UINT32_MAX, NULL) != NULL); + assert_int_eq(error_pos, 0); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, LZMA_STR_NO_SPACES << 1, NULL) != NULL); + assert_int_eq(error_pos, 0); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, LZMA_STR_NO_SPACES, NULL) != NULL); + assert_int_eq(error_pos, 0); // Test with empty string. + error_pos = -1; assert_true(lzma_str_to_filters("", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 0); // Test with invalid filter name and missing filter name. + error_pos = -1; + assert_true(lzma_str_to_filters("abcd", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 0); + + error_pos = -1; assert_true(lzma_str_to_filters("lzma2 abcd", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 6); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2--abcd", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 7); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2--", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 7); @@ -58,12 +76,15 @@ test_lzma_str_to_filters(void) // Test LZMA_STR_ALL_FILTERS flag (should work with LZMA1 if built). #if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) // Using LZMA1 as a Filter should fail without LZMA_STR_ALL_FILTERS. + error_pos = -1; assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 0); + error_pos = -1; assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, LZMA_STR_ALL_FILTERS, NULL) == NULL); + assert_int_eq(error_pos, 5); // Verify Filters array IDs are correct. The array should contain // only two elements: @@ -79,11 +100,15 @@ test_lzma_str_to_filters(void) // same Filter multiple times in the chain and having a non-last // Filter like lzma2 appear before another Filter. // Without the flag, "lzma2 lzma2" must fail. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2 lzma2", &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 11); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2 lzma2", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) == NULL); + assert_int_eq(error_pos, 11); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_FILTER_LZMA2); @@ -92,48 +117,65 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Should fail with invalid Filter options (lc + lp must be <= 4). + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:lc=3,lp=3", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) != NULL); + assert_int_eq(error_pos, 15); // Test invalid option name. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:foo=1,bar=2", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 6); + error_pos = -1; + assert_true(lzma_str_to_filters("lzma2:pb=1,bar=2", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 11); + // Test missing option value. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:lc=", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 9); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:=,pb=1", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 6); // Test unsupported preset value. + error_pos = -1; assert_true(lzma_str_to_filters("-10", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 2); + error_pos = -1; assert_true(lzma_str_to_filters("-5f", &error_pos, filters, 0, NULL) != NULL); assert_int_eq(error_pos, 2); // Test filter chain too long. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2 lzma2 lzma2 lzma2 lzma2", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) != NULL); - assert_int_eq(error_pos, 24); + assert_int_eq(error_pos, 24); // The fifth is too many. #if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) // Should fail with a Filter not supported in the .xz format (lzma1). + error_pos = -1; assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) != NULL); + assert_int_eq(error_pos, 0); #endif // Test setting options with the "=" format. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2=dict=4096,lc=2,lp=2,pb=1," "mode=fast,nice=3,mf=hc3,depth=10", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 63); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -151,8 +193,10 @@ test_lzma_str_to_filters(void) #if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) // Test BCJ Filter options. + error_pos = -1; assert_true(lzma_str_to_filters("x86:start=16", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) == NULL); + assert_int_eq(error_pos, 12); assert_uint_eq(filters[0].id, LZMA_FILTER_X86); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -165,8 +209,10 @@ test_lzma_str_to_filters(void) #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) // Test Delta Filter options. + error_pos = -1; assert_true(lzma_str_to_filters("delta:dist=20", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) == NULL); + assert_int_eq(error_pos, 13); assert_uint_eq(filters[0].id, LZMA_FILTER_DELTA); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -178,8 +224,10 @@ test_lzma_str_to_filters(void) #endif // Test skipping leading spaces. + error_pos = -1; assert_true(lzma_str_to_filters(" lzma2", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 9); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -187,8 +235,10 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Test skipping trailing spaces. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2 ", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 9); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -196,8 +246,10 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Test with "--" instead of space separating. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2--lzma2", &error_pos, filters, LZMA_STR_NO_VALIDATION, NULL) == NULL); + assert_int_eq(error_pos, 12); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_FILTER_LZMA2); @@ -206,24 +258,30 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Test preset with and without leading "-", and with "e". + error_pos = -1; assert_true(lzma_str_to_filters("-3", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 2); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); lzma_filters_free(filters, NULL); + error_pos = -1; assert_true(lzma_str_to_filters("4", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 1); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); lzma_filters_free(filters, NULL); + error_pos = -1; assert_true(lzma_str_to_filters("9e", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 2); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -231,8 +289,10 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Test using a preset as an lzma2 option. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:preset=9e", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 15); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -240,23 +300,33 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); // Test setting dictionary size with invalid modifier suffix. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=4096ZiB", &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 15); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=4096KiBs", &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 15); // Test option that cannot have multiplier modifier. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:pb=1k", &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 10); // Test option value too large. + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=4096GiB", &error_pos, filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 11); // Test valid uses of multiplier modifiers (k,m,g). + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=4096KiB", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 18); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -266,8 +336,10 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=40Mi", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 15); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -277,8 +349,10 @@ test_lzma_str_to_filters(void) lzma_filters_free(filters, NULL); + error_pos = -1; assert_true(lzma_str_to_filters("lzma2:dict=1g", &error_pos, filters, 0, NULL) == NULL); + assert_int_eq(error_pos, 13); assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); @@ -362,6 +436,10 @@ test_lzma_str_from_filters(void) assert_true(lzma_str_to_filters("x86 lzma2", NULL, filters, 0, NULL) == NULL); + // It always allocates the options structure even when it's not + // needed due to start_offset = 0 being the default. + assert_true(filters[0].options != NULL); + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, 0, NULL), LZMA_OK); @@ -370,9 +448,7 @@ test_lzma_str_from_filters(void) free(output_str); // Test setting BCJ option to NULL. - assert_false(filters[0].options == NULL); free(filters[0].options); - filters[0].options = NULL; assert_lzma_ret(lzma_str_from_filters(&output_str, filters, 0, NULL), @@ -386,6 +462,7 @@ test_lzma_str_from_filters(void) lzma_options_lzma opts; assert_false(lzma_lzma_preset(&opts, LZMA_PRESET_DEFAULT)); + // Test with too many Filters (array terminated after 4+ filters). lzma_filter oversized_filters[LZMA_FILTERS_MAX + 2]; @@ -438,6 +515,9 @@ static const char supported_encoders[][9] = { #ifdef HAVE_ENCODER_ARM64 "arm64", #endif +#ifdef HAVE_ENCODER_RISCV + "riscv", +#endif #ifdef HAVE_ENCODER_DELTA "delta", #endif @@ -466,6 +546,9 @@ static const char supported_decoders[][9] = { #ifdef HAVE_DECODER_ARM64 "arm64", #endif +#ifdef HAVE_DECODER_RISCV + "riscv", +#endif #ifdef HAVE_DECODER_DELTA "delta", #endif @@ -494,6 +577,9 @@ static const char supported_filters[][9] = { #if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64) "arm64", #endif +#if defined(HAVE_ENCODER_RISCV) || defined(HAVE_DECODER_RISCV) + "riscv", +#endif #if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) "delta", #endif @@ -512,18 +598,23 @@ test_lzma_str_list_filters(void) // Test with bad flags. assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, LZMA_STR_NO_VALIDATION , NULL), LZMA_OPTIONS_ERROR); + assert_true(str == NULL); assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, LZMA_STR_NO_SPACES, NULL), LZMA_OPTIONS_ERROR); + assert_true(str == NULL); // Test with bad Filter ID. assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN - 1, 0, NULL), LZMA_OPTIONS_ERROR); + assert_true(str == NULL); // Test LZMA_STR_ENCODER flag. assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, LZMA_STR_ENCODER, NULL), LZMA_OK); + // NOTE: Just checking for "contains" is a bit weak check as + // "arm" matches "armthumb" and "arm64" too. for (uint32_t i = 0; i < ARRAY_SIZE(supported_encoders); i++) assert_str_contains(str, supported_encoders[i]); @@ -541,6 +632,7 @@ test_lzma_str_list_filters(void) // Test LZMA_STR_GETOPT_LONG flag. assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, LZMA_STR_GETOPT_LONG, NULL), LZMA_OK); + assert_str_contains(str, "--"); free(str); diff --git a/tests/test_hardware.c b/tests/test_hardware.c index c72d9b2..45763f6 100644 --- a/tests/test_hardware.c +++ b/tests/test_hardware.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_hardware.c @@ -9,9 +11,6 @@ // // Author: Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_index.c b/tests/test_index.c index a14b33d..ba1b978 100644 --- a/tests/test_index.c +++ b/tests/test_index.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_index.c @@ -8,10 +10,6 @@ // Authors: Jia Tan // Lasse Collin // -// -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" @@ -25,8 +23,11 @@ #define MEMLIMIT (UINT64_C(1) << 20) +#ifdef HAVE_ENCODERS static uint8_t *decode_buffer; static size_t decode_buffer_size = 0; +#endif + static lzma_index *decode_test_index; @@ -44,8 +45,11 @@ test_lzma_index_memusage(void) assert_uint_eq(lzma_index_memusage((lzma_vli)UINT32_MAX + 1, 1), UINT64_MAX); - // The maximum number of Blocks should be LZMA_VLI_MAX - assert_uint_eq(lzma_index_memusage(1, LZMA_VLI_MAX), UINT64_MAX); + // While the number of blocks is lzma_vli, the real maximum value is + // much smaller than LZMA_VLI_MAX. Just check that it fails with a + // huge but valid VLI and that it succeeds with a smaller one. + assert_uint_eq(lzma_index_memusage(1, LZMA_VLI_MAX / 5), UINT64_MAX); + assert_uint(lzma_index_memusage(1, LZMA_VLI_MAX / 11), <, UINT64_MAX); // Number of Streams must be non-zero assert_uint_eq(lzma_index_memusage(0, 1), UINT64_MAX); @@ -100,7 +104,7 @@ test_lzma_index_memused(void) static void test_lzma_index_append(void) { - // Basic input-ouput test done here. + // Basic input-output test done here. // Less trivial tests for this function are done throughout // other tests. @@ -135,7 +139,7 @@ test_lzma_index_append(void) lzma_index_end(idx, NULL); // Test compressed .xz file size growing too large. This also tests - // a failing assert fixed in 68bda971bb8b666a009331455fcedb4e18d837a4. + // a failing assert fixed in ae5c07b22a6b3766b84f409f1b6b5c100469068a. // Should result in LZMA_DATA_ERROR. idx = lzma_index_init(NULL); @@ -224,28 +228,28 @@ test_lzma_index_checks(void) assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), LZMA_OK); assert_uint_eq(lzma_index_checks(idx), - UINT32_C(1) << LZMA_CHECK_NONE); + LZMA_INDEX_CHECK_MASK_NONE); // Set the check type to CRC32 and repeat stream_flags.check = LZMA_CHECK_CRC32; assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), LZMA_OK); assert_uint_eq(lzma_index_checks(idx), - UINT32_C(1) << LZMA_CHECK_CRC32); + LZMA_INDEX_CHECK_MASK_CRC32); // Set the check type to CRC64 and repeat stream_flags.check = LZMA_CHECK_CRC64; assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), LZMA_OK); assert_uint_eq(lzma_index_checks(idx), - UINT32_C(1) << LZMA_CHECK_CRC64); + LZMA_INDEX_CHECK_MASK_CRC64); // Set the check type to SHA256 and repeat stream_flags.check = LZMA_CHECK_SHA256; assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), LZMA_OK); assert_uint_eq(lzma_index_checks(idx), - UINT32_C(1) << LZMA_CHECK_SHA256); + LZMA_INDEX_CHECK_MASK_SHA256); // Create second lzma_index and cat to first lzma_index *second = lzma_index_init(NULL); @@ -257,14 +261,14 @@ test_lzma_index_checks(void) LZMA_OK); assert_uint_eq(lzma_index_checks(second), - UINT32_C(1) << LZMA_CHECK_CRC32); + LZMA_INDEX_CHECK_MASK_CRC32); assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); // Index should now have both CRC32 and SHA256 assert_uint_eq(lzma_index_checks(idx), - (UINT32_C(1) << LZMA_CHECK_CRC32) | - (UINT32_C(1) << LZMA_CHECK_SHA256)); + LZMA_INDEX_CHECK_MASK_CRC32 | + LZMA_INDEX_CHECK_MASK_SHA256); // Change the check type of the second Stream to SHA256 stream_flags.check = LZMA_CHECK_SHA256; @@ -273,7 +277,7 @@ test_lzma_index_checks(void) // Index should now have only SHA256 assert_uint_eq(lzma_index_checks(idx), - UINT32_C(1) << LZMA_CHECK_SHA256); + LZMA_INDEX_CHECK_MASK_SHA256); // Test with a third Stream lzma_index *third = lzma_index_init(NULL); @@ -284,14 +288,14 @@ test_lzma_index_checks(void) LZMA_OK); assert_uint_eq(lzma_index_checks(third), - UINT32_C(1) << LZMA_CHECK_CRC64); + LZMA_INDEX_CHECK_MASK_CRC64); assert_lzma_ret(lzma_index_cat(idx, third, NULL), LZMA_OK); // Index should now have CRC64 and SHA256 assert_uint_eq(lzma_index_checks(idx), - (UINT32_C(1) << LZMA_CHECK_CRC64) | - (UINT32_C(1) << LZMA_CHECK_SHA256)); + LZMA_INDEX_CHECK_MASK_CRC64 | + LZMA_INDEX_CHECK_MASK_SHA256); lzma_index_end(idx, NULL); } @@ -467,15 +471,18 @@ test_lzma_index_stream_size(void) // Next, append a few Blocks and retest assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 999, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 997, 1), LZMA_OK); // Stream size should be: // Size of Stream Header - 12 bytes - // Size of all Blocks - 3000 bytes + // Size of all Blocks - 3000 bytes [*] // Size of Index - 16 bytes // Size of Stream Footer - 12 bytes // Total: 3040 bytes + // + // [*] Block size is a multiple of 4 bytes so 999 and 997 get + // rounded up to 1000 bytes. assert_uint_eq(lzma_index_stream_size(idx), 3040); lzma_index *second = lzma_index_init(NULL); @@ -520,10 +527,10 @@ test_lzma_index_total_size(void) assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); assert_uint_eq(lzma_index_total_size(idx), 1000); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 999, 1), LZMA_OK); assert_uint_eq(lzma_index_total_size(idx), 2000); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 997, 1), LZMA_OK); assert_uint_eq(lzma_index_total_size(idx), 3000); // Create second lzma_index and append Blocks to it. @@ -545,6 +552,16 @@ test_lzma_index_total_size(void) // from both Streams assert_uint_eq(lzma_index_total_size(idx), 3200); + // Test sizes that aren't multiples of four bytes + assert_lzma_ret(lzma_index_append(idx, NULL, 11, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 3212); + + assert_lzma_ret(lzma_index_append(idx, NULL, 11, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 3224); + + assert_lzma_ret(lzma_index_append(idx, NULL, 9, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 3236); + lzma_index_end(idx, NULL); } @@ -560,8 +577,8 @@ test_lzma_index_file_size(void) assert_uint_eq(lzma_index_file_size(idx), 32); assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); - assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 999, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 997, 1), LZMA_OK); assert_uint_eq(lzma_index_file_size(idx), 3040); @@ -690,6 +707,7 @@ test_lzma_index_iter_rewind(void) assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); assert_uint_eq(iter.block.number_in_file, i + 1); + assert_uint_eq(iter.block.number_in_stream, i + 1); } // Rewind back to the beginning and iterate over the Blocks again @@ -700,6 +718,7 @@ test_lzma_index_iter_rewind(void) assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); assert_uint_eq(iter.block.number_in_file, i + 1); + assert_uint_eq(iter.block.number_in_stream, i + 1); } // Next concatenate two more lzma_indexes, iterate over them, @@ -919,8 +938,9 @@ test_lzma_index_iter_next(void) // Verify both Blocks // Next call to iterate Block should return true because the - // first Block can already be read from the LZMA_INDEX_ITER_STREAM - // call. + // first Block can already be read from the earlier *successful* + // LZMA_INDEX_ITER_STREAM call; the previous failed call doesn't + // modify the iterator. assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); // Rewind to test LZMA_INDEX_ITER_ANY @@ -1046,13 +1066,14 @@ test_lzma_index_iter_locate(void) lzma_index_iter_init(&iter, idx); for (uint32_t n = 4; n <= 4 * 5555; n += 4) - assert_lzma_ret(lzma_index_append(idx, NULL, n + 8, n), + assert_lzma_ret(lzma_index_append(idx, NULL, n + 7, n), LZMA_OK); assert_uint_eq(lzma_index_block_count(idx), 5555); // First Record assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.unpadded_size, 4 + 7); assert_uint_eq(iter.block.total_size, 4 + 8); assert_uint_eq(iter.block.uncompressed_size, 4); assert_uint_eq(iter.block.compressed_file_offset, @@ -1060,6 +1081,7 @@ test_lzma_index_iter_locate(void) assert_uint_eq(iter.block.uncompressed_file_offset, 0); assert_false(lzma_index_iter_locate(&iter, 3)); + assert_uint_eq(iter.block.unpadded_size, 4 + 7); assert_uint_eq(iter.block.total_size, 4 + 8); assert_uint_eq(iter.block.uncompressed_size, 4); assert_uint_eq(iter.block.compressed_file_offset, @@ -1068,6 +1090,7 @@ test_lzma_index_iter_locate(void) // Second Record assert_false(lzma_index_iter_locate(&iter, 4)); + assert_uint_eq(iter.block.unpadded_size, 2 * 4 + 7); assert_uint_eq(iter.block.total_size, 2 * 4 + 8); assert_uint_eq(iter.block.uncompressed_size, 2 * 4); assert_uint_eq(iter.block.compressed_file_offset, @@ -1077,6 +1100,7 @@ test_lzma_index_iter_locate(void) // Last Record assert_false(lzma_index_iter_locate( &iter, lzma_index_uncompressed_size(idx) - 1)); + assert_uint_eq(iter.block.unpadded_size, 4 * 5555 + 7); assert_uint_eq(iter.block.total_size, 4 * 5555 + 8); assert_uint_eq(iter.block.uncompressed_size, 4 * 5555); assert_uint_eq(iter.block.compressed_file_offset, @@ -1142,6 +1166,7 @@ test_lzma_index_iter_locate(void) for (n = 0; n < group_multiple; ++n) assert_lzma_ret(lzma_index_append(idx, NULL, 8, 0), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 16, 1), LZMA_OK); assert_false(lzma_index_iter_locate(&iter, 0)); assert_uint_eq(iter.block.total_size, 16); @@ -1171,17 +1196,17 @@ test_lzma_index_cat(void) assert_lzma_ret(lzma_index_cat(dest, NULL, NULL), LZMA_PROG_ERROR); assert_lzma_ret(lzma_index_cat(NULL, src, NULL), LZMA_PROG_ERROR); - // Check for uncompressed size overflow + // Check for compressed size overflow assert_lzma_ret(lzma_index_append(dest, NULL, (UNPADDED_SIZE_MAX / 2) + 1, 1), LZMA_OK); assert_lzma_ret(lzma_index_append(src, NULL, (UNPADDED_SIZE_MAX / 2) + 1, 1), LZMA_OK); assert_lzma_ret(lzma_index_cat(dest, src, NULL), LZMA_DATA_ERROR); - // Check for compressed size overflow lzma_index_end(src, NULL); lzma_index_end(dest, NULL); + // Check for uncompressed size overflow dest = lzma_index_init(NULL); assert_true(dest != NULL); @@ -1189,9 +1214,9 @@ test_lzma_index_cat(void) assert_true(src != NULL); assert_lzma_ret(lzma_index_append(dest, NULL, - UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + UNPADDED_SIZE_MIN, (LZMA_VLI_MAX / 2) + 1), LZMA_OK); assert_lzma_ret(lzma_index_append(src, NULL, - UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + UNPADDED_SIZE_MIN, (LZMA_VLI_MAX / 2) + 1), LZMA_OK); assert_lzma_ret(lzma_index_cat(dest, src, NULL), LZMA_DATA_ERROR); lzma_index_end(dest, NULL); @@ -1263,13 +1288,17 @@ my_alloc(void *opaque, size_t a, size_t b) { (void)opaque; + assert_true(SIZE_MAX / a >= b); + static unsigned count = 0; - if (++count > 2) + if (count >= 2) return NULL; + ++count; return malloc(a * b); } + static const lzma_allocator test_index_dup_alloc = { &my_alloc, NULL, NULL }; @@ -1326,6 +1355,7 @@ test_lzma_index_dup(void) lzma_index_end(idx, NULL); } + #if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) static void verify_index_buffer(const lzma_index *idx, const uint8_t *buffer, @@ -1459,31 +1489,30 @@ test_lzma_index_encoder(void) #endif } + static void generate_index_decode_buffer(void) { #ifdef HAVE_ENCODERS decode_test_index = lzma_index_init(NULL); - if (decode_test_index == NULL) - return; + assert_true(decode_test_index != NULL); // Add 4 Blocks for (uint32_t i = 1; i < 5; i++) - if (lzma_index_append(decode_test_index, NULL, - 0x1000 * i, 0x100 * i) != LZMA_OK) - return; + assert_lzma_ret(lzma_index_append(decode_test_index, NULL, + 0x1000 * i, 0x100 * i), LZMA_OK); - size_t size = lzma_index_size(decode_test_index); + const size_t size = (size_t)lzma_index_size(decode_test_index); decode_buffer = tuktest_malloc(size); - if (lzma_index_buffer_encode(decode_test_index, - decode_buffer, &decode_buffer_size, size) != LZMA_OK) - decode_buffer_size = 0; + assert_lzma_ret(lzma_index_buffer_encode(decode_test_index, + decode_buffer, &decode_buffer_size, size), LZMA_OK); + assert_true(decode_buffer_size != 0); #endif } -#ifdef HAVE_DECODERS +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) static void decode_index(const uint8_t *buffer, const size_t size, lzma_stream *strm, lzma_ret expected_error) @@ -1498,11 +1527,10 @@ decode_index(const uint8_t *buffer, const size_t size, lzma_stream *strm, static void test_lzma_index_decoder(void) { -#ifndef HAVE_DECODERS - assert_skip("Decoder support disabled"); +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); #else - if (decode_buffer_size == 0) - assert_skip("Could not initialize decode test buffer"); + assert_true(decode_buffer_size != 0); lzma_stream strm = LZMA_STREAM_INIT; @@ -1510,11 +1538,21 @@ test_lzma_index_decoder(void) LZMA_PROG_ERROR); assert_lzma_ret(lzma_index_decoder(&strm, NULL, MEMLIMIT), LZMA_PROG_ERROR); - assert_lzma_ret(lzma_index_decoder(NULL, &decode_test_index, - MEMLIMIT), LZMA_PROG_ERROR); + + // If the first argument (lzma_stream *strm) is NULL then + // *idx must still become NULL since the API docs say that + // it's done if an error occurs. This was fixed in + // 71eed2520e2eecae89bade9dceea16e56cfa2ea0. + lzma_index *idx_allocated = lzma_index_init(NULL); + lzma_index *idx = idx_allocated; + assert_lzma_ret(lzma_index_decoder(NULL, &idx, MEMLIMIT), + LZMA_PROG_ERROR); + assert_true(idx == NULL); + + lzma_index_end(idx_allocated, NULL); + idx_allocated = NULL; // Do actual decode - lzma_index *idx; assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), LZMA_OK); @@ -1579,8 +1617,8 @@ test_lzma_index_buffer_encode(void) #if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) assert_skip("Encoder or decoder support disabled"); #else - // More simple test than test_lzma_index_encoder() because - // currently lzma_index_buffer_encode() is mostly a wrapper + // These are simpler test than in test_lzma_index_encoder() + // because lzma_index_buffer_encode() is mostly a wrapper // around lzma_index_encoder() anyway. lzma_index *idx = lzma_index_init(NULL); assert_true(idx != NULL); @@ -1607,6 +1645,9 @@ test_lzma_index_buffer_encode(void) 0), LZMA_PROG_ERROR); out_pos = 0; assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + 0), LZMA_BUF_ERROR); + assert_uint_eq(out_pos, 0); + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, 1), LZMA_BUF_ERROR); // Do encoding @@ -1625,11 +1666,10 @@ test_lzma_index_buffer_encode(void) static void test_lzma_index_buffer_decode(void) { -#ifndef HAVE_DECODERS - assert_skip("Decoder support disabled"); +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); #else - if (decode_buffer_size == 0) - assert_skip("Could not initialize decode test buffer"); + assert_true(decode_buffer_size != 0); // Simple test since test_lzma_index_decoder() covers most of the // lzma_index_buffer_decode() code anyway. @@ -1638,34 +1678,70 @@ test_lzma_index_buffer_decode(void) assert_lzma_ret(lzma_index_buffer_decode(NULL, NULL, NULL, NULL, NULL, 0), LZMA_PROG_ERROR); - lzma_index *idx; uint64_t memlimit = MEMLIMIT; size_t in_pos = 0; + lzma_index *idx_allocated = lzma_index_init(NULL); + lzma_index *idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, NULL, NULL, NULL, NULL, 0), LZMA_PROG_ERROR); + assert_true(idx == NULL); + idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, NULL, NULL, 0), LZMA_PROG_ERROR); + assert_true(idx == NULL); + idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, decode_buffer, NULL, 0), LZMA_PROG_ERROR); + assert_true(idx == NULL); + idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, decode_buffer, NULL, 0), LZMA_PROG_ERROR); + assert_true(idx == NULL); + idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, decode_buffer, &in_pos, 0), LZMA_DATA_ERROR); + assert_true(idx == NULL); in_pos = 1; + idx = idx_allocated; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, decode_buffer, &in_pos, 0), LZMA_PROG_ERROR); + assert_true(idx == NULL); + + // Test too short input in_pos = 0; + idx = idx_allocated; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size - 1), + LZMA_DATA_ERROR); + assert_true(idx == NULL); + + lzma_index_end(idx_allocated, NULL); + idx_allocated = NULL; // Test expected successful decode + in_pos = 0; assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, decode_buffer, &in_pos, decode_buffer_size), LZMA_OK); + assert_uint_eq(in_pos, decode_buffer_size); + assert_true(index_is_equal(decode_test_index, idx)); + + lzma_index_end(idx, NULL); + + // Test too much input. This won't read past + // the end of the allocated array (decode_buffer_size bytes). + in_pos = 0; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size + 16), + LZMA_OK); + + assert_uint_eq(in_pos, decode_buffer_size); assert_true(index_is_equal(decode_test_index, idx)); lzma_index_end(idx, NULL); diff --git a/tests/test_index_hash.c b/tests/test_index_hash.c index f3c6e8f..842d534 100644 --- a/tests/test_index_hash.c +++ b/tests/test_index_hash.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_index_hash.c @@ -9,9 +11,6 @@ // // Author: Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_lzip_decoder.c b/tests/test_lzip_decoder.c index 3743d43..cee97b8 100644 --- a/tests/test_lzip_decoder.c +++ b/tests/test_lzip_decoder.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_lzip_decoder.c @@ -5,9 +7,6 @@ // // Author: Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" @@ -43,11 +42,11 @@ basic_lzip_decode(const char *src, const uint32_t expected_crc) lzma_stream strm = LZMA_STREAM_INIT; assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0), LZMA_OK); - uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE); + uint8_t output_buffer[DECODE_CHUNK_SIZE]; strm.next_in = data; strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); // Feed 1 byte at a time to the decoder to look for any bugs // when switching between decoding sequences @@ -59,12 +58,8 @@ basic_lzip_decode(const char *src, const uint32_t expected_crc) checksum = lzma_crc32(output_buffer, (size_t)(strm.next_out - output_buffer), checksum); - // No need to free output_buffer because it will - // automatically be freed at the end of the test by - // tuktest. - output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE); strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); } } @@ -126,12 +121,12 @@ trailing_helper(const char *src, const uint32_t expected_data_checksum, assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED), LZMA_OK); - uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE); + uint8_t output_buffer[DECODE_CHUNK_SIZE]; strm.next_in = data; - strm.next_out = output_buffer; strm.avail_in = file_size; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); lzma_ret ret = LZMA_OK; while (ret == LZMA_OK) { @@ -140,12 +135,8 @@ trailing_helper(const char *src, const uint32_t expected_data_checksum, checksum = lzma_crc32(output_buffer, (size_t)(strm.next_out - output_buffer), checksum); - // No need to free output_buffer because it will - // automatically be freed at the end of the test by - // tuktest. - output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE); strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); } } @@ -182,20 +173,18 @@ decode_expect_error(const char *src, lzma_ret expected_error) strm.avail_in = file_size; strm.next_in = data; - strm.avail_out = DECODE_CHUNK_SIZE; - strm.next_out = output_buffer; lzma_ret ret = LZMA_OK; - while (ret == LZMA_OK) { + do { // Discard output since we are only looking for errors strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); if (strm.avail_in == 0) ret = lzma_code(&strm, LZMA_FINISH); else ret = lzma_code(&strm, LZMA_RUN); - } + } while (ret == LZMA_OK); assert_lzma_ret(ret, expected_error); lzma_end(&strm); @@ -233,26 +222,25 @@ test_v1_trailing(void) static void -test_concatentated(void) +test_concatenated(void) { // First test a file with one v0 member and one v1 member // The first member should contain "Hello\n" and // the second member should contain "World!\n" - lzma_stream strm = LZMA_STREAM_INIT; size_t file_size; uint8_t *v0_v1 = tuktest_file_from_srcdir("files/good-2-v0-v1.lz", - &file_size); + &file_size); assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED), LZMA_OK); uint8_t output_buffer[DECODE_CHUNK_SIZE]; - strm.avail_in = file_size; strm.next_in = v0_v1; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); @@ -263,15 +251,15 @@ test_concatentated(void) // The second file contains one v1 member and one v2 member uint8_t *v1_v0 = tuktest_file_from_srcdir("files/good-2-v1-v0.lz", - &file_size); + &file_size); assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED), LZMA_OK); - strm.avail_in = file_size; strm.next_in = v1_v0; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); @@ -281,15 +269,15 @@ test_concatentated(void) // The third file contains 2 v1 members uint8_t *v1_v1 = tuktest_file_from_srcdir("files/good-2-v1-v1.lz", - &file_size); + &file_size); assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED), LZMA_OK); - strm.avail_in = file_size; strm.next_in = v1_v1; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); @@ -315,10 +303,10 @@ test_crc(void) uint8_t output_buffer[DECODE_CHUNK_SIZE]; - strm.avail_in = file_size; strm.next_in = data; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR); @@ -326,10 +314,10 @@ test_crc(void) assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED | LZMA_IGNORE_CHECK), LZMA_OK); - strm.avail_in = file_size; strm.next_in = data; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); assert_uint_eq(strm.total_in, file_size); @@ -338,10 +326,10 @@ test_crc(void) assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, LZMA_CONCATENATED | LZMA_TELL_ANY_CHECK), LZMA_OK); - strm.avail_in = file_size; strm.next_in = data; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_in = file_size; strm.next_out = output_buffer; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_GET_CHECK); assert_uint_eq(lzma_get_check(&strm), LZMA_CHECK_CRC32); @@ -367,7 +355,7 @@ test_invalid_magic_bytes(void) strm.next_in = lzip_id_string; strm.avail_in = sizeof(lzip_id_string); strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_FORMAT_ERROR); @@ -393,10 +381,10 @@ test_invalid_version(void) static void test_invalid_dictionary_size(void) { - // First file has too small dictionary size field + // The first file has a too small dictionary size field. decode_expect_error("files/bad-1-v1-dict-1.lz", LZMA_DATA_ERROR); - // Second file has too large dictionary size field + // The second file has a too large dictionary size field. decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR); } @@ -426,7 +414,7 @@ static void test_invalid_memlimit(void) { // A very low memlimit should prevent decoding. - // Should be able to update the memlimit after failing + // It should be possible to update the memlimit after the error. size_t file_size; uint8_t *data = tuktest_file_from_srcdir("files/good-1-v1.lz", &file_size); @@ -440,12 +428,12 @@ test_invalid_memlimit(void) strm.next_in = data; strm.avail_in = file_size; strm.next_out = output_buffer; - strm.avail_out = DECODE_CHUNK_SIZE; + strm.avail_out = sizeof(output_buffer); assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR); - // Up the memlimit so decoding can continue. - // First only increase by a small amount and expect an error + // Up the memlimit so that decoding can continue. + // First only increase by a small amount and expect an error. assert_lzma_ret(lzma_memlimit_set(&strm, 100), LZMA_MEMLIMIT_ERROR); assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT), LZMA_OK); @@ -474,7 +462,7 @@ main(int argc, char **argv) tuktest_run(test_v1_decode); tuktest_run(test_v0_trailing); tuktest_run(test_v1_trailing); - tuktest_run(test_concatentated); + tuktest_run(test_concatenated); tuktest_run(test_crc); tuktest_run(test_invalid_magic_bytes); tuktest_run(test_invalid_version); @@ -484,5 +472,4 @@ main(int argc, char **argv) tuktest_run(test_invalid_memlimit); return tuktest_end(); #endif - } diff --git a/tests/test_memlimit.c b/tests/test_memlimit.c index c45a44b..7e8f862 100644 --- a/tests/test_memlimit.c +++ b/tests/test_memlimit.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_memlimit.c @@ -5,9 +7,6 @@ // // Author: Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_microlzma.c b/tests/test_microlzma.c new file mode 100644 index 0000000..c1d99d7 --- /dev/null +++ b/tests/test_microlzma.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: 0BSD + +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_microlzma.c +/// \brief Tests MicroLZMA encoding and decoding +// +// Author: Jia Tan +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + +#define BUFFER_SIZE 1024 + + +#ifdef HAVE_ENCODER_LZMA1 + +// MicroLZMA encoded "Hello\nWorld\n" output size in bytes. +#define ENCODED_OUTPUT_SIZE 17 + +// Byte array of "Hello\nWorld\n". This is used for various encoder tests. +static const uint8_t hello_world[] = { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, + 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x0A }; + +// This is the CRC32 value of the MicroLZMA encoding of "Hello\nWorld\n". +// The settings used were based on LZMA_PRESET_DEFAULT as of liblzma 5.6.0. +// This assumes MicroLZMA is correct in liblzma 5.6.0, which is safe +// considering the encoded "Hello\nWorld\n" can successfully be decoded at +// this time. This is to test for regressions that cause MicroLZMA output +// to change. +static const uint32_t hello_world_encoded_crc = 0x3CDE40A8; + + +// Function implementation borrowed from lzma_decoder.c. It is needed to +// ensure the first byte of a MicroLZMA stream is set correctly with the +// negation of the LZMA properties. +static bool +lzma_lzma_lclppb_decode(lzma_options_lzma *options, uint8_t byte) +{ + if (byte > (4 * 5 + 4) * 9 + 8) + return true; + + // See the file format specification to understand this. + options->pb = byte / (9 * 5); + byte -= options->pb * 9 * 5; + options->lp = byte / 9; + options->lc = byte - options->lp * 9; + + return options->lc + options->lp > LZMA_LCLP_MAX; +} + + +/////////////////// +// Encoder tests // +/////////////////// + +// This tests a few of the basic options. These options are not unique to +// MicroLZMA in any way, its mostly ensuring that the options are actually +// being checked before initializing the decoder internals. +static void +test_encode_options(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt_lzma; + + // Initialize with default options. + assert_false(lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)); + + // NULL stream + assert_lzma_ret(lzma_microlzma_encoder(NULL, &opt_lzma), + LZMA_PROG_ERROR); + + // lc/lp/pb = 5/0/2 (lc invalid) + opt_lzma.lc = 5; + opt_lzma.lp = 0; + opt_lzma.pb = 2; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + // lc/lp/pb = 0/5/2 (lp invalid) + opt_lzma.lc = 0; + opt_lzma.lp = 5; + opt_lzma.pb = 2; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + // lc/lp/pb = 3/2/2 (lc + lp invalid) + opt_lzma.lc = 3; + opt_lzma.lp = 2; + opt_lzma.pb = 2; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + // lc/lp/pb = 3/0/5 (pb invalid) + opt_lzma.lc = 3; + opt_lzma.lp = 0; + opt_lzma.pb = 5; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + // Zero out lp, pb, lc options to not interfere with later tests. + opt_lzma.lp = 0; + opt_lzma.pb = 0; + opt_lzma.lc = 0; + + // Set invalid dictionary size. + opt_lzma.dict_size = LZMA_DICT_SIZE_MIN - 1; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + // Maximum dictionary size for the encoder, as described in lzma12.h + // is 1.5 GiB. + opt_lzma.dict_size = (UINT32_C(1) << 30) + (UINT32_C(1) << 29) + 1; + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OPTIONS_ERROR); + + lzma_end(&strm); +} + + +static void +test_encode_basic(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt_lzma; + + // The lzma_lzma_preset return value is inverse of what it perhaps + // should be, that is, it returns false on success. + assert_false(lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)); + + // Initialize the encoder using the default options. + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = hello_world; + strm.avail_in = sizeof(hello_world); + strm.next_out = output; + strm.avail_out = sizeof(output); + + // Everything must be encoded in one lzma_code() call. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + // Check that the entire input was consumed. + assert_uint_eq(strm.total_in, sizeof(hello_world)); + + // Check that the first byte in the output stream is not 0x00. + // In a regular raw LZMA stream the first byte is always 0x00. + // In MicroLZMA the first byte replaced by the bitwise-negation + // of the LZMA properties. + assert_uint(output[0], !=, 0x00); + + const uint8_t props = ~output[0]; + + lzma_options_lzma test_options; + assert_false(lzma_lzma_lclppb_decode(&test_options, props)); + + assert_uint_eq(opt_lzma.lc, test_options.lc); + assert_uint_eq(opt_lzma.lp, test_options.lp); + assert_uint_eq(opt_lzma.pb, test_options.pb); + + // Compute the check over the output data. This is compared to + // the expected check value. + const uint32_t check_val = lzma_crc32(output, strm.total_out, 0); + + assert_uint_eq(check_val, hello_world_encoded_crc); + + lzma_end(&strm); +} + + +// This tests the behavior when strm.avail_out is so small it cannot hold +// the header plus 1 encoded byte (< 6). +static void +test_encode_small_out(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt_lzma; + + assert_false(lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)); + + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = hello_world; + strm.avail_in = sizeof(hello_world); + strm.next_out = output; + strm.avail_out = 5; + + // LZMA_PROG_ERROR is expected when strm.avail_out < 6 + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_PROG_ERROR); + + // The encoder must be reset because coders cannot be used again + // after returning LZMA_PROG_ERROR. + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), LZMA_OK); + + // Reset strm.avail_out to be > 6, but not enough to hold all of the + // compressed data. + strm.avail_out = ENCODED_OUTPUT_SIZE - 1; + + // Encoding should not return an error now. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint(strm.total_in, <, sizeof(hello_world)); + + lzma_end(&strm); +} + + +// LZMA_FINISH is the only supported action. All others must +// return LZMA_PROG_ERROR. +static void +test_encode_actions(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt_lzma; + + assert_false(lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)); + + const lzma_action actions[] = { + LZMA_RUN, + LZMA_SYNC_FLUSH, + LZMA_FULL_FLUSH, + LZMA_FULL_BARRIER, + }; + + for (size_t i = 0; i < ARRAY_SIZE(actions); ++i) { + assert_lzma_ret(lzma_microlzma_encoder(&strm, &opt_lzma), + LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = hello_world; + strm.avail_in = sizeof(hello_world); + strm.next_out = output; + strm.avail_out = sizeof(output); + + assert_lzma_ret(lzma_code(&strm, actions[i]), + LZMA_PROG_ERROR); + } + + lzma_end(&strm); +} +#endif // HAVE_ENCODER_LZMA1 + + +/////////////////// +// Decoder tests // +/////////////////// + +#if defined(HAVE_DECODER_LZMA1) && defined(HAVE_ENCODER_LZMA1) + +// Byte array of "Goodbye World!". This is used for various decoder tests. +static const uint8_t goodbye_world[] = { 0x47, 0x6F, 0x6F, 0x64, 0x62, + 0x79, 0x65, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21 }; + +static uint8_t *goodbye_world_encoded = NULL; +static size_t goodbye_world_encoded_size = 0; + + +// Helper function to encode data and return the compressed size. +static size_t +basic_microlzma_encode(const uint8_t *input, size_t in_size, + uint8_t **compressed) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt_lzma; + + // Lazy way to set the output size since the input should never + // inflate by much in these simple test cases. This is tested to + // be large enough after encoding to fit the entire input, so if + // this assumption does not hold then this will fail. + const size_t out_size = in_size << 1; + + *compressed = tuktest_malloc(out_size); + + // Always encode with the default options for simplicity. + if (lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)) + goto decoder_setup_error; + + if (lzma_microlzma_encoder(&strm, &opt_lzma) != LZMA_OK) + goto decoder_setup_error; + + strm.next_in = input; + strm.avail_in = in_size; + strm.next_out = *compressed; + strm.avail_out = out_size; + + if (lzma_code(&strm, LZMA_FINISH) != LZMA_STREAM_END) + goto decoder_setup_error; + + // Check that the entire input was consumed and that it fit into + // the output buffer. + if (strm.total_in != in_size) + goto decoder_setup_error; + + lzma_end(&strm); + + // lzma_end() doesn't touch other members of lzma_stream than + // lzma_stream.internal so using strm.total_out here is fine. + return strm.total_out; + +decoder_setup_error: + tuktest_error("Failed to initialize decoder tests"); + return 0; +} + + +static void +test_decode_options(void) +{ + // NULL stream + assert_lzma_ret(lzma_microlzma_decoder(NULL, BUFFER_SIZE, + sizeof(hello_world), true, + LZMA_DICT_SIZE_DEFAULT), LZMA_PROG_ERROR); + + // Uncompressed size larger than max + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_microlzma_decoder(&strm, BUFFER_SIZE, + LZMA_VLI_MAX + 1, true, LZMA_DICT_SIZE_DEFAULT), + LZMA_OPTIONS_ERROR); +} + + +// Test that decoding succeeds when uncomp_size is correct regardless of +// the value of uncomp_size_is_exact. +static void +test_decode_uncomp_size_is_exact(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + sizeof(goodbye_world), true, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + assert_uint_eq(strm.total_in, goodbye_world_encoded_size); + + assert_uint_eq(strm.total_out, sizeof(goodbye_world)); + assert_array_eq(goodbye_world, output, sizeof(goodbye_world)); + + // Reset decoder with uncomp_size_is_exact set to false and + // uncomp_size set to correct value. Also test using the + // uncompressed size as the dictionary size. + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + sizeof(goodbye_world), false, + sizeof(goodbye_world)), LZMA_OK); + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + assert_uint_eq(strm.total_in, goodbye_world_encoded_size); + + assert_uint_eq(strm.total_out, sizeof(goodbye_world)); + assert_array_eq(goodbye_world, output, sizeof(goodbye_world)); + + lzma_end(&strm); +} + + +// This tests decoding when MicroLZMA decoder is called with +// an incorrect uncompressed size. +static void +test_decode_uncomp_size_wrong(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + sizeof(goodbye_world) + 1, false, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + // LZMA_OK should be returned because the input size given was + // larger than the actual encoded size. The decoder is expecting + // more input to possibly fill the uncompressed size that was set. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_OK); + + assert_uint_eq(strm.total_out, sizeof(goodbye_world)); + + assert_array_eq(goodbye_world, output, sizeof(goodbye_world)); + + // Next, test with uncomp_size_is_exact set. + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + sizeof(goodbye_world) + 1, true, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + // No error detected, even though all input was consumed and there + // is more room in the output buffer. + // + // FIXME? LZMA_FINISH tells that no more input is coming and + // the MicroLZMA decoder knows the exact compressed size from + // the initialization as well. So should it return LZMA_DATA_ERROR + // on the first call instead of relying on the generic lzma_code() + // logic to eventually get LZMA_BUF_ERROR? + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_OK); + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_OK); + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_BUF_ERROR); + + assert_uint_eq(strm.total_out, sizeof(goodbye_world)); + assert_array_eq(goodbye_world, output, sizeof(goodbye_world)); + + // Reset stream with uncomp_size smaller than the real + // uncompressed size. + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + ARRAY_SIZE(hello_world) - 1, true, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + // This case actually results in an error since it decodes the full + // uncompressed size but the range coder is not in the proper state + // for the stream to end. + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_DATA_ERROR); + + lzma_end(&strm); +} + + +static void +test_decode_comp_size_wrong(void) +{ + lzma_stream strm = LZMA_STREAM_INIT; + + // goodbye_world_encoded_size + 1 is safe because extra space was + // allocated for goodbye_world_encoded. The extra space isn't + // initialized but it shouldn't be read either, thus Valgrind + // has to remain happy with this code. + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size + 1, + sizeof(goodbye_world), true, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + // When uncomp_size_is_exact is set, the compressed size must be + // correct or else LZMA_DATA_ERROR is returned. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR); + + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size + 1, + sizeof(goodbye_world), false, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + strm.next_in = goodbye_world_encoded; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + // When uncomp_size_is_exact is not set, the decoder does not + // detect when the compressed size is wrong as long as all of the + // expected output has been decoded. This is because the decoder + // assumes that the real uncompressed size might be bigger than + // the specified value and in that case more input might be needed + // as well. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + lzma_end(&strm); +} + + +static void +test_decode_bad_lzma_properties(void) +{ + // Alter first byte to encode invalid LZMA properties. + uint8_t *compressed = tuktest_malloc(goodbye_world_encoded_size); + memcpy(compressed, goodbye_world_encoded, goodbye_world_encoded_size); + + // lc=3, lp=2, pb=2 + compressed[0] = (uint8_t)~0x6FU; + + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + sizeof(goodbye_world), false, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + uint8_t output[BUFFER_SIZE]; + + strm.next_in = compressed; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_OPTIONS_ERROR); + + // Use valid, but incorrect LZMA properties. + // lc=3, lp=1, pb=2 + compressed[0] = (uint8_t)~0x66; + + assert_lzma_ret(lzma_microlzma_decoder(&strm, + goodbye_world_encoded_size, + ARRAY_SIZE(goodbye_world), true, + LZMA_DICT_SIZE_DEFAULT), LZMA_OK); + + strm.next_in = compressed; + strm.avail_in = goodbye_world_encoded_size; + strm.next_out = output; + strm.avail_out = sizeof(output); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_DATA_ERROR); + + lzma_end(&strm); +} +#endif + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + +#ifndef HAVE_ENCODER_LZMA1 + tuktest_early_skip("LZMA1 encoder disabled"); +#else + tuktest_run(test_encode_options); + tuktest_run(test_encode_basic); + tuktest_run(test_encode_small_out); + tuktest_run(test_encode_actions); + + // MicroLZMA decoder tests require the basic encoder functionality. +# ifdef HAVE_DECODER_LZMA1 + goodbye_world_encoded_size = basic_microlzma_encode(goodbye_world, + sizeof(goodbye_world), &goodbye_world_encoded); + + tuktest_run(test_decode_options); + tuktest_run(test_decode_uncomp_size_is_exact); + tuktest_run(test_decode_uncomp_size_wrong); + tuktest_run(test_decode_comp_size_wrong); + tuktest_run(test_decode_bad_lzma_properties); +# endif + + return tuktest_end(); +#endif +} diff --git a/tests/test_scripts.sh b/tests/test_scripts.sh index ee82361..ca9600e 100755 --- a/tests/test_scripts.sh +++ b/tests/test_scripts.sh @@ -1,18 +1,18 @@ #!/bin/sh +# SPDX-License-Identifier: 0BSD ############################################################################### # # Author: Jonathan Nieder # -# This file has been put into the public domain. -# You can do whatever you want with this file. -# ############################################################################### # If scripts weren't built, this test is skipped. -XZ=../src/xz/xz -XZDIFF=../src/scripts/xzdiff -XZGREP=../src/scripts/xzgrep +# When this is run from CMake, $1 is a relative path +# to the directory with the executables and the scripts. +XZ=${1:-../src/xz}/xz +XZDIFF=${1:-../src/scripts}/xzdiff +XZGREP=${1:-../src/scripts}/xzgrep for i in XZ XZDIFF XZGREP; do eval test -x "\$$i" && continue @@ -23,14 +23,15 @@ done # Installing the scripts in this case is a bit silly but they # could still be used with other decompression tools so configure # doesn't automatically disable scripts if decoders are disabled. -if grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then +if test ! -f ../config.h \ + || grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then : else echo "Decompression support is disabled, skipping this test." exit 77 fi -PATH=`pwd`/../src/xz:$PATH +PATH=`pwd`/${1:-../src/xz}:$PATH export PATH test -z "$srcdir" && srcdir=. diff --git a/tests/test_stream_flags.c b/tests/test_stream_flags.c index 2248e67..3bc48be 100644 --- a/tests/test_stream_flags.c +++ b/tests/test_stream_flags.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_stream_flags.c @@ -6,9 +8,6 @@ // Authors: Jia Tan // Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/test_suffix.sh b/tests/test_suffix.sh new file mode 100755 index 0000000..7fc5996 --- /dev/null +++ b/tests/test_suffix.sh @@ -0,0 +1,192 @@ +#!/bin/sh +# SPDX-License-Identifier: 0BSD + +############################################################################### +# +# Author: Jia Tan +# +############################################################################### + +# Optional argument: +# $1 = directory of the xz executable + +# If xz was not built, skip this test. Autotools and CMake put +# the xz executable in a different location. +XZ=${1:-../src/xz}/xz +if test ! -x "$XZ"; then + echo "xz was not built, skipping this test." + exit 77 +fi + +# If compression or decompression support is missing, this test is skipped. +# This isn't perfect because it does not specifically check for LZMA1/2 +# filters. Many of the other tests also assume LZMA1/2 support if encoders +# or decoders are enabled. +if test ! -f ../config.h ; then + : +elif grep 'define HAVE_ENCODERS' ../config.h > /dev/null \ + && grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then + : +else + echo "Compression or decompression support is disabled, skipping this test." + exit 77 +fi + +# Create temporary input file. The file contents are not important. +SUFFIX_INPUT="suffix_temp" +SUFFIX_INPUT_FILES="$SUFFIX_INPUT"_files +SUFFIX_INPUT_FILES0="$SUFFIX_INPUT"_files0 + +# Remove possible leftover temporary files +rm -f \ + "$SUFFIX_INPUT" \ + "$SUFFIX_INPUT.foo" \ + "$SUFFIX_INPUT_FILES" \ + "$SUFFIX_INPUT_FILES" + +echo "foobar" > "$SUFFIX_INPUT" + +# Test basic suffix when compressing with raw format. +if "$XZ" -zfk --suffix=".foo" -Fraw --lzma1=preset=0 "$SUFFIX_INPUT" ; then + : +else + echo "Failed to compress a file with a suffix set in raw format" + exit 1 +fi + +# Test the output file is named properly. +if test -f "$SUFFIX_INPUT.foo" ; then + : +else + echo "Raw format compressed output file not named properly" + exit 1 +fi + +# Expect an error when compressing with raw format without a suffix +if "$XZ" -zfk -Fraw --lzma1=preset=0 "$SUFFIX_INPUT" 2> /dev/null; then + echo "Error not reported when compressing in raw format without a suffix" + exit 1 +fi + +# Expect an error when decompressing with raw format without a suffix +if "$XZ" -df -Fraw --lzma1=preset=0 "$SUFFIX_INPUT.foo" 2> /dev/null; then + echo "Error not reported when decompressing in raw format without a suffix" + exit 1 +fi + +# Test basic decompression with raw format and a suffix. This will also +# delete $SUFFIX_INPUT.foo +if "$XZ" -df --suffix=".foo" -Fraw --lzma1=preset=0 "$SUFFIX_INPUT.foo"; then + : +else + echo "Failed to decompress a file with a suffix set in raw format" + exit 1 +fi + +# Test basic compression with .xz format and a suffix +if "$XZ" -zfk --suffix=".foo" --lzma2=preset=0 "$SUFFIX_INPUT" ; then + : +else + echo "Failed to compress a file with a suffix set in .xz format" + exit 1 +fi + +# Test the output file is named properly. +if test -f "$SUFFIX_INPUT.foo" ; then + : +else + echo ".xz format compressed output file named properly" + exit 1 +fi + +# This will delete $SUFFIX_INPUT.foo +if "$XZ" -df --suffix=".foo" "$SUFFIX_INPUT.foo"; then + : +else + echo "Failed to decompress a file with a suffix set in .xz format" + exit 1 +fi + +# Test reading from stdin in raw mode. This was broken in +# cc5aa9ab138beeecaee5a1e81197591893ee9ca0 and fixed in +# 837ea40b1c9d4998cac4500b55171bf33e0c31a6 +if echo foo | "$XZ" -Fraw --lzma1=preset=0 > /dev/null ; then + : +else + echo "Implicit write to stdout not detected" + exit 1 +fi + +# Create two temporary files to be used with --files and --files0. +printf "$SUFFIX_INPUT\n" > "$SUFFIX_INPUT_FILES" +printf "$SUFFIX_INPUT\0" > "$SUFFIX_INPUT_FILES0" + +# Test proper handling of --files/--files0 when no suffix is set. This +# must result in an error because xz does not know how to rename the output +# file from the input files. This caused a segmentation fault due to a +# mistake in f481523baac946fa3bc13d79186ffaf0c0b818a7, which was fixed by +# 0a601ddc89fd7e1370807c6b58964f361dfcd34a. +if "$XZ" -Fraw --lzma1=preset=0 --files="$SUFFIX_INPUT_FILES" 2> /dev/null ; then + echo "Failed to report error when compressing a file specified by --files in raw mode without a suffix" + exit 1 +fi + +if "$XZ" -Fraw --lzma1=preset=0 --files0="$SUFFIX_INPUT_FILES0" 2> /dev/null ; then + echo "Failed to report error when compressing a file specified by --files0 in raw mode without a suffix" + exit 1 +fi + +# Test proper suffix usage in raw mode with --files and --files0. +if "$XZ" -zfk -Fraw --lzma1=preset=0 --suffix=.foo --files="$SUFFIX_INPUT_FILES" ; then + : +else + echo "Error compressing a file specified by --files in raw mode with a suffix set" + exit 1 +fi + +if test -f "$SUFFIX_INPUT.foo" ; then + : +else + echo "Entry processed by --files not named properly" + exit 1 +fi + +# Remove the artifact so we can be sure the next test executes properly. +rm "$SUFFIX_INPUT.foo" + +if "$XZ" -zfk -Fraw --lzma1=preset=0 --suffix=.foo --files0="$SUFFIX_INPUT_FILES0" ; then + : +else + echo "Error compressing a file specified by --files0 in raw mode with a suffix set" + exit 1 +fi + +if test -f "$SUFFIX_INPUT.foo" ; then + : +else + echo "Entry processed by --files0 not named properly" + exit 1 +fi + +# When the file type cannot be determined by xz, it will copy the contents +# of the file only if -c,--stdout is used. This was broken by +# 837ea40b1c9d4998cac4500b55171bf33e0c31a6 and fixed by +# f481523baac946fa3bc13d79186ffaf0c0b818a7. +if echo foo | "$XZ" -df > /dev/null 2>&1; then + echo "Failed to report error when decompressing unknown file type without -c,--stdout" + exit 1 +fi + +if echo foo | "$XZ" -dfc > /dev/null; then + : +else + echo "Failed to copy input to standard out when decompressing unknown file type with -c,--stdout" + exit 1 +fi + +# Remove remaining test artifacts +rm -f \ + "$SUFFIX_INPUT" \ + "$SUFFIX_INPUT.foo" \ + "$SUFFIX_INPUT_FILES" \ + "$SUFFIX_INPUT_FILES0" diff --git a/tests/test_vli.c b/tests/test_vli.c index 996b775..51487dc 100644 --- a/tests/test_vli.c +++ b/tests/test_vli.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file test_vli.c @@ -5,9 +7,6 @@ // // Author: Jia Tan // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #include "tests.h" diff --git a/tests/tests.cmake b/tests/tests.cmake new file mode 100644 index 0000000..62c546c --- /dev/null +++ b/tests/tests.cmake @@ -0,0 +1,198 @@ +# SPDX-License-Identifier: 0BSD + +############################################################################# +# +# Optional file to be included by the top-level CMakeLists.txt to run tests +# +# The CMake rules for the tests are in this separate optional file so +# that it's trivial to just delete the whole "tests" directory and still +# get an otherwise normal CMake-based build. This way it's easy to ensure +# that nothing in the "tests" directory can affect the build process. +# +# Author: Lasse Collin +# +############################################################################# + +include(CTest) + +if(BUILD_TESTING) + ################# + # liblzma tests # + ################# + + set(LIBLZMA_TESTS + test_bcj_exact_size + test_block_header + test_check + test_filter_flags + test_filter_str + test_hardware + test_index + test_index_hash + test_lzip_decoder + test_memlimit + test_stream_flags + test_vli + ) + + # MicroLZMA encoder is needed for both encoder and decoder tests. + # If MicroLZMA decoder is not configured but LZMA1 decoder is, then + # test_microlzma will fail to compile because this configuration is + # not possible in the Autotools build, so the test was not made to + # support it since it would have required additional changes. + if (MICROLZMA_ENCODER AND (MICROLZMA_DECODER + OR NOT "lzma1" IN_LIST DECODERS)) + list(APPEND LIBLZMA_TESTS test_microlzma) + endif() + + foreach(TEST IN LISTS LIBLZMA_TESTS) + add_executable("${TEST}" "tests/${TEST}.c") + + target_include_directories("${TEST}" PRIVATE + src/common + src/liblzma/api + src/liblzma + ) + + target_link_libraries("${TEST}" PRIVATE liblzma) + + # Put the test programs into their own subdirectory so they don't + # pollute the top-level dir which might contain xz and xzdec. + set_target_properties("${TEST}" PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests_bin" + ) + + add_test(NAME "${TEST}" + COMMAND "${CMAKE_CURRENT_BINARY_DIR}/tests_bin/${TEST}" + ) + + # Set srcdir environment variable so that the tests find their + # input files from the source tree. + # + # Set the return code for skipped tests to match Automake convention. + set_tests_properties("${TEST}" PROPERTIES + ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests" + SKIP_RETURN_CODE 77 + ) + endforeach() + + + ########################### + # Command line tool tests # + ########################### + + # Since the CMake-based build doesn't use config.h, the test scripts + # cannot grep the contents of config.h to know which features have + # been disabled. When config.h is missing, they assume that all + # features are enabled. Thus, check if certain groups of features have + # been disabled and then possibly skip some of the tests entirely instead + # of letting them fail. + set(SUPPORTED_FILTERS_SORTED "${SUPPORTED_FILTERS}") + list(SORT SUPPORTED_FILTERS_SORTED) + + set(ENCODERS_SORTED "${ENCODERS}") + list(SORT ENCODERS_SORTED) + + if("${ENCODERS_SORTED}" STREQUAL "${SUPPORTED_FILTERS_SORTED}") + set(HAVE_ALL_ENCODERS ON) + else() + set(HAVE_ALL_ENCODERS OFF) + endif() + + set(DECODERS_SORTED "${DECODERS}") + list(SORT DECODERS_SORTED) + + if("${DECODERS_SORTED}" STREQUAL "${SUPPORTED_FILTERS_SORTED}") + set(HAVE_ALL_DECODERS ON) + else() + set(HAVE_ALL_DECODERS OFF) + endif() + + set(ADDITIONAL_SUPPORTED_CHECKS_SORTED "${ADDITIONAL_SUPPORTED_CHECKS}") + list(SORT ADDITIONAL_SUPPORTED_CHECKS_SORTED) + + set(ADDITIONAL_CHECK_TYPES_SORTED "${ADDITIONAL_CHECK_TYPES}") + list(SORT ADDITIONAL_CHECK_TYPES_SORTED) + + if("${ADDITIONAL_SUPPORTED_CHECKS_SORTED}" STREQUAL + "${ADDITIONAL_CHECK_TYPES_SORTED}") + set(HAVE_ALL_CHECK_TYPES ON) + else() + set(HAVE_ALL_CHECK_TYPES OFF) + endif() + + # test_scripts.sh only needs LZMA2 decoder and CRC32. + if(UNIX AND HAVE_DECODERS) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_scripts") + + add_test(NAME test_scripts.sh + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_scripts.sh" ".." + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_scripts" + ) + + set_tests_properties(test_scripts.sh PROPERTIES + ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests" + SKIP_RETURN_CODE 77 + ) + endif() + + # test_suffix.sh only needs LZMA2 encoder and decoder. + if(UNIX AND HAVE_ENCODERS AND HAVE_DECODERS) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_suffix") + + add_test(NAME test_suffix.sh + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_suffix.sh" ".." + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_suffix" + ) + + set_tests_properties(test_suffix.sh PROPERTIES + SKIP_RETURN_CODE 77 + ) + endif() + + # The test_compress.sh based tests compress and decompress using different + # filters so run it only if all encoders and decoders have been enabled. + if(UNIX AND HAVE_ALL_ENCODERS AND HAVE_ALL_DECODERS) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_compress") + + add_executable(create_compress_files tests/create_compress_files.c) + target_include_directories(create_compress_files PRIVATE src/common) + set_target_properties(create_compress_files PROPERTIES + RUNTIME_OUTPUT_DIRECTORY test_compress) + + foreach(T compress_generated_abc + compress_generated_text + compress_generated_random) + add_test(NAME "test_${T}" + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_compress.sh" + "${T}" ".." + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test_compress" + ) + + set_tests_properties("test_${T}" PROPERTIES + ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests" + SKIP_RETURN_CODE 77 + ) + endforeach() + endif() + + # test_files.sh decompresses files that use different filters and + # check types so run it only if support for all of them has been enabled. + if(UNIX AND HAVE_ALL_DECODERS AND HAVE_ALL_CHECK_TYPES AND LZIP_DECODER) + # test_files.sh doesn't make any temporary files but it + # must not be run at the top-level build directory because + # it checks if ../config.h exists. We don't want to read + # files outside the build directory! + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/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" + ) + + set_tests_properties(test_files.sh PROPERTIES + ENVIRONMENT "srcdir=${CMAKE_CURRENT_SOURCE_DIR}/tests" + SKIP_RETURN_CODE 77 + ) + endif() +endif() diff --git a/tests/tests.h b/tests/tests.h index 2d42700..f706cab 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file tests.h @@ -5,9 +7,6 @@ // // Author: Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #ifndef LZMA_TESTS_H diff --git a/tests/tuktest.h b/tests/tuktest.h index 508eace..12abd53 100644 --- a/tests/tuktest.h +++ b/tests/tuktest.h @@ -1,10 +1,13 @@ +// SPDX-License-Identifier: 0BSD + /////////////////////////////////////////////////////////////////////////////// // /// \file tuktest.h /// \brief Helper macros for writing simple test programs -/// \version 2023-01-08 +/// \version 2024-02-14 /// -/// Some inspiration was taken from STest by Keith Nicholas. +/// Some inspiration was taken from Seatest by Keith Nicholas and +/// from STest which is a fork of Seatest by Jia Tan. /// /// This is standard C99/C11 only and thus should be fairly portable /// outside POSIX systems too. @@ -110,8 +113,8 @@ /// Meson: https://mesonbuild.com/Unit-tests.html /// /// CMake handles passing and failing tests by default but treats hard -/// errors as regular fails. To CMake support skipped tests correctly, -/// one has to set the SKIP_RETURN_CODE property for each test: +/// errors as regular fails. To make CMake support skipped tests +/// correctly, one has to set the SKIP_RETURN_CODE property for each test: /// /// set_tests_properties(foo_test_name PROPERTIES SKIP_RETURN_CODE 77) /// @@ -121,9 +124,6 @@ // // Author: Lasse Collin // -// This file has been put into the public domain. -// You can do whatever you want with this file. -// /////////////////////////////////////////////////////////////////////////////// #ifndef TUKTEST_H @@ -155,14 +155,14 @@ // This is silencing warnings about unused functions. Not all test programs // need all functions from this header. -#if TUKTEST_GNUC_REQ(3, 0) +#if TUKTEST_GNUC_REQ(3, 0) || defined(__clang__) # define tuktest_maybe_unused __attribute__((__unused__)) #else # define tuktest_maybe_unused #endif // We need printf("") so silence the warning about empty format string. -#if TUKTEST_GNUC_REQ(4, 2) +#if TUKTEST_GNUC_REQ(4, 2) || defined(__clang__) # pragma GCC diagnostic ignored "-Wformat-zero-length" #endif |