diff options
Diffstat (limited to 'tests')
131 files changed, 9534 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..ebc33a7 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,76 @@ +## +## 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 \ + 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 \ + xzgrep_expected_output + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/common \ + -I$(top_srcdir)/src/liblzma/api \ + -I$(top_srcdir)/src/liblzma + +LDADD = $(top_builddir)/src/liblzma/liblzma.la + +LDADD += $(LTLIBINTL) + +check_PROGRAMS = \ + create_compress_files \ + test_check \ + test_hardware \ + test_stream_flags \ + test_filter_flags \ + test_filter_str \ + test_block_header \ + test_index \ + test_index_hash \ + test_bcj_exact_size \ + test_memlimit \ + test_lzip_decoder \ + test_vli + +TESTS = \ + test_check \ + test_hardware \ + test_stream_flags \ + test_filter_flags \ + test_filter_str \ + test_block_header \ + test_index \ + test_index_hash \ + test_bcj_exact_size \ + test_memlimit \ + test_lzip_decoder \ + test_vli \ + test_files.sh \ + test_compress_prepared_bcj_sparc \ + test_compress_prepared_bcj_x86 \ + test_compress_generated_abc \ + test_compress_generated_random \ + test_compress_generated_text + +if COND_SCRIPTS +TESTS += test_scripts.sh +endif + +clean-local: + -rm -f compress_generated_* \ + xzgrep_test_output xzgrep_test_1.xz xzgrep_test_2.xz diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..46cf0f0 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,1349 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +check_PROGRAMS = create_compress_files$(EXEEXT) 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) +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_compress_generated_random test_compress_generated_text \ + $(am__append_1) +@COND_SCRIPTS_TRUE@am__append_1 = 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 \ + $(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 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/posix-shell.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/tuklib_common.m4 \ + $(top_srcdir)/m4/tuklib_cpucores.m4 \ + $(top_srcdir)/m4/tuklib_integer.m4 \ + $(top_srcdir)/m4/tuklib_mbstr.m4 \ + $(top_srcdir)/m4/tuklib_physmem.m4 \ + $(top_srcdir)/m4/tuklib_progname.m4 \ + $(top_srcdir)/m4/visibility.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +create_compress_files_SOURCES = create_compress_files.c +create_compress_files_OBJECTS = create_compress_files.$(OBJEXT) +create_compress_files_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +create_compress_files_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +test_bcj_exact_size_SOURCES = test_bcj_exact_size.c +test_bcj_exact_size_OBJECTS = test_bcj_exact_size.$(OBJEXT) +test_bcj_exact_size_LDADD = $(LDADD) +test_bcj_exact_size_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +test_block_header_SOURCES = test_block_header.c +test_block_header_OBJECTS = test_block_header.$(OBJEXT) +test_block_header_LDADD = $(LDADD) +test_block_header_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +test_check_SOURCES = test_check.c +test_check_OBJECTS = test_check.$(OBJEXT) +test_check_LDADD = $(LDADD) +test_check_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +test_filter_flags_SOURCES = test_filter_flags.c +test_filter_flags_OBJECTS = test_filter_flags.$(OBJEXT) +test_filter_flags_LDADD = $(LDADD) +test_filter_flags_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +test_filter_str_SOURCES = test_filter_str.c +test_filter_str_OBJECTS = test_filter_str.$(OBJEXT) +test_filter_str_LDADD = $(LDADD) +test_filter_str_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +test_hardware_SOURCES = test_hardware.c +test_hardware_OBJECTS = test_hardware.$(OBJEXT) +test_hardware_LDADD = $(LDADD) +test_hardware_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +test_index_SOURCES = test_index.c +test_index_OBJECTS = test_index.$(OBJEXT) +test_index_LDADD = $(LDADD) +test_index_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +test_index_hash_SOURCES = test_index_hash.c +test_index_hash_OBJECTS = test_index_hash.$(OBJEXT) +test_index_hash_LDADD = $(LDADD) +test_index_hash_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +test_lzip_decoder_SOURCES = test_lzip_decoder.c +test_lzip_decoder_OBJECTS = test_lzip_decoder.$(OBJEXT) +test_lzip_decoder_LDADD = $(LDADD) +test_lzip_decoder_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +test_memlimit_SOURCES = test_memlimit.c +test_memlimit_OBJECTS = test_memlimit.$(OBJEXT) +test_memlimit_LDADD = $(LDADD) +test_memlimit_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) +test_stream_flags_DEPENDENCIES = \ + $(top_builddir)/src/liblzma/liblzma.la $(am__DEPENDENCIES_1) +test_vli_SOURCES = test_vli.c +test_vli_OBJECTS = test_vli.$(OBJEXT) +test_vli_LDADD = $(LDADD) +test_vli_DEPENDENCIES = $(top_builddir)/src/liblzma/liblzma.la \ + $(am__DEPENDENCIES_1) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/create_compress_files.Po \ + ./$(DEPDIR)/test_bcj_exact_size.Po \ + ./$(DEPDIR)/test_block_header.Po ./$(DEPDIR)/test_check.Po \ + ./$(DEPDIR)/test_filter_flags.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 +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +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 +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 +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_CFLAGS = @AM_CFLAGS@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CAPSICUM_LIB = @CAPSICUM_LIB@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GETOPT_H = @GETOPT_H@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_EXEEXT = @LN_EXEEXT@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +MSGMERGE_FOR_MSGFMT_OPTION = @MSGMERGE_FOR_MSGFMT_OPTION@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSIX_SHELL = @POSIX_SHELL@ +POSUB = @POSUB@ +PREFERABLY_POSIX_SHELL = @PREFERABLY_POSIX_SHELL@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_CXX = @PTHREAD_CXX@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +RC = @RC@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_path_for_scripts = @enable_path_for_scripts@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +xz = @xz@ +EXTRA_DIST = \ + files \ + ossfuzz \ + 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 \ + xzgrep_expected_output + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/common \ + -I$(top_srcdir)/src/liblzma/api \ + -I$(top_srcdir)/src/liblzma + +LDADD = $(top_builddir)/src/liblzma/liblzma.la $(LTLIBINTL) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +create_compress_files$(EXEEXT): $(create_compress_files_OBJECTS) $(create_compress_files_DEPENDENCIES) $(EXTRA_create_compress_files_DEPENDENCIES) + @rm -f create_compress_files$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(create_compress_files_OBJECTS) $(create_compress_files_LDADD) $(LIBS) + +test_bcj_exact_size$(EXEEXT): $(test_bcj_exact_size_OBJECTS) $(test_bcj_exact_size_DEPENDENCIES) $(EXTRA_test_bcj_exact_size_DEPENDENCIES) + @rm -f test_bcj_exact_size$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_bcj_exact_size_OBJECTS) $(test_bcj_exact_size_LDADD) $(LIBS) + +test_block_header$(EXEEXT): $(test_block_header_OBJECTS) $(test_block_header_DEPENDENCIES) $(EXTRA_test_block_header_DEPENDENCIES) + @rm -f test_block_header$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_block_header_OBJECTS) $(test_block_header_LDADD) $(LIBS) + +test_check$(EXEEXT): $(test_check_OBJECTS) $(test_check_DEPENDENCIES) $(EXTRA_test_check_DEPENDENCIES) + @rm -f test_check$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_check_OBJECTS) $(test_check_LDADD) $(LIBS) + +test_filter_flags$(EXEEXT): $(test_filter_flags_OBJECTS) $(test_filter_flags_DEPENDENCIES) $(EXTRA_test_filter_flags_DEPENDENCIES) + @rm -f test_filter_flags$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_filter_flags_OBJECTS) $(test_filter_flags_LDADD) $(LIBS) + +test_filter_str$(EXEEXT): $(test_filter_str_OBJECTS) $(test_filter_str_DEPENDENCIES) $(EXTRA_test_filter_str_DEPENDENCIES) + @rm -f test_filter_str$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_filter_str_OBJECTS) $(test_filter_str_LDADD) $(LIBS) + +test_hardware$(EXEEXT): $(test_hardware_OBJECTS) $(test_hardware_DEPENDENCIES) $(EXTRA_test_hardware_DEPENDENCIES) + @rm -f test_hardware$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_hardware_OBJECTS) $(test_hardware_LDADD) $(LIBS) + +test_index$(EXEEXT): $(test_index_OBJECTS) $(test_index_DEPENDENCIES) $(EXTRA_test_index_DEPENDENCIES) + @rm -f test_index$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_index_OBJECTS) $(test_index_LDADD) $(LIBS) + +test_index_hash$(EXEEXT): $(test_index_hash_OBJECTS) $(test_index_hash_DEPENDENCIES) $(EXTRA_test_index_hash_DEPENDENCIES) + @rm -f test_index_hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_index_hash_OBJECTS) $(test_index_hash_LDADD) $(LIBS) + +test_lzip_decoder$(EXEEXT): $(test_lzip_decoder_OBJECTS) $(test_lzip_decoder_DEPENDENCIES) $(EXTRA_test_lzip_decoder_DEPENDENCIES) + @rm -f test_lzip_decoder$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_lzip_decoder_OBJECTS) $(test_lzip_decoder_LDADD) $(LIBS) + +test_memlimit$(EXEEXT): $(test_memlimit_OBJECTS) $(test_memlimit_DEPENDENCIES) $(EXTRA_test_memlimit_DEPENDENCIES) + @rm -f test_memlimit$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_memlimit_OBJECTS) $(test_memlimit_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) + +test_vli$(EXEEXT): $(test_vli_OBJECTS) $(test_vli_DEPENDENCIES) $(EXTRA_test_vli_DEPENDENCIES) + @rm -f test_vli$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_vli_OBJECTS) $(test_vli_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_compress_files.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bcj_exact_size.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_block_header.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_check.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_filter_flags.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_filter_str.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hardware.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_index.Po@am__quote@ # am--include-marker +@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_stream_flags.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_vli.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +test_check.log: test_check$(EXEEXT) + @p='test_check$(EXEEXT)'; \ + b='test_check'; \ + $(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_hardware.log: test_hardware$(EXEEXT) + @p='test_hardware$(EXEEXT)'; \ + b='test_hardware'; \ + $(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_stream_flags.log: test_stream_flags$(EXEEXT) + @p='test_stream_flags$(EXEEXT)'; \ + b='test_stream_flags'; \ + $(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_filter_flags.log: test_filter_flags$(EXEEXT) + @p='test_filter_flags$(EXEEXT)'; \ + b='test_filter_flags'; \ + $(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_filter_str.log: test_filter_str$(EXEEXT) + @p='test_filter_str$(EXEEXT)'; \ + b='test_filter_str'; \ + $(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_block_header.log: test_block_header$(EXEEXT) + @p='test_block_header$(EXEEXT)'; \ + b='test_block_header'; \ + $(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_index.log: test_index$(EXEEXT) + @p='test_index$(EXEEXT)'; \ + b='test_index'; \ + $(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_index_hash.log: test_index_hash$(EXEEXT) + @p='test_index_hash$(EXEEXT)'; \ + b='test_index_hash'; \ + $(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_bcj_exact_size.log: test_bcj_exact_size$(EXEEXT) + @p='test_bcj_exact_size$(EXEEXT)'; \ + b='test_bcj_exact_size'; \ + $(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_memlimit.log: test_memlimit$(EXEEXT) + @p='test_memlimit$(EXEEXT)'; \ + b='test_memlimit'; \ + $(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_lzip_decoder.log: test_lzip_decoder$(EXEEXT) + @p='test_lzip_decoder$(EXEEXT)'; \ + b='test_lzip_decoder'; \ + $(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_vli.log: test_vli$(EXEEXT) + @p='test_vli$(EXEEXT)'; \ + b='test_vli'; \ + $(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_files.sh.log: test_files.sh + @p='test_files.sh'; \ + b='test_files.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) \ + "$$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'; \ + $(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_generated_abc.log: test_compress_generated_abc + @p='test_compress_generated_abc'; \ + b='test_compress_generated_abc'; \ + $(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_generated_random.log: test_compress_generated_random + @p='test_compress_generated_random'; \ + b='test_compress_generated_random'; \ + $(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_generated_text.log: test_compress_generated_text + @p='test_compress_generated_text'; \ + b='test_compress_generated_text'; \ + $(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'; \ + $(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.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool clean-local \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/create_compress_files.Po + -rm -f ./$(DEPDIR)/test_bcj_exact_size.Po + -rm -f ./$(DEPDIR)/test_block_header.Po + -rm -f ./$(DEPDIR)/test_check.Po + -rm -f ./$(DEPDIR)/test_filter_flags.Po + -rm -f ./$(DEPDIR)/test_filter_str.Po + -rm -f ./$(DEPDIR)/test_hardware.Po + -rm -f ./$(DEPDIR)/test_index.Po + -rm -f ./$(DEPDIR)/test_index_hash.Po + -rm -f ./$(DEPDIR)/test_lzip_decoder.Po + -rm -f ./$(DEPDIR)/test_memlimit.Po + -rm -f ./$(DEPDIR)/test_stream_flags.Po + -rm -f ./$(DEPDIR)/test_vli.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/create_compress_files.Po + -rm -f ./$(DEPDIR)/test_bcj_exact_size.Po + -rm -f ./$(DEPDIR)/test_block_header.Po + -rm -f ./$(DEPDIR)/test_check.Po + -rm -f ./$(DEPDIR)/test_filter_flags.Po + -rm -f ./$(DEPDIR)/test_filter_str.Po + -rm -f ./$(DEPDIR)/test_hardware.Po + -rm -f ./$(DEPDIR)/test_index.Po + -rm -f ./$(DEPDIR)/test_index_hash.Po + -rm -f ./$(DEPDIR)/test_lzip_decoder.Po + -rm -f ./$(DEPDIR)/test_memlimit.Po + -rm -f ./$(DEPDIR)/test_stream_flags.Po + -rm -f ./$(DEPDIR)/test_vli.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ + clean-local cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am recheck tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +clean-local: + -rm -f compress_generated_* \ + xzgrep_test_output xzgrep_test_1.xz xzgrep_test_2.xz + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/bcj_test.c b/tests/bcj_test.c new file mode 100644 index 0000000..05de38a --- /dev/null +++ b/tests/bcj_test.c @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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 differnew file mode 100644 index 0000000..86ea7dd --- /dev/null +++ b/tests/compress_prepared_bcj_sparc diff --git a/tests/compress_prepared_bcj_x86 b/tests/compress_prepared_bcj_x86 Binary files differnew file mode 100644 index 0000000..bcc546f --- /dev/null +++ b/tests/compress_prepared_bcj_x86 diff --git a/tests/create_compress_files.c b/tests/create_compress_files.c new file mode 100644 index 0000000..76aa3e3 --- /dev/null +++ b/tests/create_compress_files.c @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file create_compress_files.c +/// \brief Creates bunch of test files to be compressed +/// +/// Using a test file generator program saves space in the source code +/// package considerably. +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "sysdefs.h" +#include <stdio.h> + + +// If a command-line argument was given, only create the file if its +// name was specified on the command line. If no args were given then +// all files are created. +// +// Avoid re-creating the test files every time the tests are run. +#define maybe_create_test(argc, argv, name) \ +do { \ + if ((argc < 2 || strcmp(argv[1], "compress_generated_" #name) == 0) \ + && !file_exists("compress_generated_" #name)) { \ + FILE *file = file_create("compress_generated_" #name); \ + write_ ## name(file); \ + file_finish(file, "compress_generated_" #name); \ + } \ +} while (0) + + +static bool +file_exists(const char *filename) +{ + // Trying to be somewhat portable by avoiding stat(). + FILE *file = fopen(filename, "rb"); + bool ret; + + if (file != NULL) { + fclose(file); + ret = true; + } else { + ret = false; + } + + return ret; +} + + +static FILE * +file_create(const char *filename) +{ + FILE *file = fopen(filename, "wb"); + + if (file == NULL) { + perror(filename); + exit(EXIT_FAILURE); + } + + return file; +} + + +static void +file_finish(FILE *file, const char *filename) +{ + const bool ferror_fail = ferror(file); + const bool fclose_fail = fclose(file); + + if (ferror_fail || fclose_fail) { + perror(filename); + exit(EXIT_FAILURE); + } +} + + +// File that repeats "abc\n" a few thousand times. This is targeted +// especially at Subblock filter's run-length encoder. +static void +write_abc(FILE *file) +{ + for (size_t i = 0; i < 12345; ++i) + if (fwrite("abc\n", 4, 1, file) != 1) + exit(EXIT_FAILURE); +} + + +// File that doesn't compress. We always use the same random seed to +// generate identical files on all systems. +static void +write_random(FILE *file) +{ + uint32_t n = 5; + + for (size_t i = 0; i < 123456; ++i) { + n = 101771 * n + 71777; + + putc((uint8_t)(n), file); + putc((uint8_t)(n >> 8), file); + putc((uint8_t)(n >> 16), file); + putc((uint8_t)(n >> 24), file); + } +} + + +// Text file +static void +write_text(FILE *file) +{ + static const char *lorem[] = { + "Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", + "adipisicing", "elit,", "sed", "do", "eiusmod", "tempor", + "incididunt", "ut", "labore", "et", "dolore", "magna", + "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", + "nostrud", "exercitation", "ullamco", "laboris", "nisi", + "ut", "aliquip", "ex", "ea", "commodo", "consequat.", + "Duis", "aute", "irure", "dolor", "in", "reprehenderit", + "in", "voluptate", "velit", "esse", "cillum", "dolore", + "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", + "occaecat", "cupidatat", "non", "proident,", "sunt", "in", + "culpa", "qui", "officia", "deserunt", "mollit", "anim", + "id", "est", "laborum." + }; + + // Let the first paragraph be the original text. + for (size_t w = 0; w < ARRAY_SIZE(lorem); ++w) { + fprintf(file, "%s ", lorem[w]); + + if (w % 7 == 6) + fprintf(file, "\n"); + } + + // The rest shall be (hopefully) meaningless combinations of + // the same words. + uint32_t n = 29; + + for (size_t p = 0; p < 500; ++p) { + fprintf(file, "\n\n"); + + for (size_t w = 0; w < ARRAY_SIZE(lorem); ++w) { + n = 101771 * n + 71777; + + fprintf(file, "%s ", lorem[n % ARRAY_SIZE(lorem)]); + + if (w % 7 == 6) + fprintf(file, "\n"); + } + } +} + + +int +main(int argc, char **argv) +{ + maybe_create_test(argc, argv, abc); + maybe_create_test(argc, argv, random); + maybe_create_test(argc, argv, text); + return EXIT_SUCCESS; +} diff --git a/tests/files/README b/tests/files/README new file mode 100644 index 0000000..119cac4 --- /dev/null +++ b/tests/files/README @@ -0,0 +1,409 @@ + +.xz and .lzma Test Files +------------------------ + +0. Introduction + + This directory contains bunch of files to test handling of .xz, + .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. + + +1. File Types + + Good files (good-*) must decode successfully without requiring + a lot of CPU time or RAM. + + Unsupported files (unsupported-*) are good files, but headers + indicate features not supported by the current file format + specification. + + Bad files (bad-*) must cause the decoder to give an error. Like + with the good files, these files must not require a lot of CPU + time or RAM before they get detected to be broken. + + +2. Descriptions of Individual .xz Files + +2.1. Good Files + + good-0-empty.xz has one Stream with no Blocks. + + good-0pad-empty.xz has one Stream with no Blocks followed by + four-byte Stream Padding. + + good-0cat-empty.xz has two zero-Block Streams concatenated without + Stream Padding. + + good-0catpad-empty.xz has two zero-Block Streams concatenated with + four-byte Stream Padding between the Streams. + + good-1-check-none.xz has one Stream with one Block with two + uncompressed LZMA2 chunks and no integrity check. + + good-1-check-crc32.xz has one Stream with one Block with two + uncompressed LZMA2 chunks and CRC32 check. + + good-1-check-crc64.xz is like good-1-check-crc32.xz but with CRC64. + + good-1-check-sha256.xz is like good-1-check-crc32.xz but with + SHA256. + + good-2-lzma2.xz has one Stream with two Blocks with one uncompressed + LZMA2 chunk in each Block. + + good-1-block_header-1.xz has both Compressed Size and Uncompressed + Size in the Block Header. This has also four extra bytes of Header + Padding. + + good-1-block_header-2.xz has known Compressed Size. + + good-1-block_header-3.xz has known Uncompressed Size. + + 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. + + 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. + + good-1-lzma2-1.xz has two LZMA2 chunks, of which the second sets + new properties. + + good-1-lzma2-2.xz has two LZMA2 chunks, of which the second resets + the state without specifying new properties. + + good-1-lzma2-3.xz has two LZMA2 chunks, of which the first is + uncompressed and the second is LZMA. The first chunk resets dictionary + and the second sets new properties. + + good-1-lzma2-4.xz has three LZMA2 chunks: First is LZMA, second is + uncompressed with dictionary reset, and third is LZMA with new + properties but without dictionary reset. + + good-1-lzma2-5.xz has an empty LZMA2 stream with only the end of + payload marker. XZ Utils 5.0.1 and older incorrectly see this file + as corrupt. + + good-1-3delta-lzma2.xz has three Delta filters and LZMA2. + + good-1-empty-bcj-lzma2.xz has an empty Block that uses PowerPC BCJ + and LZMA2. liblzma from XZ Utils 5.0.1 and older may incorrectly + return LZMA_BUF_ERROR in some cases. See commit message + d8db706acb8316f9861abd432cfbe001dd6d0c5c for the details. + + +2.2. Unsupported Files + + unsupported-check.xz uses Check ID 0x02 which isn't supported by + the current version of the file format. It is implementation-defined + how this file handled (it may reject it, or decode it possibly with + a warning). + + unsupported-block_header.xz has a non-null byte in Header Padding, + which may indicate presence of a new unsupported field. + + unsupported-filter_flags-1.xz has unsupported Filter ID 0x7F. + + unsupported-filter_flags-2.xz specifies only Delta filter in the + List of Filter Flags, but Delta isn't allowed as the last filter in + the chain. It could be a little more correct to detect this file as + corrupt instead of unsupported, but saying it is unsupported is + simpler in case of liblzma. + + unsupported-filter_flags-3.xz specifies two LZMA2 filters in the + List of Filter Flags. LZMA2 is allowed only as the last filter in the + chain. It could be a little more correct to detect this file as + corrupt instead of unsupported, but saying it is unsupported is + simpler in case of liblzma. + + +2.3. Bad Files + + bad-0pad-empty.xz has one Stream with no Blocks followed by + five-byte Stream Padding. Stream Padding must be a multiple of four + bytes, thus this file is corrupt. + + bad-0catpad-empty.xz has two zero-Block Streams concatenated with + five-byte Stream Padding between the Streams. + + bad-0cat-alone.xz is good-0-empty.xz concatenated with an empty + LZMA_Alone file. + + bad-0cat-header_magic.xz is good-0cat-empty.xz but with one byte + wrong in the Header Magic Bytes field of the second Stream. liblzma + gives LZMA_DATA_ERROR for this. (LZMA_FORMAT_ERROR is used only if + the first Stream of a file has invalid Header Magic Bytes.) + + bad-0-header_magic.xz is good-0-empty.xz but with one byte wrong + in the Header Magic Bytes field. liblzma gives LZMA_FORMAT_ERROR for + this. + + bad-0-footer_magic.xz is good-0-empty.xz but with one byte wrong + in the Footer Magic Bytes field. liblzma gives LZMA_DATA_ERROR for + this. + + bad-0-empty-truncated.xz is good-0-empty.xz without the last byte + of the file. + + bad-0-nonempty_index.xz has no Blocks but Index claims that there is + one Block. + + bad-0-backward_size.xz has wrong Backward Size in Stream Footer. + + bad-1-stream_flags-1.xz has different Stream Flags in Stream Header + and Stream Footer. + + bad-1-stream_flags-2.xz has wrong CRC32 in Stream Header. + + bad-1-stream_flags-3.xz has wrong CRC32 in Stream Footer. + + bad-1-vli-1.xz has two-byte variable-length integer in the + Uncompressed Size field in Block Header while one-byte would be enough + for that value. It's important that the file gets rejected due to too + big integer encoding instead of due to Uncompressed Size not matching + the value stored in the Block Header. That is, the decoder must not + try to decode the Compressed Data field. + + bad-1-vli-2.xz has ten-byte variable-length integer as Uncompressed + Size in Block Header. It's important that the file gets rejected due + to too big integer encoding instead of due to Uncompressed Size not + matching the value stored in the Block Header. That is, the decoder + must not try to decode the Compressed Data field. + + bad-1-block_header-1.xz has Block Header that ends in the middle of + the Filter Flags field. + + bad-1-block_header-2.xz has Block Header that has Compressed Size and + Uncompressed Size but no List of Filter Flags field. + + bad-1-block_header-3.xz has wrong CRC32 in Block Header. + + bad-1-block_header-4.xz has too big Compressed Size in Block Header + (2^63 - 1 bytes while maximum is a little less, because the whole + Block must stay smaller than 2^63). It's important that the file + gets rejected due to invalid Compressed Size value; the decoder + must not try decoding the Compressed Data field. + + bad-1-block_header-5.xz has zero as Compressed Size in Block Header. + + bad-1-block_header-6.xz has corrupt Block Header which may crash + xz -lvv in XZ Utils 5.0.3 and earlier. It was fixed in the commit + c0297445064951807803457dca1611b3c47e7f0f. + + bad-2-index-1.xz has wrong Unpadded Sizes in Index. + + bad-2-index-2.xz has wrong Uncompressed Sizes in Index. + + bad-2-index-3.xz has non-null byte in Index Padding. + + bad-2-index-4.xz wrong CRC32 in Index. + + bad-2-index-5.xz has zero as Unpadded Size. It is important that the + file gets rejected specifically due to Unpadded Size having an invalid + value. + + bad-3-index-uncomp-overflow.xz has Index whose Uncompressed Size + fields have huge values whose sum exceeds the maximum allowed size + of 2^63 - 1 bytes. In this file the sum is exactly 2^64. + lzma_index_append() in liblzma <= 5.2.6 lacks the integer overflow + check for the uncompressed size and thus doesn't catch the error + when decoding the Index field in this file. This makes "xz -l" + not detect the error and will display 0 as the uncompressed size. + Note that regular decompression isn't affected by this bug because + it uses lzma_index_hash_append() instead. + + bad-2-compressed_data_padding.xz has non-null byte in the padding of + the Compressed Data field of the first Block. + + bad-1-check-crc32.xz has wrong Check (CRC32). + + bad-1-check-crc32-2.xz has Compressed Size and Uncompressed Size in + Block Header but wrong Check (CRC32) in the actual data. This file + differs by one byte from good-1-block_header-1.xz: the last byte of + the Check field is wrong. This file is useful for testing error + detection in the threaded decoder when a worker thread is configured + to pass input one byte at a time to the Block decoder. + + bad-1-check-crc64.xz has wrong Check (CRC64). + + bad-1-check-sha256.xz has wrong Check (SHA-256). + + bad-1-lzma2-1.xz has LZMA2 stream whose first chunk (uncompressed) + doesn't reset the dictionary. + + bad-1-lzma2-2.xz has two LZMA2 chunks, of which the second chunk + indicates dictionary reset, but the LZMA compressed data tries to + repeat data from the previous chunk. + + bad-1-lzma2-3.xz sets new invalid properties (lc=8, lp=0, pb=0) in + the middle of Block. + + bad-1-lzma2-4.xz has two LZMA2 chunks, of which the first is + uncompressed and the second is LZMA. The first chunk resets dictionary + as it should, but the second chunk tries to reset state without + specifying properties for LZMA. + + bad-1-lzma2-5.xz is like bad-1-lzma2-4.xz but doesn't try to reset + anything in the header of the second chunk. + + bad-1-lzma2-6.xz has reserved LZMA2 control byte value (0x03). + + bad-1-lzma2-7.xz has EOPM at LZMA level. + + bad-1-lzma2-8.xz is like good-1-lzma2-4.xz but doesn't set new + properties in the third LZMA2 chunk. + + bad-1-lzma2-9.xz has LZMA2 stream that is truncated at the end of + a LZMA2 chunk (no end marker). The uncompressed size of the partial + LZMA2 stream exceeds the value stored in the Block Header. + + bad-1-lzma2-10.xz has LZMA2 stream that, from point of view of a + LZMA2 decoder, extends past the end of Block (and even the end of + the file). Uncompressed Size in Block Header is bigger than the + invalid LZMA2 stream may produce (even if a decoder reads until + the end of the file). The Check type is None to nullify certain + simple size-based sanity checks in a Block decoder. + + bad-1-lzma2-11.xz has LZMA2 stream that lacks the end of + payload marker. When Compressed Size bytes have been decoded, + Uncompressed Size bytes of output will have been produced but + the LZMA2 decoder doesn't indicate end of stream. + + +3. Descriptions of Individual .lzma Files + +3.1. Good Files + + good-unknown_size-with_eopm.lzma has unknown size in the header + and end of payload marker at the end. + + good-known_size-without_eopm.lzma has a known size in the header + and no end of payload marker at the end. + + good-known_size-with_eopm.lzma has a known size in the header + and end of payload marker at the end. XZ Utils 5.2.5 and older + will give an error at the end of the file after producing the + correct uncompressed output. + + +3.2. Bad Files + + bad-unknown_size-without_eopm.lzma has unknown size in the header + but no end of payload marker at the end. This file might be seen + by a decoder as if it were truncated. + + bad-too_big_size-with_eopm.lzma has too big uncompressed size in + the header and the end of payload marker will be detected before + the specified number of bytes have been decoded. + + bad-too_small_size-without_eopm-1.lzma has too small uncompressed + size in the header. The decoder will look for end of payload marker + but instead find a literal that would produce more output. + + bad-too_small_size-without_eopm-2.lzma is like -1 above but instead + of a literal the problem occurs with a short repeated match. + + bad-too_small_size-without_eopm-3.lzma is like -1 above but instead + of a literal the problem occurs in the middle of a match. + + +4. Descriptions of Individual .lz (lzip) Files + +4.1. Good Files + + good-1-v0.lz contains a single version 0 member. lzip 1.17 and + *older* can decompress this; support for version 0 was removed + in lzip 1.18. + + good-1-v0-trailing-1.lz is like good-1-v0.lz but contains + trailing data that the decompressor must ignore. + + good-1-v1.lz contains a single version 1 member. lzip 1.3 and + newer can decompress this. + + good-1-v1-trailing-1.lz is like good-1-v1.lz but contains + trailing data that the decompressor must ignore. + + good-1-v1-trailing-2.lz is like good-1-v1.lz but contains + trailing data whose first three bytes match the .lz magic bytes. + With lzip >= 1.20 this file results in an error unless one uses + the command line option --loose-trailing. lzip 1.3 to 1.19 decode + this file successfully by default. XZ Utils uses the old behavior + because it allows lzma_code() to stop at the first byte of the + trailing data as long as the first byte isn't 0x4C (L in US-ASCII); + otherwise the first 1-3 bytes that equal to the magic bytes are + consumed and lost in lzma_code(), and this is visible in xz too: + + $ ( xz -dc ; cat ) < good-1-v1-trailing-2.lz + Hello + World! + Trailing garbage + + $ ( xz -dc --single-stream ; cat ) < good-1-v1-trailing-2.lz + Hello + World! + LZITrailing garbage + + good-2-v0-v1.lz contains two members of which the first is + version 0 and the second version 1. lzip versions 1.3 to 1.17 + (inclusive) can decompress this. + + good-2-v1-v0.lz contains two members of which the first is + version 1 and the second version 0. lzip versions 1.3 to 1.17 + (inclusive) can decompress this. + + good-2-v1-v1.lz contains two version 1 members. lzip versions 1.3 + and newer can decompress this. + + +4.2. Unsupported Files + + unsupported-1-v234.lz is like good-1-v1.lz except the version + field has been set to 234 (0xEA) which, as of writing, isn't + defined or supported by any .lz implementation. + + +4.3. Bad Files + + bad-1-v1-magic-1.lz is like good-1-v1.lz but the first magic byte + is wrong. + + bad-1-v1-magic-2.lz is like good-1-v1.lz but the last (fourth) + magic byte is wrong. + + bad-1-v1-dict-1.lz has too low value in the dictionary size field. + + bad-1-v1-dict-2.lz has too high value in the dictionary size field. + + bad-1-v1-crc32.lz has wrong CRC32 value. + + bad-1-v0-uncomp-size.lz is version 0 format with incorrect value + in the uncompressed size field. + + bad-1-v1-uncomp-size.lz is version 1 format with incorrect value + in the uncompressed size field. + + bad-1-v1-member-size.lz has incorrect value in the member size + field. + + bad-1-v1-trailing-magic.lz has the four .lz magic bytes as trailing + data. This should be detected as a truncated file and thus result + in an error. That is, the last four bytes of the file should not be + ignored as trailing garbage. lzip >= 1.18 matches this behavior + while older versions ignore the last four bytes and don't indicate + an error. + diff --git a/tests/files/bad-0-backward_size.xz b/tests/files/bad-0-backward_size.xz Binary files differnew file mode 100644 index 0000000..2b46fa9 --- /dev/null +++ b/tests/files/bad-0-backward_size.xz diff --git a/tests/files/bad-0-empty-truncated.xz b/tests/files/bad-0-empty-truncated.xz Binary files differnew file mode 100644 index 0000000..f879af8 --- /dev/null +++ b/tests/files/bad-0-empty-truncated.xz diff --git a/tests/files/bad-0-footer_magic.xz b/tests/files/bad-0-footer_magic.xz Binary files differnew file mode 100644 index 0000000..5d9e389 --- /dev/null +++ b/tests/files/bad-0-footer_magic.xz diff --git a/tests/files/bad-0-header_magic.xz b/tests/files/bad-0-header_magic.xz Binary files differnew file mode 100644 index 0000000..5984a45 --- /dev/null +++ b/tests/files/bad-0-header_magic.xz diff --git a/tests/files/bad-0-nonempty_index.xz b/tests/files/bad-0-nonempty_index.xz Binary files differnew file mode 100644 index 0000000..ed6e81f --- /dev/null +++ b/tests/files/bad-0-nonempty_index.xz diff --git a/tests/files/bad-0cat-alone.xz b/tests/files/bad-0cat-alone.xz Binary files differnew file mode 100644 index 0000000..a915a3a --- /dev/null +++ b/tests/files/bad-0cat-alone.xz diff --git a/tests/files/bad-0cat-header_magic.xz b/tests/files/bad-0cat-header_magic.xz Binary files differnew file mode 100644 index 0000000..426bf2d --- /dev/null +++ b/tests/files/bad-0cat-header_magic.xz diff --git a/tests/files/bad-0catpad-empty.xz b/tests/files/bad-0catpad-empty.xz Binary files differnew file mode 100644 index 0000000..97c1330 --- /dev/null +++ b/tests/files/bad-0catpad-empty.xz diff --git a/tests/files/bad-0pad-empty.xz b/tests/files/bad-0pad-empty.xz Binary files differnew file mode 100644 index 0000000..45e00b7 --- /dev/null +++ b/tests/files/bad-0pad-empty.xz diff --git a/tests/files/bad-1-block_header-1.xz b/tests/files/bad-1-block_header-1.xz Binary files differnew file mode 100644 index 0000000..d991536 --- /dev/null +++ b/tests/files/bad-1-block_header-1.xz diff --git a/tests/files/bad-1-block_header-2.xz b/tests/files/bad-1-block_header-2.xz Binary files differnew file mode 100644 index 0000000..ae42ecf --- /dev/null +++ b/tests/files/bad-1-block_header-2.xz diff --git a/tests/files/bad-1-block_header-3.xz b/tests/files/bad-1-block_header-3.xz Binary files differnew file mode 100644 index 0000000..606cbd2 --- /dev/null +++ b/tests/files/bad-1-block_header-3.xz diff --git a/tests/files/bad-1-block_header-4.xz b/tests/files/bad-1-block_header-4.xz Binary files differnew file mode 100644 index 0000000..e72dfbf --- /dev/null +++ b/tests/files/bad-1-block_header-4.xz diff --git a/tests/files/bad-1-block_header-5.xz b/tests/files/bad-1-block_header-5.xz Binary files differnew file mode 100644 index 0000000..9652112 --- /dev/null +++ b/tests/files/bad-1-block_header-5.xz diff --git a/tests/files/bad-1-block_header-6.xz b/tests/files/bad-1-block_header-6.xz Binary files differnew file mode 100644 index 0000000..ecf0ff0 --- /dev/null +++ b/tests/files/bad-1-block_header-6.xz diff --git a/tests/files/bad-1-check-crc32-2.xz b/tests/files/bad-1-check-crc32-2.xz Binary files differnew file mode 100644 index 0000000..95731ae --- /dev/null +++ b/tests/files/bad-1-check-crc32-2.xz diff --git a/tests/files/bad-1-check-crc32.xz b/tests/files/bad-1-check-crc32.xz Binary files differnew file mode 100644 index 0000000..1ebe131 --- /dev/null +++ b/tests/files/bad-1-check-crc32.xz diff --git a/tests/files/bad-1-check-crc64.xz b/tests/files/bad-1-check-crc64.xz Binary files differnew file mode 100644 index 0000000..cdb7709 --- /dev/null +++ b/tests/files/bad-1-check-crc64.xz diff --git a/tests/files/bad-1-check-sha256.xz b/tests/files/bad-1-check-sha256.xz Binary files differnew file mode 100644 index 0000000..def7bff --- /dev/null +++ b/tests/files/bad-1-check-sha256.xz diff --git a/tests/files/bad-1-lzma2-1.xz b/tests/files/bad-1-lzma2-1.xz Binary files differnew file mode 100644 index 0000000..640f592 --- /dev/null +++ b/tests/files/bad-1-lzma2-1.xz diff --git a/tests/files/bad-1-lzma2-10.xz b/tests/files/bad-1-lzma2-10.xz Binary files differnew file mode 100644 index 0000000..246515e --- /dev/null +++ b/tests/files/bad-1-lzma2-10.xz diff --git a/tests/files/bad-1-lzma2-11.xz b/tests/files/bad-1-lzma2-11.xz Binary files differnew file mode 100644 index 0000000..ce857b6 --- /dev/null +++ b/tests/files/bad-1-lzma2-11.xz diff --git a/tests/files/bad-1-lzma2-2.xz b/tests/files/bad-1-lzma2-2.xz Binary files differnew file mode 100644 index 0000000..69ab07d --- /dev/null +++ b/tests/files/bad-1-lzma2-2.xz diff --git a/tests/files/bad-1-lzma2-3.xz b/tests/files/bad-1-lzma2-3.xz Binary files differnew file mode 100644 index 0000000..66f48c5 --- /dev/null +++ b/tests/files/bad-1-lzma2-3.xz diff --git a/tests/files/bad-1-lzma2-4.xz b/tests/files/bad-1-lzma2-4.xz Binary files differnew file mode 100644 index 0000000..ac97041 --- /dev/null +++ b/tests/files/bad-1-lzma2-4.xz diff --git a/tests/files/bad-1-lzma2-5.xz b/tests/files/bad-1-lzma2-5.xz Binary files differnew file mode 100644 index 0000000..700464d --- /dev/null +++ b/tests/files/bad-1-lzma2-5.xz diff --git a/tests/files/bad-1-lzma2-6.xz b/tests/files/bad-1-lzma2-6.xz Binary files differnew file mode 100644 index 0000000..2bda0c4 --- /dev/null +++ b/tests/files/bad-1-lzma2-6.xz diff --git a/tests/files/bad-1-lzma2-7.xz b/tests/files/bad-1-lzma2-7.xz Binary files differnew file mode 100644 index 0000000..8cc711c --- /dev/null +++ b/tests/files/bad-1-lzma2-7.xz diff --git a/tests/files/bad-1-lzma2-8.xz b/tests/files/bad-1-lzma2-8.xz Binary files differnew file mode 100644 index 0000000..f21a71b --- /dev/null +++ b/tests/files/bad-1-lzma2-8.xz diff --git a/tests/files/bad-1-lzma2-9.xz b/tests/files/bad-1-lzma2-9.xz Binary files differnew file mode 100644 index 0000000..0553905 --- /dev/null +++ b/tests/files/bad-1-lzma2-9.xz diff --git a/tests/files/bad-1-stream_flags-1.xz b/tests/files/bad-1-stream_flags-1.xz Binary files differnew file mode 100644 index 0000000..6511773 --- /dev/null +++ b/tests/files/bad-1-stream_flags-1.xz diff --git a/tests/files/bad-1-stream_flags-2.xz b/tests/files/bad-1-stream_flags-2.xz Binary files differnew file mode 100644 index 0000000..0c66b36 --- /dev/null +++ b/tests/files/bad-1-stream_flags-2.xz diff --git a/tests/files/bad-1-stream_flags-3.xz b/tests/files/bad-1-stream_flags-3.xz Binary files differnew file mode 100644 index 0000000..a9b1f98 --- /dev/null +++ b/tests/files/bad-1-stream_flags-3.xz diff --git a/tests/files/bad-1-v0-uncomp-size.lz b/tests/files/bad-1-v0-uncomp-size.lz Binary files differnew file mode 100644 index 0000000..6bf4c6c --- /dev/null +++ b/tests/files/bad-1-v0-uncomp-size.lz diff --git a/tests/files/bad-1-v1-crc32.lz b/tests/files/bad-1-v1-crc32.lz Binary files differnew file mode 100644 index 0000000..3387618 --- /dev/null +++ b/tests/files/bad-1-v1-crc32.lz diff --git a/tests/files/bad-1-v1-dict-1.lz b/tests/files/bad-1-v1-dict-1.lz Binary files differnew file mode 100644 index 0000000..20768d5 --- /dev/null +++ b/tests/files/bad-1-v1-dict-1.lz diff --git a/tests/files/bad-1-v1-dict-2.lz b/tests/files/bad-1-v1-dict-2.lz Binary files differnew file mode 100644 index 0000000..1f22e6d --- /dev/null +++ b/tests/files/bad-1-v1-dict-2.lz diff --git a/tests/files/bad-1-v1-magic-1.lz b/tests/files/bad-1-v1-magic-1.lz Binary files differnew file mode 100644 index 0000000..b5e374d --- /dev/null +++ b/tests/files/bad-1-v1-magic-1.lz diff --git a/tests/files/bad-1-v1-magic-2.lz b/tests/files/bad-1-v1-magic-2.lz Binary files differnew file mode 100644 index 0000000..f5d5b97 --- /dev/null +++ b/tests/files/bad-1-v1-magic-2.lz diff --git a/tests/files/bad-1-v1-member-size.lz b/tests/files/bad-1-v1-member-size.lz Binary files differnew file mode 100644 index 0000000..fd8636a --- /dev/null +++ b/tests/files/bad-1-v1-member-size.lz diff --git a/tests/files/bad-1-v1-trailing-magic.lz b/tests/files/bad-1-v1-trailing-magic.lz Binary files differnew file mode 100644 index 0000000..f7926c5 --- /dev/null +++ b/tests/files/bad-1-v1-trailing-magic.lz diff --git a/tests/files/bad-1-v1-uncomp-size.lz b/tests/files/bad-1-v1-uncomp-size.lz Binary files differnew file mode 100644 index 0000000..c89a283 --- /dev/null +++ b/tests/files/bad-1-v1-uncomp-size.lz diff --git a/tests/files/bad-1-vli-1.xz b/tests/files/bad-1-vli-1.xz Binary files differnew file mode 100644 index 0000000..6514ab1 --- /dev/null +++ b/tests/files/bad-1-vli-1.xz diff --git a/tests/files/bad-1-vli-2.xz b/tests/files/bad-1-vli-2.xz Binary files differnew file mode 100644 index 0000000..c16941b --- /dev/null +++ b/tests/files/bad-1-vli-2.xz diff --git a/tests/files/bad-2-compressed_data_padding.xz b/tests/files/bad-2-compressed_data_padding.xz Binary files differnew file mode 100644 index 0000000..382d047 --- /dev/null +++ b/tests/files/bad-2-compressed_data_padding.xz diff --git a/tests/files/bad-2-index-1.xz b/tests/files/bad-2-index-1.xz Binary files differnew file mode 100644 index 0000000..f51ed21 --- /dev/null +++ b/tests/files/bad-2-index-1.xz diff --git a/tests/files/bad-2-index-2.xz b/tests/files/bad-2-index-2.xz Binary files differnew file mode 100644 index 0000000..d7d00ff --- /dev/null +++ b/tests/files/bad-2-index-2.xz diff --git a/tests/files/bad-2-index-3.xz b/tests/files/bad-2-index-3.xz Binary files differnew file mode 100644 index 0000000..62428b8 --- /dev/null +++ b/tests/files/bad-2-index-3.xz diff --git a/tests/files/bad-2-index-4.xz b/tests/files/bad-2-index-4.xz Binary files differnew file mode 100644 index 0000000..9cf2df6 --- /dev/null +++ b/tests/files/bad-2-index-4.xz diff --git a/tests/files/bad-2-index-5.xz b/tests/files/bad-2-index-5.xz Binary files differnew file mode 100644 index 0000000..0a79270 --- /dev/null +++ b/tests/files/bad-2-index-5.xz diff --git a/tests/files/bad-3-index-uncomp-overflow.xz b/tests/files/bad-3-index-uncomp-overflow.xz Binary files differnew file mode 100644 index 0000000..e1440ec --- /dev/null +++ b/tests/files/bad-3-index-uncomp-overflow.xz diff --git a/tests/files/bad-too_big_size-with_eopm.lzma b/tests/files/bad-too_big_size-with_eopm.lzma Binary files differnew file mode 100644 index 0000000..b7cd3b0 --- /dev/null +++ b/tests/files/bad-too_big_size-with_eopm.lzma diff --git a/tests/files/bad-too_small_size-without_eopm-1.lzma b/tests/files/bad-too_small_size-without_eopm-1.lzma Binary files differnew file mode 100644 index 0000000..cc2805c --- /dev/null +++ b/tests/files/bad-too_small_size-without_eopm-1.lzma diff --git a/tests/files/bad-too_small_size-without_eopm-2.lzma b/tests/files/bad-too_small_size-without_eopm-2.lzma Binary files differnew file mode 100644 index 0000000..e37cab1 --- /dev/null +++ b/tests/files/bad-too_small_size-without_eopm-2.lzma diff --git a/tests/files/bad-too_small_size-without_eopm-3.lzma b/tests/files/bad-too_small_size-without_eopm-3.lzma Binary files differnew file mode 100644 index 0000000..67a1af3 --- /dev/null +++ b/tests/files/bad-too_small_size-without_eopm-3.lzma diff --git a/tests/files/bad-unknown_size-without_eopm.lzma b/tests/files/bad-unknown_size-without_eopm.lzma Binary files differnew file mode 100644 index 0000000..b3d7a52 --- /dev/null +++ b/tests/files/bad-unknown_size-without_eopm.lzma diff --git a/tests/files/good-0-empty.xz b/tests/files/good-0-empty.xz Binary files differnew file mode 100644 index 0000000..83b95e0 --- /dev/null +++ b/tests/files/good-0-empty.xz diff --git a/tests/files/good-0cat-empty.xz b/tests/files/good-0cat-empty.xz Binary files differnew file mode 100644 index 0000000..e6fc314 --- /dev/null +++ b/tests/files/good-0cat-empty.xz diff --git a/tests/files/good-0catpad-empty.xz b/tests/files/good-0catpad-empty.xz Binary files differnew file mode 100644 index 0000000..4f86b7d --- /dev/null +++ b/tests/files/good-0catpad-empty.xz diff --git a/tests/files/good-0pad-empty.xz b/tests/files/good-0pad-empty.xz Binary files differnew file mode 100644 index 0000000..c51e3a6 --- /dev/null +++ b/tests/files/good-0pad-empty.xz diff --git a/tests/files/good-1-3delta-lzma2.xz b/tests/files/good-1-3delta-lzma2.xz Binary files differnew file mode 100644 index 0000000..a0be1d0 --- /dev/null +++ b/tests/files/good-1-3delta-lzma2.xz diff --git a/tests/files/good-1-arm64-lzma2-1.xz b/tests/files/good-1-arm64-lzma2-1.xz Binary files differnew file mode 100644 index 0000000..78169f1 --- /dev/null +++ b/tests/files/good-1-arm64-lzma2-1.xz diff --git a/tests/files/good-1-arm64-lzma2-2.xz b/tests/files/good-1-arm64-lzma2-2.xz Binary files differnew file mode 100644 index 0000000..e0302fe --- /dev/null +++ b/tests/files/good-1-arm64-lzma2-2.xz diff --git a/tests/files/good-1-block_header-1.xz b/tests/files/good-1-block_header-1.xz Binary files differnew file mode 100644 index 0000000..fea5ad2 --- /dev/null +++ b/tests/files/good-1-block_header-1.xz diff --git a/tests/files/good-1-block_header-2.xz b/tests/files/good-1-block_header-2.xz Binary files differnew file mode 100644 index 0000000..6b5dcb3 --- /dev/null +++ b/tests/files/good-1-block_header-2.xz diff --git a/tests/files/good-1-block_header-3.xz b/tests/files/good-1-block_header-3.xz Binary files differnew file mode 100644 index 0000000..1565312 --- /dev/null +++ b/tests/files/good-1-block_header-3.xz diff --git a/tests/files/good-1-check-crc32.xz b/tests/files/good-1-check-crc32.xz Binary files differnew file mode 100644 index 0000000..6c89593 --- /dev/null +++ b/tests/files/good-1-check-crc32.xz diff --git a/tests/files/good-1-check-crc64.xz b/tests/files/good-1-check-crc64.xz Binary files differnew file mode 100644 index 0000000..5a9915d --- /dev/null +++ b/tests/files/good-1-check-crc64.xz diff --git a/tests/files/good-1-check-none.xz b/tests/files/good-1-check-none.xz Binary files differnew file mode 100644 index 0000000..1e85faf --- /dev/null +++ b/tests/files/good-1-check-none.xz diff --git a/tests/files/good-1-check-sha256.xz b/tests/files/good-1-check-sha256.xz Binary files differnew file mode 100644 index 0000000..fdc556b --- /dev/null +++ b/tests/files/good-1-check-sha256.xz diff --git a/tests/files/good-1-delta-lzma2.tiff.xz b/tests/files/good-1-delta-lzma2.tiff.xz Binary files differnew file mode 100644 index 0000000..1f033bc --- /dev/null +++ b/tests/files/good-1-delta-lzma2.tiff.xz diff --git a/tests/files/good-1-empty-bcj-lzma2.xz b/tests/files/good-1-empty-bcj-lzma2.xz Binary files differnew file mode 100644 index 0000000..94016d8 --- /dev/null +++ b/tests/files/good-1-empty-bcj-lzma2.xz diff --git a/tests/files/good-1-lzma2-1.xz b/tests/files/good-1-lzma2-1.xz Binary files differnew file mode 100644 index 0000000..d8d6489 --- /dev/null +++ b/tests/files/good-1-lzma2-1.xz diff --git a/tests/files/good-1-lzma2-2.xz b/tests/files/good-1-lzma2-2.xz Binary files differnew file mode 100644 index 0000000..7e8cdf1 --- /dev/null +++ b/tests/files/good-1-lzma2-2.xz diff --git a/tests/files/good-1-lzma2-3.xz b/tests/files/good-1-lzma2-3.xz Binary files differnew file mode 100644 index 0000000..c4c72be --- /dev/null +++ b/tests/files/good-1-lzma2-3.xz diff --git a/tests/files/good-1-lzma2-4.xz b/tests/files/good-1-lzma2-4.xz Binary files differnew file mode 100644 index 0000000..e0d623a --- /dev/null +++ b/tests/files/good-1-lzma2-4.xz diff --git a/tests/files/good-1-lzma2-5.xz b/tests/files/good-1-lzma2-5.xz Binary files differnew file mode 100644 index 0000000..339d1c3 --- /dev/null +++ b/tests/files/good-1-lzma2-5.xz diff --git a/tests/files/good-1-sparc-lzma2.xz b/tests/files/good-1-sparc-lzma2.xz Binary files differnew file mode 100644 index 0000000..4532bc6 --- /dev/null +++ b/tests/files/good-1-sparc-lzma2.xz diff --git a/tests/files/good-1-v0-trailing-1.lz b/tests/files/good-1-v0-trailing-1.lz Binary files differnew file mode 100644 index 0000000..91f2b64 --- /dev/null +++ b/tests/files/good-1-v0-trailing-1.lz diff --git a/tests/files/good-1-v0.lz b/tests/files/good-1-v0.lz Binary files differnew file mode 100644 index 0000000..99e3f27 --- /dev/null +++ b/tests/files/good-1-v0.lz diff --git a/tests/files/good-1-v1-trailing-1.lz b/tests/files/good-1-v1-trailing-1.lz Binary files differnew file mode 100644 index 0000000..198e65c --- /dev/null +++ b/tests/files/good-1-v1-trailing-1.lz diff --git a/tests/files/good-1-v1-trailing-2.lz b/tests/files/good-1-v1-trailing-2.lz Binary files differnew file mode 100644 index 0000000..9a028fa --- /dev/null +++ b/tests/files/good-1-v1-trailing-2.lz diff --git a/tests/files/good-1-v1.lz b/tests/files/good-1-v1.lz Binary files differnew file mode 100644 index 0000000..4c9565c --- /dev/null +++ b/tests/files/good-1-v1.lz diff --git a/tests/files/good-1-x86-lzma2.xz b/tests/files/good-1-x86-lzma2.xz Binary files differnew file mode 100644 index 0000000..8053917 --- /dev/null +++ b/tests/files/good-1-x86-lzma2.xz diff --git a/tests/files/good-2-lzma2.xz b/tests/files/good-2-lzma2.xz Binary files differnew file mode 100644 index 0000000..bed5085 --- /dev/null +++ b/tests/files/good-2-lzma2.xz diff --git a/tests/files/good-2-v0-v1.lz b/tests/files/good-2-v0-v1.lz Binary files differnew file mode 100644 index 0000000..dc3165a --- /dev/null +++ b/tests/files/good-2-v0-v1.lz diff --git a/tests/files/good-2-v1-v0.lz b/tests/files/good-2-v1-v0.lz Binary files differnew file mode 100644 index 0000000..a999582 --- /dev/null +++ b/tests/files/good-2-v1-v0.lz diff --git a/tests/files/good-2-v1-v1.lz b/tests/files/good-2-v1-v1.lz Binary files differnew file mode 100644 index 0000000..5381891 --- /dev/null +++ b/tests/files/good-2-v1-v1.lz diff --git a/tests/files/good-known_size-with_eopm.lzma b/tests/files/good-known_size-with_eopm.lzma Binary files differnew file mode 100644 index 0000000..1b45307 --- /dev/null +++ b/tests/files/good-known_size-with_eopm.lzma diff --git a/tests/files/good-known_size-without_eopm.lzma b/tests/files/good-known_size-without_eopm.lzma Binary files differnew file mode 100644 index 0000000..83623fd --- /dev/null +++ b/tests/files/good-known_size-without_eopm.lzma diff --git a/tests/files/good-unknown_size-with_eopm.lzma b/tests/files/good-unknown_size-with_eopm.lzma Binary files differnew file mode 100644 index 0000000..0f4ff82 --- /dev/null +++ b/tests/files/good-unknown_size-with_eopm.lzma diff --git a/tests/files/unsupported-1-v234.lz b/tests/files/unsupported-1-v234.lz Binary files differnew file mode 100644 index 0000000..e571b0f --- /dev/null +++ b/tests/files/unsupported-1-v234.lz diff --git a/tests/files/unsupported-block_header.xz b/tests/files/unsupported-block_header.xz Binary files differnew file mode 100644 index 0000000..3830442 --- /dev/null +++ b/tests/files/unsupported-block_header.xz diff --git a/tests/files/unsupported-check.xz b/tests/files/unsupported-check.xz Binary files differnew file mode 100644 index 0000000..c28355e --- /dev/null +++ b/tests/files/unsupported-check.xz diff --git a/tests/files/unsupported-filter_flags-1.xz b/tests/files/unsupported-filter_flags-1.xz Binary files differnew file mode 100644 index 0000000..48b9373 --- /dev/null +++ b/tests/files/unsupported-filter_flags-1.xz diff --git a/tests/files/unsupported-filter_flags-2.xz b/tests/files/unsupported-filter_flags-2.xz Binary files differnew file mode 100644 index 0000000..c283359 --- /dev/null +++ b/tests/files/unsupported-filter_flags-2.xz diff --git a/tests/files/unsupported-filter_flags-3.xz b/tests/files/unsupported-filter_flags-3.xz Binary files differnew file mode 100644 index 0000000..2608498 --- /dev/null +++ b/tests/files/unsupported-filter_flags-3.xz diff --git a/tests/ossfuzz/Makefile b/tests/ossfuzz/Makefile new file mode 100644 index 0000000..747fb66 --- /dev/null +++ b/tests/ossfuzz/Makefile @@ -0,0 +1,7 @@ +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 + +clean: + rm -f *.o diff --git a/tests/ossfuzz/config/fuzz.dict b/tests/ossfuzz/config/fuzz.dict new file mode 100644 index 0000000..932d67c --- /dev/null +++ b/tests/ossfuzz/config/fuzz.dict @@ -0,0 +1,2 @@ +"\xFD7zXZ\x00" +"YZ" diff --git a/tests/ossfuzz/config/fuzz.options b/tests/ossfuzz/config/fuzz.options new file mode 100644 index 0000000..d59dfc1 --- /dev/null +++ b/tests/ossfuzz/config/fuzz.options @@ -0,0 +1,2 @@ +[libfuzzer] +dict = fuzz.dict diff --git a/tests/ossfuzz/fuzz.c b/tests/ossfuzz/fuzz.c new file mode 100644 index 0000000..6d89930 --- /dev/null +++ b/tests/ossfuzz/fuzz.c @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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/test_bcj_exact_size.c b/tests/test_bcj_exact_size.c new file mode 100644 index 0000000..551166c --- /dev/null +++ b/tests/test_bcj_exact_size.c @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_bcj_exact_size.c +/// \brief Tests BCJ decoding when the output size is known +/// +/// These tests fail with XZ Utils 5.0.3 and earlier. +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + + +static void +test_exact_size(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + if (!lzma_filter_encoder_is_supported(LZMA_FILTER_POWERPC) + || !lzma_filter_decoder_is_supported( + LZMA_FILTER_POWERPC)) + assert_skip("PowerPC BCJ encoder and/or decoder " + "is disabled"); + + // Something to be compressed + const uint8_t in[16] = "0123456789ABCDEF"; + + // in[] after compression + uint8_t compressed[1024]; + size_t compressed_size = 0; + + // Output buffer for decompressing compressed[] + uint8_t out[sizeof(in)]; + + // Compress with PowerPC BCJ and LZMA2. PowerPC BCJ is used because + // it has fixed 4-byte alignment which makes triggering the potential + // bug easy. + lzma_options_lzma opt_lzma2; + assert_false(lzma_lzma_preset(&opt_lzma2, 0)); + + lzma_filter filters[3] = { + { .id = LZMA_FILTER_POWERPC, .options = NULL }, + { .id = LZMA_FILTER_LZMA2, .options = &opt_lzma2 }, + { .id = LZMA_VLI_UNKNOWN, .options = NULL }, + }; + + assert_lzma_ret(lzma_stream_buffer_encode( + filters, LZMA_CHECK_CRC32, NULL, + in, sizeof(in), + compressed, &compressed_size, sizeof(compressed)), + LZMA_OK); + + // Decompress so that we won't give more output space than + // the Stream will need. + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_stream_decoder(&strm, 10 << 20, 0), LZMA_OK); + + strm.next_in = compressed; + strm.next_out = out; + + while (true) { + if (strm.total_in < compressed_size) + strm.avail_in = 1; + + const lzma_ret ret = lzma_code(&strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) { + assert_uint_eq(strm.total_in, compressed_size); + assert_uint_eq(strm.total_out, sizeof(in)); + lzma_end(&strm); + return; + } + + assert_lzma_ret(ret, LZMA_OK); + + if (strm.total_out < sizeof(in)) + strm.avail_out = 1; + } +#endif +} + + +static void +test_empty_block(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + if (!lzma_filter_decoder_is_supported(LZMA_FILTER_POWERPC)) + assert_skip("PowerPC BCJ decoder is disabled"); + + // An empty file with one Block using PowerPC BCJ and LZMA2. + size_t in_size; + uint8_t *empty_bcj_lzma2 = tuktest_file_from_srcdir( + "files/good-1-empty-bcj-lzma2.xz", &in_size); + + // Decompress without giving any output space. + uint64_t memlimit = 1 << 20; + uint8_t out[1]; + size_t in_pos = 0; + size_t out_pos = 0; + assert_lzma_ret(lzma_stream_buffer_decode(&memlimit, 0, NULL, + empty_bcj_lzma2, &in_pos, in_size, out, &out_pos, 0), + LZMA_OK); + assert_uint_eq(in_pos, in_size); + assert_uint_eq(out_pos, 0); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + + tuktest_run(test_exact_size); + tuktest_run(test_empty_block); + + return tuktest_end(); +} diff --git a/tests/test_block_header.c b/tests/test_block_header.c new file mode 100644 index 0000000..b310135 --- /dev/null +++ b/tests/test_block_header.c @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_block_header.c +/// \brief Tests Block Header coders +// +// 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" + + +static lzma_options_lzma opt_lzma; + + +// Used in test_lzma_block_header_decode() between tests to ensure +// no artifacts are leftover in the block struct that could influence +// later tests. +#define RESET_BLOCK(block, buf) \ +do { \ + lzma_filter *filters_ = (block).filters; \ + lzma_filters_free(filters_, NULL); \ + memzero((buf), sizeof((buf))); \ + memzero(&(block), sizeof(lzma_block)); \ + (block).filters = filters_; \ + (block).check = LZMA_CHECK_CRC32; \ +} while (0); + + +#ifdef HAVE_ENCODERS +static lzma_filter filters_none[1] = { + { + .id = LZMA_VLI_UNKNOWN, + }, +}; + + +static lzma_filter filters_one[2] = { + { + .id = LZMA_FILTER_LZMA2, + .options = &opt_lzma, + }, { + .id = LZMA_VLI_UNKNOWN, + } +}; + + +// These filters are only used in test_lzma_block_header_decode() +// which only runs if encoders and decoders are configured. +#ifdef HAVE_DECODERS +static lzma_filter filters_four[5] = { + { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_LZMA2, + .options = &opt_lzma, + }, { + .id = LZMA_VLI_UNKNOWN, + } +}; +#endif + + +static lzma_filter filters_five[6] = { + { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_X86, + .options = NULL, + }, { + .id = LZMA_FILTER_LZMA2, + .options = &opt_lzma, + }, { + .id = LZMA_VLI_UNKNOWN, + } +}; +#endif + + +static void +test_lzma_block_header_size(void) +{ +#ifndef HAVE_ENCODERS + assert_skip("Encoder support disabled"); +#else + if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86)) + assert_skip("x86 BCJ encoder is disabled"); + + lzma_block block = { + .version = 0, + .filters = filters_one, + .compressed_size = LZMA_VLI_UNKNOWN, + .uncompressed_size = LZMA_VLI_UNKNOWN, + .check = LZMA_CHECK_CRC32 + }; + + // Test that all initial options are valid + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); + + // Test invalid version number + for (uint32_t i = 2; i < 20; i++) { + block.version = i; + assert_lzma_ret(lzma_block_header_size(&block), + LZMA_OPTIONS_ERROR); + } + + block.version = 1; + + // Test invalid compressed size + block.compressed_size = 0; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + + block.compressed_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + block.compressed_size = LZMA_VLI_UNKNOWN; + + // Test invalid uncompressed size + block.uncompressed_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + block.uncompressed_size = LZMA_VLI_MAX; + + // Test invalid filters + block.filters = NULL; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + + block.filters = filters_none; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + + block.filters = filters_five; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_PROG_ERROR); + + block.filters = filters_one; + + // Test setting compressed_size to something valid + block.compressed_size = 4096; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); + + // Test setting uncompressed_size to something valid + block.uncompressed_size = 4096; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); + + // This should pass, but header_size will be an invalid value + // because the total block size will not be able to fit in a valid + // lzma_vli. This way a temporary value can be used to reserve + // space for the header and later the actual value can be set. + block.compressed_size = LZMA_VLI_MAX; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); + + // Use an invalid value for a filter option. This should still pass + // because the size of the LZMA2 properties is known by liblzma + // without reading any of the options so it doesn't validate them. + lzma_options_lzma bad_ops; + assert_false(lzma_lzma_preset(&bad_ops, 1)); + bad_ops.pb = 0x1000; + + lzma_filter bad_filters[2] = { + { + .id = LZMA_FILTER_LZMA2, + .options = &bad_ops + }, + { + .id = LZMA_VLI_UNKNOWN, + .options = NULL + } + }; + + block.filters = bad_filters; + + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); + + // Use an invalid block option. The check type isn't stored in + // the Block Header and so _header_size ignores it. + block.check = INVALID_LZMA_CHECK_ID; + block.ignore_check = false; + + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_uint(block.header_size, >=, LZMA_BLOCK_HEADER_SIZE_MIN); + assert_uint(block.header_size, <=, LZMA_BLOCK_HEADER_SIZE_MAX); + assert_uint_eq(block.header_size % 4, 0); +#endif +} + + +static void +test_lzma_block_header_encode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + + if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86) + || !lzma_filter_decoder_is_supported(LZMA_FILTER_X86)) + assert_skip("x86 BCJ encoder and/or decoder " + "is disabled"); + + lzma_block block = { + .version = 1, + .filters = filters_one, + .compressed_size = LZMA_VLI_UNKNOWN, + .uncompressed_size = LZMA_VLI_UNKNOWN, + .check = LZMA_CHECK_CRC32, + }; + + // Ensure all block options are valid before changes are tested + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + + uint8_t out[LZMA_BLOCK_HEADER_SIZE_MAX]; + + // Test invalid block version + for (uint32_t i = 2; i < 20; i++) { + block.version = i; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + } + + block.version = 1; + + // Test invalid header size (< min, > max, % 4 != 0) + block.header_size = LZMA_BLOCK_HEADER_SIZE_MIN - 4; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.header_size = LZMA_BLOCK_HEADER_SIZE_MIN + 2; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.header_size = LZMA_BLOCK_HEADER_SIZE_MAX + 4; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + + // Test invalid compressed_size + block.compressed_size = 0; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.compressed_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + + // This test passes test_lzma_block_header_size, but should + // fail here because there is not enough space to encode the + // proper block size because the total size is too big to fit + // in an lzma_vli + block.compressed_size = LZMA_VLI_MAX; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.compressed_size = LZMA_VLI_UNKNOWN; + + // Test invalid uncompressed size + block.uncompressed_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.uncompressed_size = LZMA_VLI_UNKNOWN; + + // Test invalid block check + block.check = INVALID_LZMA_CHECK_ID; + block.ignore_check = false; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + block.check = LZMA_CHECK_CRC32; + + // Test invalid filters + block.filters = NULL; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + + block.filters = filters_none; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + + block.filters = filters_five; + block.header_size = LZMA_BLOCK_HEADER_SIZE_MAX - 4; + assert_lzma_ret(lzma_block_header_encode(&block, out), + LZMA_PROG_ERROR); + + // Test valid encoding and verify bytes of block header. + // More complicated tests for encoding headers are included + // in test_lzma_block_header_decode. + block.filters = filters_one; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK); + + // First read block header size from out and verify + // that it == (encoded size + 1) * 4 + uint32_t header_size = (out[0] + 1U) * 4; + assert_uint_eq(header_size, block.header_size); + + // Next read block flags + uint8_t flags = out[1]; + + // Should have number of filters = 1 + assert_uint_eq((flags & 0x3) + 1, 1); + + // Bits 2-7 must be empty not set + assert_uint_eq(flags & (0xFF - 0x3), 0); + + // Verify filter flags + // Decode Filter ID + lzma_vli filter_id = 0; + size_t pos = 2; + assert_lzma_ret(lzma_vli_decode(&filter_id, NULL, out, + &pos, header_size), LZMA_OK); + assert_uint_eq(filter_id, filters_one[0].id); + + // Decode Size of Properties + lzma_vli prop_size = 0; + assert_lzma_ret(lzma_vli_decode(&prop_size, NULL, out, + &pos, header_size), LZMA_OK); + + // LZMA2 has 1 byte prop size + assert_uint_eq(prop_size, 1); + uint8_t expected_filter_props = 0; + assert_lzma_ret(lzma_properties_encode(filters_one, + &expected_filter_props), LZMA_OK); + assert_uint_eq(out[pos], expected_filter_props); + pos++; + + // Check null-padding + for (size_t i = pos; i < header_size - 4; i++) + assert_uint_eq(out[i], 0); + + // Check CRC32 + assert_uint_eq(read32le(&out[header_size - 4]), lzma_crc32(out, + header_size - 4, 0)); +#endif +} + + +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +// Helper function to compare two lzma_block structures field by field +static void +compare_blocks(lzma_block *block_expected, lzma_block *block_actual) +{ + assert_uint_eq(block_actual->version, block_expected->version); + assert_uint_eq(block_actual->compressed_size, + block_expected->compressed_size); + assert_uint_eq(block_actual->uncompressed_size, + block_expected->uncompressed_size); + assert_uint_eq(block_actual->check, block_expected->check); + assert_uint_eq(block_actual->header_size, block_expected->header_size); + + // Compare filter IDs + assert_true(block_expected->filters && block_actual->filters); + lzma_filter expected_filter = block_expected->filters[0]; + uint32_t filter_count = 0; + while (expected_filter.id != LZMA_VLI_UNKNOWN) { + assert_uint_eq(block_actual->filters[filter_count].id, + expected_filter.id); + expected_filter = block_expected->filters[++filter_count]; + } + + assert_uint_eq(block_actual->filters[filter_count].id, + LZMA_VLI_UNKNOWN); +} +#endif + + +static void +test_lzma_block_header_decode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + if (!lzma_filter_encoder_is_supported(LZMA_FILTER_X86) + || !lzma_filter_decoder_is_supported(LZMA_FILTER_X86)) + assert_skip("x86 BCJ encoder and/or decoder " + "is disabled"); + + lzma_block block = { + .filters = filters_one, + .compressed_size = LZMA_VLI_UNKNOWN, + .uncompressed_size = LZMA_VLI_UNKNOWN, + .check = LZMA_CHECK_CRC32, + .version = 0 + }; + + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + + // Encode block header with simple options + uint8_t out[LZMA_BLOCK_HEADER_SIZE_MAX]; + assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK); + + // Decode block header and check that the options match + lzma_filter decoded_filters[LZMA_FILTERS_MAX + 1]; + lzma_block decoded_block = { + .version = 0, + .filters = decoded_filters, + .check = LZMA_CHECK_CRC32 + }; + decoded_block.header_size = lzma_block_header_size_decode(out[0]); + + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OK); + compare_blocks(&block, &decoded_block); + + // Reset output buffer and decoded_block + RESET_BLOCK(decoded_block, out); + + // Test with compressed size set + block.compressed_size = 4096; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK); + decoded_block.header_size = lzma_block_header_size_decode(out[0]); + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OK); + compare_blocks(&block, &decoded_block); + + RESET_BLOCK(decoded_block, out); + + // Test with uncompressed size set + block.uncompressed_size = 4096; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK); + decoded_block.header_size = lzma_block_header_size_decode(out[0]); + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OK); + compare_blocks(&block, &decoded_block); + + RESET_BLOCK(decoded_block, out); + + // Test with multiple filters + block.filters = filters_four; + assert_lzma_ret(lzma_block_header_size(&block), LZMA_OK); + assert_lzma_ret(lzma_block_header_encode(&block, out), LZMA_OK); + decoded_block.header_size = lzma_block_header_size_decode(out[0]); + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OK); + compare_blocks(&block, &decoded_block); + + lzma_filters_free(decoded_filters, NULL); + + // Test with too high version. The decoder will set it to a version + // that it supports. + decoded_block.version = 2; + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OK); + assert_uint_eq(decoded_block.version, 1); + + // Free the filters for the last time since all other cases should + // result in an error. + lzma_filters_free(decoded_filters, NULL); + + // Test bad check type + decoded_block.check = INVALID_LZMA_CHECK_ID; + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_PROG_ERROR); + decoded_block.check = LZMA_CHECK_CRC32; + + // Test bad check value + out[decoded_block.header_size - 1] -= 10; + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_DATA_ERROR); + out[decoded_block.header_size - 1] += 10; + + // Test non-NULL padding + out[decoded_block.header_size - 5] = 1; + + // Recompute CRC32 + write32le(&out[decoded_block.header_size - 4], lzma_crc32(out, + decoded_block.header_size - 4, 0)); + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OPTIONS_ERROR); + + // Test unsupported flags + out[1] = 0xFF; + + // Recompute CRC32 + write32le(&out[decoded_block.header_size - 4], lzma_crc32(out, + decoded_block.header_size - 4, 0)); + assert_lzma_ret(lzma_block_header_decode(&decoded_block, NULL, out), + LZMA_OPTIONS_ERROR); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + + if (lzma_lzma_preset(&opt_lzma, 1)) + tuktest_error("lzma_lzma_preset() failed"); + + tuktest_run(test_lzma_block_header_size); + tuktest_run(test_lzma_block_header_encode); + tuktest_run(test_lzma_block_header_decode); + + return tuktest_end(); +} diff --git a/tests/test_check.c b/tests/test_check.c new file mode 100644 index 0000000..cb1ad25 --- /dev/null +++ b/tests/test_check.c @@ -0,0 +1,390 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_check.c +/// \brief Tests integrity checks +// +// 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" +#include "mythread.h" + + +// These must be specified as numbers so that the test works on EBCDIC +// systems too. +// static const uint8_t test_string[9] = "123456789"; +// static const uint8_t test_unaligned[12] = "xxx123456789"; +static const uint8_t test_string[9] = { 49, 50, 51, 52, 53, 54, 55, 56, 57 }; +static const uint8_t test_unaligned[12] + = { 120, 120, 120, 49, 50, 51, 52, 53, 54, 55, 56, 57 }; + +// 2 MB is more than enough for the tests. Actually a tiny value would +// work because we don't actually decompress the files, we only test +// decoding of the Stream Header fields. +#define TEST_CHECK_MEMLIMIT (2U << 20) + +static size_t no_check_size; +static uint8_t *no_check_xz_data; + +static size_t unsupported_check_size; +static uint8_t *unsupported_check_xz_data; + +#ifdef HAVE_CHECK_CRC32 +static size_t crc32_size; +static uint8_t *crc32_xz_data; +#endif + +#ifdef HAVE_CHECK_CRC64 +static size_t crc64_size; +static uint8_t *crc64_xz_data; +#endif + +#ifdef HAVE_CHECK_SHA256 +static size_t sha256_size; +static uint8_t *sha256_xz_data; +#endif + + +#ifdef HAVE_CHECK_CRC64 +static const uint8_t * +get_random256(uint32_t *seed) +{ + static uint8_t buf[256]; + + for (size_t i = 0; i < sizeof(buf); ++i) { + *seed = *seed * 1103515245 + 12345; + buf[i] = (uint8_t)(*seed >> 22); + } + + return buf; +} +#endif + + +static void +test_lzma_crc32(void) +{ + // CRC32 is always enabled. + assert_true(lzma_check_is_supported(LZMA_CHECK_CRC32)); + + const uint32_t test_vector = 0xCBF43926; + + // Test 1 + assert_uint_eq(lzma_crc32(test_string, sizeof(test_string), 0), + test_vector); + + // Test 2 + assert_uint_eq(lzma_crc32(test_unaligned + 3, sizeof(test_string), 0), + test_vector); + + // Test 3 + uint32_t crc = 0; + for (size_t i = 0; i < sizeof(test_string); ++i) + crc = lzma_crc32(test_string + i, 1, crc); + assert_uint_eq(crc, test_vector); +} + + +static void +test_lzma_crc64(void) +{ + // CRC64 can be disabled. + if (!lzma_check_is_supported(LZMA_CHECK_CRC64)) + assert_skip("CRC64 support is disabled"); + + // If CRC64 is disabled then lzma_crc64() will be missing. + // Using an ifdef here avoids a linker error. +#ifdef HAVE_CHECK_CRC64 + const uint64_t test_vector = 0x995DC9BBDF1939FA; + + // Test 1 + assert_uint_eq(lzma_crc64(test_string, sizeof(test_string), 0), + test_vector); + + // Test 2 + assert_uint_eq(lzma_crc64(test_unaligned + 3, sizeof(test_string), 0), + test_vector); + + // Test 3 + uint64_t crc = 0; + for (size_t i = 0; i < sizeof(test_string); ++i) + crc = lzma_crc64(test_string + i, 1, crc); + assert_uint_eq(crc, test_vector); + + // Test 4: The CLMUL implementation works on 16-byte chunks. + // Test combination of different start and end alignments + // and also short buffer lengths where special handling is needed. + uint32_t seed = 29; + crc = 0x96E30D5184B7FA2C; // Random initial value + for (size_t start = 0; start < 32; ++start) + for (size_t size = 1; size < 256 - 32; ++size) + crc = lzma_crc64(get_random256(&seed), size, crc); + + assert_uint_eq(crc, 0x23AB787177231C9F); +#endif +} + + +static void +test_lzma_supported_checks(void) +{ + static const lzma_check expected_check_ids[] = { + LZMA_CHECK_NONE, +#ifdef HAVE_CHECK_CRC32 + LZMA_CHECK_CRC32, +#endif +#ifdef HAVE_CHECK_CRC64 + LZMA_CHECK_CRC64, +#endif +#ifdef HAVE_CHECK_SHA256 + LZMA_CHECK_SHA256, +#endif + }; + + for (lzma_check i = 0; i <= LZMA_CHECK_ID_MAX + 1; i++) { + bool matched = false; + for (unsigned int j = 0; j < ARRAY_SIZE(expected_check_ids); + j++) { + if (expected_check_ids[j] == i) { + matched = true; + break; + } + } + + if (matched) + assert_true(lzma_check_is_supported(i)); + else + assert_false(lzma_check_is_supported(i)); + } +} + + +static void +test_lzma_check_size(void) +{ + // Expected check sizes taken from src/liblzma/api/lzma/check.h + static const uint32_t expected_check_sizes[] = { + 0, 4, 4, 4, 8, 8, 8, 16, 16, 16, + 32, 32, 32, 64, 64, 64 + }; + + for (lzma_check i = 0; i < ARRAY_SIZE(expected_check_sizes); i++) + assert_uint_eq(expected_check_sizes[i], lzma_check_size(i)); + + assert_uint_eq(lzma_check_size(INVALID_LZMA_CHECK_ID), UINT32_MAX); +} + + +// Test the single threaded decoder for lzma_get_check +static void +test_lzma_get_check_st(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + const uint32_t flags = LZMA_TELL_ANY_CHECK | + LZMA_TELL_UNSUPPORTED_CHECK | + LZMA_TELL_NO_CHECK; + + uint8_t outbuf[128]; + lzma_stream strm = LZMA_STREAM_INIT; + + // Test a file with no integrity check: + assert_lzma_ret(lzma_stream_decoder(&strm, TEST_CHECK_MEMLIMIT, + flags), LZMA_OK); + strm.next_in = no_check_xz_data; + strm.avail_in = no_check_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_NO_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_NONE); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + + // Test a file with an unsupported integrity check type: + assert_lzma_ret(lzma_stream_decoder(&strm, TEST_CHECK_MEMLIMIT, + flags), LZMA_OK); + strm.next_in = unsupported_check_xz_data; + strm.avail_in = unsupported_check_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_UNSUPPORTED_CHECK); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + + // Test a file with CRC32 as the integrity check: +#ifdef HAVE_CHECK_CRC32 + assert_lzma_ret(lzma_stream_decoder(&strm, TEST_CHECK_MEMLIMIT, + flags), LZMA_OK); + strm.next_in = crc32_xz_data; + strm.avail_in = crc32_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_CRC32); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + // Test a file with CRC64 as the integrity check: +#ifdef HAVE_CHECK_CRC64 + assert_lzma_ret(lzma_stream_decoder(&strm, TEST_CHECK_MEMLIMIT, + flags), LZMA_OK); + strm.next_in = crc64_xz_data; + strm.avail_in = crc64_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_CRC64); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + // Test a file with SHA-256 as the integrity check: +#ifdef HAVE_CHECK_SHA256 + assert_lzma_ret(lzma_stream_decoder(&strm, TEST_CHECK_MEMLIMIT, + flags), LZMA_OK); + strm.next_in = sha256_xz_data; + strm.avail_in = sha256_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_SHA256); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + lzma_end(&strm); +#endif +} + + +static void +test_lzma_get_check_mt(void) +{ +#ifndef MYTHREAD_ENABLED + assert_skip("Threading support disabled"); +#elif !defined(HAVE_DECODERS) + assert_skip("Decoder support disabled"); +#else + const uint32_t flags = LZMA_TELL_ANY_CHECK | + LZMA_TELL_UNSUPPORTED_CHECK | + LZMA_TELL_NO_CHECK; + + const lzma_mt options = { + .flags = flags, + .threads = 2, + .timeout = 0, + .memlimit_threading = TEST_CHECK_MEMLIMIT, + .memlimit_stop = TEST_CHECK_MEMLIMIT + }; + + uint8_t outbuf[128]; + lzma_stream strm = LZMA_STREAM_INIT; + + // Test a file with no integrity check: + assert_lzma_ret(lzma_stream_decoder_mt(&strm, &options), LZMA_OK); + strm.next_in = no_check_xz_data; + strm.avail_in = no_check_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_NO_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_NONE); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + + // Test a file with an unsupported integrity check type: + assert_lzma_ret(lzma_stream_decoder_mt(&strm, &options), LZMA_OK); + strm.next_in = unsupported_check_xz_data; + strm.avail_in = unsupported_check_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_UNSUPPORTED_CHECK); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); + + // Test a file with CRC32 as the integrity check: +#ifdef HAVE_CHECK_CRC32 + assert_lzma_ret(lzma_stream_decoder_mt(&strm, &options), LZMA_OK); + strm.next_in = crc32_xz_data; + strm.avail_in = crc32_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_CRC32); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + // Test a file with CRC64 as the integrity check: +#ifdef HAVE_CHECK_CRC64 + assert_lzma_ret(lzma_stream_decoder_mt(&strm, &options), LZMA_OK); + strm.next_in = crc64_xz_data; + strm.avail_in = crc64_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_CRC64); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + // Test a file with SHA-256 as the integrity check: +#ifdef HAVE_CHECK_SHA256 + assert_lzma_ret(lzma_stream_decoder_mt(&strm,&options), LZMA_OK); + strm.next_in = sha256_xz_data; + strm.avail_in = sha256_size; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_GET_CHECK); + assert_lzma_check(lzma_get_check(&strm), LZMA_CHECK_SHA256); + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), LZMA_STREAM_END); +#endif + + lzma_end(&strm); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + + no_check_xz_data = tuktest_file_from_srcdir( + "files/good-1-check-none.xz", &no_check_size); + + unsupported_check_xz_data = tuktest_file_from_srcdir( + "files/unsupported-check.xz", + &unsupported_check_size); + +#ifdef HAVE_CHECK_CRC32 + crc32_xz_data = tuktest_file_from_srcdir( + "files/good-1-check-crc32.xz", &crc32_size); +#endif + +#ifdef HAVE_CHECK_CRC64 + crc64_xz_data = tuktest_file_from_srcdir( + "files/good-1-check-crc64.xz", &crc64_size); +#endif + +#ifdef HAVE_CHECK_SHA256 + sha256_xz_data = tuktest_file_from_srcdir( + "files/good-1-check-sha256.xz", &sha256_size); +#endif + + tuktest_run(test_lzma_crc32); + tuktest_run(test_lzma_crc64); + tuktest_run(test_lzma_supported_checks); + tuktest_run(test_lzma_check_size); + tuktest_run(test_lzma_get_check_st); + tuktest_run(test_lzma_get_check_mt); + + return tuktest_end(); +} diff --git a/tests/test_compress.sh b/tests/test_compress.sh new file mode 100755 index 0000000..ff13cef --- /dev/null +++ b/tests/test_compress.sh @@ -0,0 +1,150 @@ +#!/bin/sh + +############################################################################### +# +# Author: Lasse Collin +# +# This file has been put into the public domain. +# You can do whatever you want with this file. +# +############################################################################### + +# If xz wasn't built, this test is skipped. +if test -x ../src/xz/xz ; then + : +else + exit 77 +fi + +# 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 \ + && grep 'define HAVE_DECODERS' ../config.h > /dev/null ; then + : +else + echo "Compression or decompression support is disabled, skipping this test." + exit 77 +fi + +# Find out if our shell supports functions. +eval 'unset foo ; foo() { return 42; } ; foo' +if test $? != 42 ; then + echo "/bin/sh doesn't support functions, skipping this test." + exit 77 +fi + +test_xz() { + if $XZ -c "$@" "$FILE" > "$TMP_COMP"; then + : + else + echo "Compressing failed: $* $FILE" + exit 1 + fi + + if $XZ -cd "$TMP_COMP" > "$TMP_UNCOMP" ; then + : + else + echo "Decompressing failed: $* $FILE" + exit 1 + fi + + if cmp "$TMP_UNCOMP" "$FILE" ; then + : + else + echo "Decompressed file does not match" \ + "the original: $* $FILE" + exit 1 + fi + + if test -n "$XZDEC" ; then + if $XZDEC "$TMP_COMP" > "$TMP_UNCOMP" ; then + : + else + echo "Decompressing failed: $* $FILE" + exit 1 + fi + + if cmp "$TMP_UNCOMP" "$FILE" ; then + : + else + echo "Decompressed file does not match" \ + "the original: $* $FILE" + exit 1 + fi + 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= + +# Create the required input file if needed. +# +# Derive temporary filenames for compressed and uncompressed outputs +# from the input filename. This is needed when multiple tests are +# run in parallel. +FILE=$1 +TMP_COMP="tmp_comp_$FILE" +TMP_UNCOMP="tmp_uncomp_$FILE" + +case $FILE in + # compress_generated files will be created in the build directory + # in the /tests/ sub-directory. + compress_generated_*) + if ./create_compress_files "$FILE" ; then + : + else + rm -f "$FILE" + echo "Failed to create the file '$FILE'." + exit 1 + fi + ;; + # compress_prepared files exist in the source directory since they + # do not need to be copied or regenerated. + compress_prepared_*) + FILE="$srcdir/$FILE" + ;; + '') + echo "No test file was specified." + exit 1 + ;; +esac + +# Remove temporary now (in case they are something weird), and on exit. +rm -f "$TMP_COMP" "$TMP_UNCOMP" +trap 'rm -f "$TMP_COMP" "$TMP_UNCOMP"' 0 + +# Compress and decompress the file with various filter configurations. +# +# Don't test with empty arguments; it breaks some ancient +# proprietary /bin/sh versions due to $@ used in test_xz(). +test_xz -1 +test_xz -2 +test_xz -3 +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 + shift + test_xz "$@" --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 + +exit 0 diff --git a/tests/test_compress_generated_abc b/tests/test_compress_generated_abc new file mode 100755 index 0000000..43c6951 --- /dev/null +++ b/tests/test_compress_generated_abc @@ -0,0 +1,3 @@ +#!/bin/sh + +exec "$srcdir/test_compress.sh" compress_generated_abc diff --git a/tests/test_compress_generated_random b/tests/test_compress_generated_random new file mode 100755 index 0000000..e47555d --- /dev/null +++ b/tests/test_compress_generated_random @@ -0,0 +1,3 @@ +#!/bin/sh + +exec "$srcdir/test_compress.sh" compress_generated_random diff --git a/tests/test_compress_generated_text b/tests/test_compress_generated_text new file mode 100755 index 0000000..412ae0e --- /dev/null +++ b/tests/test_compress_generated_text @@ -0,0 +1,3 @@ +#!/bin/sh + +exec "$srcdir/test_compress.sh" compress_generated_text diff --git a/tests/test_compress_prepared_bcj_sparc b/tests/test_compress_prepared_bcj_sparc new file mode 100755 index 0000000..deb76ef --- /dev/null +++ b/tests/test_compress_prepared_bcj_sparc @@ -0,0 +1,3 @@ +#!/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 new file mode 100755 index 0000000..3452d7f --- /dev/null +++ b/tests/test_compress_prepared_bcj_x86 @@ -0,0 +1,3 @@ +#!/bin/sh + +exec "$srcdir/test_compress.sh" compress_prepared_bcj_x86 diff --git a/tests/test_files.sh b/tests/test_files.sh new file mode 100755 index 0000000..60402e7 --- /dev/null +++ b/tests/test_files.sh @@ -0,0 +1,197 @@ +#!/bin/sh + +############################################################################### +# +# 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 +test -x "$XZ" || XZ= +test -x "$XZDEC" || XZDEC= +if test -z "$XZ$XZDEC"; then + echo "xz and xzdec were not built, skipping this test." + exit 77 +fi + +# If decompression support is missing, this test is skipped. +# 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 + : +else + echo "Decompression support is disabled, skipping this test." + exit 77 +fi + +# If a feature was disabled at build time, make it possible to skip +# some of the test files. Use exit status 77 if any files were skipped. +EXIT_STATUS=0 +have_feature() +{ + 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 + return 1 +} + + +####### +# .xz # +####### + +# 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 + +for I in "$srcdir"/files/good-*.xz +do + # If features were disabled at build time, keep this still working. + case $I in + */good-1-*delta-lzma2*.xz) + have_feature DECODER_DELTA "$I" || continue + ;; + esac + case $I in + */good-1-empty-bcj-lzma2.xz) + have_feature DECODER_POWERPC "$I" || continue + ;; + esac + case $I in + */good-1-sparc-lzma2.xz) + have_feature DECODER_SPARC "$I" || continue + ;; + esac + case $I in + */good-1-x86-lzma2.xz) + have_feature DECODER_X86 "$I" || continue + ;; + esac + case $I in + */good-1-arm64-lzma2-*.xz) + have_feature DECODER_ARM64 "$I" || continue + ;; + esac + + if test -z "$XZ" || "$XZ" $NO_WARN -dc "$I" > /dev/null; then + : + else + echo "Good file failed: $I" + exit 1 + fi + + if test -z "$XZDEC" || "$XZDEC" $NO_WARN "$I" > /dev/null; then + : + else + echo "Good file failed: $I" + exit 1 + fi +done + +for I in "$srcdir"/files/bad-*.xz +do + if test -n "$XZ" && "$XZ" -dc "$I" > /dev/null 2>&1; then + echo "Bad file succeeded: $I" + exit 1 + fi + + # xzdec doesn't warn about unsupported check so skip this if any of + # the check types were disabled at built time (NO_WARN isn't empty). + if test -n "$XZDEC" && test -z "$NO_WARN" \ + && "$XZDEC" "$I" > /dev/null 2>&1; then + echo "Bad file succeeded: $I" + exit 1 + fi +done + +# Testing for the lzma_index_append() bug in <= 5.2.6 needs "xz -l": +I="$srcdir/files/bad-3-index-uncomp-overflow.xz" +if test -n "$XZ" && "$XZ" -l "$I" > /dev/null 2>&1; then + echo "Bad file succeeded with xz -l: $I" + exit 1 +fi + +for I in "$srcdir"/files/unsupported-*.xz +do + # Test these only with xz as unsupported-check.xz will exit + # successfully with xzdec because it doesn't warn about + # unsupported check type. + if test -n "$XZ" && "$XZ" -dc "$I" > /dev/null 2>&1; then + echo "Unsupported file succeeded: $I" + exit 1 + fi +done + +# Test that this passes with --no-warn (-Q). +I="$srcdir/files/unsupported-check.xz" +if test -z "$XZ" || "$XZ" -dcqQ "$I" > /dev/null; then + : +else + echo "Unsupported file failed with xz -Q: $I" + exit 1 +fi + +if test -z "$XZDEC" || "$XZDEC" -qQ "$I" > /dev/null; then + : +else + echo "Unsupported file failed with xzdec -Q: $I" + exit 1 +fi + + +######### +# .lzma # +######### + +for I in "$srcdir"/files/good-*.lzma +do + if test -z "$XZ" || "$XZ" -dc "$I" > /dev/null; then + : + else + echo "Good file failed: $I" + exit 1 + fi +done + +for I in "$srcdir"/files/bad-*.lzma +do + if test -n "$XZ" && "$XZ" -dc "$I" > /dev/null 2>&1; then + echo "Bad file succeeded: $I" + exit 1 + fi +done + + +####### +# .lz # +####### + +if have_feature LZIP_DECODER ".lz files" ; then + for I in "$srcdir"/files/good-*.lz + do + if test -z "$XZ" || "$XZ" -dc "$I" > /dev/null; then + : + else + echo "Good file failed: $I" + exit 1 + fi + done + + for I in "$srcdir"/files/bad-*.lz "$srcdir"/files/unsupported-*.lz + do + if test -n "$XZ" && "$XZ" -dc "$I" > /dev/null 2>&1; then + echo "Bad file succeeded: $I" + exit 1 + fi + done +fi + +exit "$EXIT_STATUS" diff --git a/tests/test_filter_flags.c b/tests/test_filter_flags.c new file mode 100644 index 0000000..6d9f0b9 --- /dev/null +++ b/tests/test_filter_flags.c @@ -0,0 +1,527 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_filter_flags.c +/// \brief Tests Filter Flags coders +// +// 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" + +// FIXME: This is from src/liblzma/common/common.h but it cannot be +// included here. This constant is needed in only a few files, perhaps +// move it to some other internal header or create a new one? +#define LZMA_FILTER_RESERVED_START (LZMA_VLI_C(1) << 62) + + +#if defined(HAVE_ENCODERS) +// No tests are run without encoders, so init the global filters +// only when the encoders are enabled. +static lzma_filter lzma1_filter = { LZMA_FILTER_LZMA1, NULL }; +static lzma_filter lzma2_filter = { LZMA_FILTER_LZMA2, NULL }; +static lzma_filter delta_filter = { LZMA_FILTER_DELTA, NULL }; + +static lzma_filter bcj_filters_encoders[] = { +#ifdef HAVE_ENCODER_X86 + { LZMA_FILTER_X86, NULL }, +#endif +#ifdef HAVE_ENCODER_POWERPC + { LZMA_FILTER_POWERPC, NULL }, +#endif +#ifdef HAVE_ENCODER_IA64 + { LZMA_FILTER_IA64, NULL }, +#endif +#ifdef HAVE_ENCODER_ARM + { LZMA_FILTER_ARM, NULL }, +#endif +#ifdef HAVE_ENCODER_ARM64 + { LZMA_FILTER_ARM64, NULL }, +#endif +#ifdef HAVE_ENCODER_ARMTHUMB + { LZMA_FILTER_ARMTHUMB, NULL }, +#endif +#ifdef HAVE_ENCODER_SPARC + { LZMA_FILTER_SPARC, NULL }, +#endif +}; + +// HAVE_ENCODERS ifdef not terminated here because decoders are +// only used if encoders are, but encoders can still be used +// even if decoders are not. + +#ifdef HAVE_DECODERS +static lzma_filter bcj_filters_decoders[] = { +#ifdef HAVE_DECODER_X86 + { LZMA_FILTER_X86, NULL }, +#endif +#ifdef HAVE_DECODER_POWERPC + { LZMA_FILTER_POWERPC, NULL }, +#endif +#ifdef HAVE_DECODER_IA64 + { LZMA_FILTER_IA64, NULL }, +#endif +#ifdef HAVE_DECODER_ARM + { LZMA_FILTER_ARM, NULL }, +#endif +#ifdef HAVE_DECODER_ARM64 + { LZMA_FILTER_ARM64, NULL }, +#endif +#ifdef HAVE_DECODER_ARMTHUMB + { LZMA_FILTER_ARMTHUMB, NULL }, +#endif +#ifdef HAVE_DECODER_SPARC + { LZMA_FILTER_SPARC, NULL }, +#endif +}; +#endif +#endif + + +static void +test_lzma_filter_flags_size(void) +{ +#ifndef HAVE_ENCODERS + assert_skip("Encoder support disabled"); +#else + // For each supported filter, test that the size can be calculated + // and that the size calculated is reasonable. A reasonable size + // must be greater than 0, but less than the maximum size for the + // block header. + uint32_t size = 0; + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA1)) { + assert_lzma_ret(lzma_filter_flags_size(&size, + &lzma1_filter), LZMA_PROG_ERROR); + } + + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2)) { + assert_lzma_ret(lzma_filter_flags_size(&size, + &lzma2_filter), LZMA_OK); + 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++) { + assert_lzma_ret(lzma_filter_flags_size(&size, + &bcj_filters_encoders[i]), LZMA_OK); + assert_true(size != 0 && size < LZMA_BLOCK_HEADER_SIZE_MAX); + } + + if (lzma_filter_encoder_is_supported(LZMA_FILTER_DELTA)) { + assert_lzma_ret(lzma_filter_flags_size(&size, + &delta_filter), LZMA_OK); + assert_true(size != 0 && size < LZMA_BLOCK_HEADER_SIZE_MAX); + } + + // Test invalid Filter IDs + lzma_filter bad_filter = { 2, NULL }; + + assert_lzma_ret(lzma_filter_flags_size(&size, &bad_filter), + LZMA_OPTIONS_ERROR); + bad_filter.id = LZMA_VLI_MAX; + assert_lzma_ret(lzma_filter_flags_size(&size, &bad_filter), + LZMA_PROG_ERROR); + bad_filter.id = LZMA_FILTER_RESERVED_START; + assert_lzma_ret(lzma_filter_flags_size(&size, &bad_filter), + LZMA_PROG_ERROR); +#endif +} + + +// Helper function for test_lzma_filter_flags_encode. +// The should_encode parameter represents if the encoding operation +// is expected to fail. +// Avoid data -> encode -> decode -> compare to data. +// Instead create expected encoding and compare to result from +// lzma_filter_flags_encode. +// Filter Flags in .xz are encoded as: +// |Filter ID (VLI)|Size of Properties (VLI)|Filter Properties| +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +static void +verify_filter_flags_encode(lzma_filter *filter, bool should_encode) +{ + uint32_t size = 0; + + // 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)); + size_t out_pos = 0; + if (!should_encode) { + assert_false(lzma_filter_flags_encode(filter, encoded_out, + &out_pos, size) == LZMA_OK); + return; + } + + // Next encode the Filter Flags for the provided filter + assert_lzma_ret(lzma_filter_flags_encode(filter, encoded_out, + &out_pos, size), LZMA_OK); + assert_uint_eq(size, out_pos); + + // Next decode the VLI for the Filter ID and verify it matches + // the expected Filter ID + size_t filter_id_vli_size = 0; + lzma_vli filter_id = 0; + assert_lzma_ret(lzma_vli_decode(&filter_id, NULL, encoded_out, + &filter_id_vli_size, size), LZMA_OK); + assert_uint_eq(filter->id, filter_id); + + // Next decode the Size of Properties and ensure it equals + // the expected size. + // Expected size should be: + // total filter flag length - size of filter id VLI + size of + // property size VLI + // Not verifying the contents of Filter Properties since + // that belongs in a different test + size_t size_of_properties_vli_size = 0; + lzma_vli size_of_properties = 0; + assert_lzma_ret(lzma_vli_decode(&size_of_properties, NULL, + encoded_out + filter_id_vli_size, + &size_of_properties_vli_size, size), LZMA_OK); + assert_uint_eq(size - (size_of_properties_vli_size + + filter_id_vli_size), size_of_properties); +} +#endif + + +static void +test_lzma_filter_flags_encode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + // No test for LZMA1 since the .xz format does not support LZMA1 + // and so the flags cannot be encoded for that filter + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2)) { + // Test with NULL options that should fail + lzma_options_lzma *options = lzma2_filter.options; + lzma2_filter.options = NULL; + verify_filter_flags_encode(&lzma2_filter, false); + + // Place options back in the filter, and test should pass + lzma2_filter.options = options; + verify_filter_flags_encode(&lzma2_filter, true); + } + + // NOTE: Many BCJ filters require that start_offset is a multiple + // of some power of two. The Filter Flags encoder and decoder don't + // completely validate the options and thus 257 passes the tests + // with all BCJ filters. It would be caught when initializing + // a filter chain encoder or decoder. + lzma_options_bcj bcj_options = { + .start_offset = 257 + }; + + const uint32_t bcj_array_size = ARRAY_SIZE(bcj_filters_encoders); + for (uint32_t i = 0; i < bcj_array_size; i++) { + // NULL options should pass for bcj filters + verify_filter_flags_encode(&bcj_filters_encoders[i], true); + lzma_filter bcj_with_options = { + bcj_filters_encoders[i].id, &bcj_options }; + verify_filter_flags_encode(&bcj_with_options, true); + } + + if (lzma_filter_encoder_is_supported(LZMA_FILTER_DELTA)) { + lzma_options_delta delta_opts_below_min = { + .type = LZMA_DELTA_TYPE_BYTE, + .dist = LZMA_DELTA_DIST_MIN - 1 + }; + + lzma_options_delta delta_opts_above_max = { + .type = LZMA_DELTA_TYPE_BYTE, + .dist = LZMA_DELTA_DIST_MAX + 1 + }; + + verify_filter_flags_encode(&delta_filter, true); + + lzma_filter delta_filter_bad_options = { + LZMA_FILTER_DELTA, &delta_opts_below_min }; + + // Next test error case using minimum - 1 delta distance + verify_filter_flags_encode(&delta_filter_bad_options, false); + + // Next test error case using maximum + 1 delta distance + delta_filter_bad_options.options = &delta_opts_above_max; + verify_filter_flags_encode(&delta_filter_bad_options, false); + + // Next test NULL case + delta_filter_bad_options.options = NULL; + verify_filter_flags_encode(&delta_filter_bad_options, false); + } + + // Test expected failing cases + lzma_filter bad_filter = { LZMA_FILTER_RESERVED_START, NULL }; + size_t out_pos = 0; + 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); + out_pos = 0; + bad_filter.id = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_filter_flags_encode(&bad_filter, out, &out_pos, + out_size), LZMA_PROG_ERROR); + out_pos = 0; + + // Invalid Filter ID + bad_filter.id = 2; + assert_lzma_ret(lzma_filter_flags_encode(&bad_filter, out, &out_pos, + out_size), LZMA_OPTIONS_ERROR); + out_pos = 0; + + // Out size too small + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2)) { + uint32_t bad_size = 0; + + // First test with 0 output size + assert_lzma_ret(lzma_filter_flags_encode( + &lzma2_filter, out, &out_pos, 0), + LZMA_PROG_ERROR); + + // Next calculate the size needed to encode and + // use less than that + assert_lzma_ret(lzma_filter_flags_size(&bad_size, + &lzma2_filter), LZMA_OK); + + assert_lzma_ret(lzma_filter_flags_encode( + &lzma2_filter, out, &out_pos, + bad_size - 1), LZMA_PROG_ERROR); + out_pos = 0; + } + + // Invalid options + if (lzma_filter_encoder_is_supported(LZMA_FILTER_DELTA)) { + bad_filter.id = LZMA_FILTER_DELTA; + + // First test with NULL options + assert_lzma_ret(lzma_filter_flags_encode(&bad_filter, out, + &out_pos, out_size), LZMA_PROG_ERROR); + out_pos = 0; + + // Next test with invalid options + lzma_options_delta bad_options = { + .dist = LZMA_DELTA_DIST_MAX + 1, + .type = LZMA_DELTA_TYPE_BYTE + }; + bad_filter.options = &bad_options; + + assert_lzma_ret(lzma_filter_flags_encode(&bad_filter, out, + &out_pos, out_size), LZMA_PROG_ERROR); + } +#endif +} + + +// Helper function for test_lzma_filter_flags_decode. +// Encodes the filter_in without using lzma_filter_flags_encode. +// Leaves the specific assertions of filter_out options to the caller +// 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) +{ + uint32_t total_size = 0; + + assert_lzma_ret(lzma_filter_flags_size(&total_size, filter_in), + LZMA_OK); + assert_uint(total_size, >, 0); + uint8_t *filter_flag_buffer = tuktest_malloc(total_size); + + uint32_t properties_size = 0; + size_t out_pos = 0; + size_t in_pos = 0; + assert_lzma_ret(lzma_properties_size(&properties_size, filter_in), + LZMA_OK); + assert_lzma_ret(lzma_vli_encode(filter_in->id, NULL, + filter_flag_buffer, &out_pos, total_size), LZMA_OK); + assert_lzma_ret(lzma_vli_encode(properties_size, NULL, + filter_flag_buffer, &out_pos, total_size), + LZMA_OK); + assert_lzma_ret(lzma_properties_encode(filter_in, + filter_flag_buffer + out_pos), LZMA_OK); + assert_lzma_ret(lzma_filter_flags_decode(filter_out, NULL, + filter_flag_buffer, &in_pos, total_size), + LZMA_OK); + assert_uint_eq(filter_in->id, filter_out->id); +} +#endif + + +static void +test_lzma_filter_flags_decode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + // For each filter, only run the decoder test if both the encoder + // and decoder are enabled. This is because verify_filter_flags_decode + // uses lzma_filter_flags_size which requires the encoder. + if (lzma_filter_decoder_is_supported(LZMA_FILTER_LZMA2) && + lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2)) { + lzma_filter lzma2_decoded = { LZMA_FILTER_LZMA2, NULL }; + + verify_filter_flags_decode(&lzma2_filter, &lzma2_decoded); + + lzma_options_lzma *expected = lzma2_filter.options; + lzma_options_lzma *decoded = lzma2_decoded.options; + + // Only the dictionary size is encoded and decoded + // so only compare those + assert_uint_eq(decoded->dict_size, expected->dict_size); + + // The decoded options must be freed by the caller + free(decoded); + } + + const uint32_t bcj_array_size = ARRAY_SIZE(bcj_filters_decoders); + for (uint32_t i = 0; i < bcj_array_size; i++) { + if (lzma_filter_encoder_is_supported( + bcj_filters_decoders[i].id)) { + lzma_filter bcj_decoded = { + bcj_filters_decoders[i].id, NULL }; + + lzma_filter bcj_encoded = { + bcj_filters_decoders[i].id, NULL }; + + // First test without options + verify_filter_flags_decode(&bcj_encoded, + &bcj_decoded); + assert_true(bcj_decoded.options == NULL); + + // Next test with offset + lzma_options_bcj options = { + .start_offset = 257 + }; + + bcj_encoded.options = &options; + verify_filter_flags_decode(&bcj_encoded, + &bcj_decoded); + lzma_options_bcj *decoded_opts = bcj_decoded.options; + assert_uint_eq(decoded_opts->start_offset, + options.start_offset); + free(decoded_opts); + } + } + + if (lzma_filter_decoder_is_supported(LZMA_FILTER_DELTA) && + lzma_filter_encoder_is_supported(LZMA_FILTER_DELTA)) { + lzma_filter delta_decoded = { LZMA_FILTER_DELTA, NULL }; + + verify_filter_flags_decode(&delta_filter, &delta_decoded); + lzma_options_delta *expected = delta_filter.options; + lzma_options_delta *decoded = delta_decoded.options; + assert_uint_eq(expected->dist, decoded->dist); + assert_uint_eq(expected->type, decoded->type); + + free(decoded); + } + + // Test expected failing cases + uint8_t bad_encoded_filter[LZMA_BLOCK_HEADER_SIZE_MAX]; + lzma_filter bad_filter; + + // Filter ID outside of valid range + lzma_vli bad_filter_id = LZMA_FILTER_RESERVED_START; + size_t bad_encoded_out_pos = 0; + size_t in_pos = 0; + + assert_lzma_ret(lzma_vli_encode(bad_filter_id, NULL, + bad_encoded_filter, &bad_encoded_out_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_OK); + + assert_lzma_ret(lzma_filter_flags_decode(&bad_filter, NULL, + bad_encoded_filter, &in_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_DATA_ERROR); + + bad_encoded_out_pos = 0; + in_pos = 0; + + // Invalid Filter ID + bad_filter_id = 2; + bad_encoded_out_pos = 0; + in_pos = 0; + + assert_lzma_ret(lzma_vli_encode(bad_filter_id, NULL, + bad_encoded_filter, &bad_encoded_out_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_OK); + + // Next encode Size of Properties with the value of 0 + assert_lzma_ret(lzma_vli_encode(0, NULL, + bad_encoded_filter, &bad_encoded_out_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_OK); + + // Decode should fail on bad Filter ID + assert_lzma_ret(lzma_filter_flags_decode(&bad_filter, NULL, + bad_encoded_filter, &in_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_OPTIONS_ERROR); + bad_encoded_out_pos = 0; + in_pos = 0; + + // Outsize too small + // Encode the LZMA2 filter normally, but then set + // the out size when decoding as too small + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2) && + lzma_filter_decoder_is_supported(LZMA_FILTER_LZMA2)) { + uint32_t filter_flag_size = 0; + assert_lzma_ret(lzma_filter_flags_size(&filter_flag_size, + &lzma2_filter), LZMA_OK); + + assert_lzma_ret(lzma_filter_flags_encode(&lzma2_filter, + bad_encoded_filter, &bad_encoded_out_pos, + LZMA_BLOCK_HEADER_SIZE_MAX), LZMA_OK); + + assert_lzma_ret(lzma_filter_flags_decode(&bad_filter, NULL, + bad_encoded_filter, &in_pos, + filter_flag_size - 1), LZMA_DATA_ERROR); + } +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + +#ifdef HAVE_ENCODERS + // Only init filter options if encoder is supported because decoder + // tests requires encoder support, so the decoder tests will only + // run if for a given filter both the encoder and decoder are enabled. + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA1)) { + lzma_options_lzma *options = tuktest_malloc( + sizeof(lzma_options_lzma)); + lzma_lzma_preset(options, LZMA_PRESET_DEFAULT); + lzma1_filter.options = options; + } + + if (lzma_filter_encoder_is_supported(LZMA_FILTER_LZMA2)) { + lzma_options_lzma *options = tuktest_malloc( + sizeof(lzma_options_lzma)); + lzma_lzma_preset(options, LZMA_PRESET_DEFAULT); + lzma2_filter.options = options; + } + + if (lzma_filter_encoder_is_supported(LZMA_FILTER_DELTA)) { + lzma_options_delta *options = tuktest_malloc( + sizeof(lzma_options_delta)); + options->dist = LZMA_DELTA_DIST_MIN; + options->type = LZMA_DELTA_TYPE_BYTE; + delta_filter.options = options; + } +#endif + + tuktest_run(test_lzma_filter_flags_size); + tuktest_run(test_lzma_filter_flags_encode); + tuktest_run(test_lzma_filter_flags_decode); + return tuktest_end(); +} diff --git a/tests/test_filter_str.c b/tests/test_filter_str.c new file mode 100644 index 0000000..15aee55 --- /dev/null +++ b/tests/test_filter_str.c @@ -0,0 +1,593 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_filter_str.c +/// \brief Tests Filter string functions +// +// Author: Jia Tan +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + + +static void +test_lzma_str_to_filters(void) +{ + lzma_filter filters[LZMA_FILTERS_MAX + 1]; + int error_pos; + + // Test with NULL string. + assert_true(lzma_str_to_filters(NULL, &error_pos, filters, 0, + NULL) != NULL); + + // Test with NULL filter array. + assert_true(lzma_str_to_filters("lzma2", &error_pos, NULL, 0, + NULL) != NULL); + + // Test with unsupported flags. + assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, + UINT32_MAX, NULL) != NULL); + + assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, + LZMA_STR_NO_SPACES << 1, NULL) != NULL); + + assert_true(lzma_str_to_filters("lzma2", &error_pos, filters, + LZMA_STR_NO_SPACES, NULL) != NULL); + + // Test with empty string. + 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. + assert_true(lzma_str_to_filters("lzma2 abcd", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 6); + + assert_true(lzma_str_to_filters("lzma2--abcd", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 7); + + assert_true(lzma_str_to_filters("lzma2--", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 7); + + // 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. + assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, + 0, NULL) != NULL); + assert_int_eq(error_pos, 0); + + assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, + LZMA_STR_ALL_FILTERS, NULL) == NULL); + + // Verify Filters array IDs are correct. The array should contain + // only two elements: + // 1. LZMA1 Filter + // 2. LZMA_VLI_UNKNOWN filter array terminator + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA1); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); +#endif + + // Test LZMA_STR_NO_VALIDATION flag. This should allow having the + // 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. + assert_true(lzma_str_to_filters("lzma2 lzma2", &error_pos, filters, + 0, NULL) != NULL); + + assert_true(lzma_str_to_filters("lzma2 lzma2", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[2].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Should fail with invalid Filter options (lc + lp must be <= 4). + assert_true(lzma_str_to_filters("lzma2:lc=3,lp=3", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) != NULL); + + // Test invalid option name. + assert_true(lzma_str_to_filters("lzma2:foo=1,bar=2", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 6); + + // Test missing option value. + assert_true(lzma_str_to_filters("lzma2:lc=", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 9); + + 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. + assert_true(lzma_str_to_filters("-10", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 2); + + assert_true(lzma_str_to_filters("-5f", &error_pos, + filters, 0, NULL) != NULL); + assert_int_eq(error_pos, 2); + + // Test filter chain too long. + 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); + +#if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) + // Should fail with a Filter not supported in the .xz format (lzma1). + assert_true(lzma_str_to_filters("lzma1", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) != NULL); +#endif + + // Test setting options with the "=" format. + 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_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_options_lzma *opts = filters[0].options; + assert_uint_eq(opts->dict_size, 4096); + assert_uint_eq(opts->lc, 2); + assert_uint_eq(opts->lp, 2); + assert_uint_eq(opts->pb, 1); + assert_uint_eq(opts->mode, LZMA_MODE_FAST); + assert_uint_eq(opts->nice_len, 3); + assert_uint_eq(opts->mf, LZMA_MF_HC3); + assert_uint_eq(opts->depth, 10); + + lzma_filters_free(filters, NULL); + +#if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) + // Test BCJ Filter options. + assert_true(lzma_str_to_filters("x86:start=16", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_X86); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_options_bcj *bcj_opts = filters[0].options; + assert_uint_eq(bcj_opts->start_offset, 16); + + lzma_filters_free(filters, NULL); +#endif + +#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) + // Test Delta Filter options. + assert_true(lzma_str_to_filters("delta:dist=20", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_DELTA); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_options_delta *delta_opts = filters[0].options; + assert_uint_eq(delta_opts->dist, 20); + + lzma_filters_free(filters, NULL); +#endif + + // Test skipping leading spaces. + assert_true(lzma_str_to_filters(" lzma2", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Test skipping trailing spaces. + assert_true(lzma_str_to_filters("lzma2 ", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Test with "--" instead of space separating. + assert_true(lzma_str_to_filters("lzma2--lzma2", &error_pos, filters, + LZMA_STR_NO_VALIDATION, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[2].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Test preset with and without leading "-", and with "e". + assert_true(lzma_str_to_filters("-3", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + assert_true(lzma_str_to_filters("4", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + assert_true(lzma_str_to_filters("9e", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Test using a preset as an lzma2 option. + assert_true(lzma_str_to_filters("lzma2:preset=9e", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + lzma_filters_free(filters, NULL); + + // Test setting dictionary size with invalid modifier suffix. + assert_true(lzma_str_to_filters("lzma2:dict=4096ZiB", &error_pos, filters, + 0, NULL) != NULL); + + assert_true(lzma_str_to_filters("lzma2:dict=4096KiBs", &error_pos, filters, + 0, NULL) != NULL); + + // Test option that cannot have multiplier modifier. + assert_true(lzma_str_to_filters("lzma2:pb=1k", &error_pos, filters, + 0, NULL) != NULL); + + // Test option value too large. + assert_true(lzma_str_to_filters("lzma2:dict=4096GiB", &error_pos, filters, + 0, NULL) != NULL); + + // Test valid uses of multiplier modifiers (k,m,g). + assert_true(lzma_str_to_filters("lzma2:dict=4096KiB", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + opts = filters[0].options; + assert_uint_eq(opts->dict_size, 4096 << 10); + + lzma_filters_free(filters, NULL); + + assert_true(lzma_str_to_filters("lzma2:dict=40Mi", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + opts = filters[0].options; + assert_uint_eq(opts->dict_size, 40 << 20); + + lzma_filters_free(filters, NULL); + + assert_true(lzma_str_to_filters("lzma2:dict=1g", &error_pos, filters, + 0, NULL) == NULL); + + assert_uint_eq(filters[0].id, LZMA_FILTER_LZMA2); + assert_uint_eq(filters[1].id, LZMA_VLI_UNKNOWN); + + opts = filters[0].options; + assert_uint_eq(opts->dict_size, 1 << 30); + + lzma_filters_free(filters, NULL); +} + + +static void +test_lzma_str_from_filters(void) +{ + lzma_filter filters[LZMA_FILTERS_MAX]; + filters[0].id = LZMA_VLI_UNKNOWN; + + char *output_str = NULL; + + // Test basic NULL inputs. + assert_lzma_ret(lzma_str_from_filters(NULL, filters, 0, NULL), + LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_str_from_filters(&output_str, NULL, 0, NULL), + LZMA_PROG_ERROR); + + // Test with empty filters array. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, 0, NULL), + LZMA_OPTIONS_ERROR); + + // Create a simple filter array only containing an LZMA2 Filter. + assert_true(lzma_str_to_filters("lzma2", NULL, filters, 0, NULL) + == NULL); + + // Test with bad flags. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_ALL_FILTERS, NULL), LZMA_OPTIONS_ERROR); + + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_NO_VALIDATION, NULL), LZMA_OPTIONS_ERROR); + + // Test with no flags. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, 0, NULL), + LZMA_OK); + + assert_str_eq(output_str, "lzma2"); + free(output_str); + + // Test LZMA_STR_ENCODER flag. + // Only the the return value is checked since the actual string + // may change in the future (even though it is unlikely). + // The order of options or the inclusion of new options could + // cause a change in output, so we will avoid hardcoding an + // expected result. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_ENCODER, NULL), LZMA_OK); + free(output_str); + + // Test LZMA_STR_DECODER flag. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_DECODER, NULL), LZMA_OK); + free(output_str); + + // Test LZMA_STR_GETOPT_LONG flag. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_GETOPT_LONG, NULL), LZMA_OK); + free(output_str); + + // Test LZMA_STR_NO_SPACES flag. + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_NO_SPACES, NULL), LZMA_OK); + + // Check to be sure there are no spaces. + assert_true(strchr(output_str, ' ') == NULL); + + free(output_str); + + lzma_filters_free(filters, NULL); + +#if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) + assert_true(lzma_str_to_filters("x86 lzma2", NULL, filters, 0, NULL) + == NULL); + + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, 0, NULL), + LZMA_OK); + + assert_str_eq(output_str, "x86 lzma2"); + + 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), + LZMA_OK); + + assert_str_eq(output_str, "x86 lzma2"); + + lzma_filters_free(filters, NULL); + free(output_str); +#endif + + 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]; + + for (uint32_t i = 0; i < ARRAY_SIZE(oversized_filters) - 1; i++) { + oversized_filters[i].id = LZMA_FILTER_LZMA2; + oversized_filters[i].options = &opts; + } + + oversized_filters[LZMA_FILTERS_MAX + 1].id = LZMA_VLI_UNKNOWN; + oversized_filters[LZMA_FILTERS_MAX + 1].options = NULL; + + assert_lzma_ret(lzma_str_from_filters(&output_str, oversized_filters, + 0, NULL), LZMA_OPTIONS_ERROR); + + // Test with NULL filter options (when they cannot be NULL). + filters[0].id = LZMA_FILTER_LZMA2; + filters[0].options = NULL; + filters[1].id = LZMA_VLI_UNKNOWN; + + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_ENCODER, NULL), LZMA_OPTIONS_ERROR); + + // Test with bad Filter ID. + filters[0].id = LZMA_VLI_UNKNOWN - 1; + assert_lzma_ret(lzma_str_from_filters(&output_str, filters, + LZMA_STR_ENCODER, NULL), LZMA_OPTIONS_ERROR); +} + + +static const char supported_encoders[][9] = { + "lzma2", +#ifdef HAVE_ENCODER_X86 + "x86", +#endif +#ifdef HAVE_ENCODER_POWERPC + "powerpc", +#endif +#ifdef HAVE_ENCODER_IA64 + "ia64", +#endif +#ifdef HAVE_ENCODER_ARM + "arm", +#endif +#ifdef HAVE_ENCODER_ARMTHUMB + "armthumb", +#endif +#ifdef HAVE_ENCODER_SPARC + "sparc", +#endif +#ifdef HAVE_ENCODER_ARM64 + "arm64", +#endif +#ifdef HAVE_ENCODER_DELTA + "delta", +#endif +}; + +static const char supported_decoders[][9] = { + "lzma2", +#ifdef HAVE_DECODER_X86 + "x86", +#endif +#ifdef HAVE_DECODER_POWERPC + "powerpc", +#endif +#ifdef HAVE_DECODER_IA64 + "ia64", +#endif +#ifdef HAVE_DECODER_ARM + "arm", +#endif +#ifdef HAVE_DECODER_ARMTHUMB + "armthumb", +#endif +#ifdef HAVE_DECODER_SPARC + "sparc", +#endif +#ifdef HAVE_DECODER_ARM64 + "arm64", +#endif +#ifdef HAVE_DECODER_DELTA + "delta", +#endif +}; + +static const char supported_filters[][9] = { + "lzma2", +#if defined(HAVE_ENCODER_X86) || defined(HAVE_DECODER_X86) + "x86", +#endif +#if defined(HAVE_ENCODER_POWERPC) || defined(HAVE_DECODER_POWERPC) + "powerpc", +#endif +#if defined(HAVE_ENCODER_IA64) || defined(HAVE_DECODER_IA64) + "ia64", +#endif +#if defined(HAVE_ENCODER_ARM) || defined(HAVE_DECODER_ARM) + "arm", +#endif +#if defined(HAVE_ENCODER_ARMTHUMB) || defined(HAVE_DECODER_ARMTHUMB) + "armthumb", +#endif +#if defined(HAVE_ENCODER_SPARC) || defined(HAVE_DECODER_SPARC) + "sparc", +#endif +#if defined(HAVE_ENCODER_ARM64) || defined(HAVE_DECODER_ARM64) + "arm64", +#endif +#if defined(HAVE_ENCODER_DELTA) || defined(HAVE_DECODER_DELTA) + "delta", +#endif +}; + + +static void +test_lzma_str_list_filters(void) +{ + // Test with basic NULL inputs. + assert_lzma_ret(lzma_str_list_filters(NULL, LZMA_VLI_UNKNOWN, 0, + NULL), LZMA_PROG_ERROR); + + char *str = NULL; + + // Test with bad flags. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_NO_VALIDATION , NULL), LZMA_OPTIONS_ERROR); + + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_NO_SPACES, NULL), LZMA_OPTIONS_ERROR); + + // Test with bad Filter ID. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN - 1, + 0, NULL), LZMA_OPTIONS_ERROR); + + // Test LZMA_STR_ENCODER flag. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_ENCODER, NULL), LZMA_OK); + + for (uint32_t i = 0; i < ARRAY_SIZE(supported_encoders); i++) + assert_str_contains(str, supported_encoders[i]); + + free(str); + + // Test LZMA_STR_DECODER flag. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_DECODER, NULL), LZMA_OK); + + for (uint32_t i = 0; i < ARRAY_SIZE(supported_decoders); i++) + assert_str_contains(str, supported_decoders[i]); + + free(str); + + // Test LZMA_STR_GETOPT_LONG flag. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_GETOPT_LONG, NULL), LZMA_OK); + + free(str); + + // Test LZMA_STR_ALL_FILTERS flag. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + LZMA_STR_ALL_FILTERS, NULL), LZMA_OK); +#if defined(HAVE_ENCODER_LZMA1) || defined(HAVE_DECODER_LZMA1) + // With the flag, the string should contain the LZMA1 Filter. + assert_str_contains(str, "lzma1"); + + free(str); + + // If a non .xz filter is specified, it should still list the Filter. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_FILTER_LZMA1, + 0, NULL), LZMA_OK); + assert_str_eq(str, "lzma1"); +#endif + free(str); + + // Test with no flags. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_VLI_UNKNOWN, + 0, NULL), LZMA_OK); + + for (uint32_t i = 0; i < ARRAY_SIZE(supported_filters); i++) + assert_str_contains(str, supported_filters[i]); + + assert_str_doesnt_contain(str, "lzma1"); + + free(str); + + // Test providing a Filter ID. + assert_lzma_ret(lzma_str_list_filters(&str, LZMA_FILTER_LZMA2, + LZMA_STR_ALL_FILTERS, NULL), LZMA_OK); + assert_str_eq(str, "lzma2"); + + free(str); +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + + tuktest_run(test_lzma_str_to_filters); + tuktest_run(test_lzma_str_from_filters); + tuktest_run(test_lzma_str_list_filters); + + return tuktest_end(); +} diff --git a/tests/test_hardware.c b/tests/test_hardware.c new file mode 100644 index 0000000..c72d9b2 --- /dev/null +++ b/tests/test_hardware.c @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_hardware.c +/// \brief Tests src/liblzma/api/lzma/hardware.h API functions +/// +/// Since the output values of these functions are hardware dependent, these +/// tests are trivial. They are simply used to detect errors and machines +/// that these function are not supported on. +// +// Author: Jia Tan +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" +#include "mythread.h" + + +static void +test_lzma_physmem(void) +{ + // NOTE: Use _skip instead of _fail because 0 can also mean that we + // don't know how to get this information on this operating system. + if (lzma_physmem() == 0) + assert_skip("Could not determine amount of physical memory"); +} + + +static void +test_lzma_cputhreads(void) +{ +#ifndef MYTHREAD_ENABLED + assert_skip("Threading support disabled"); +#else + if (lzma_cputhreads() == 0) + assert_skip("Could not determine cpu core count"); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + tuktest_run(test_lzma_physmem); + tuktest_run(test_lzma_cputhreads); + return tuktest_end(); +} diff --git a/tests/test_index.c b/tests/test_index.c new file mode 100644 index 0000000..a14b33d --- /dev/null +++ b/tests/test_index.c @@ -0,0 +1,1715 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_index.c +/// \brief Tests functions handling the lzma_index structure +/// +/// \todo Implement tests for lzma_file_info_decoder +// +// 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" + +// liblzma internal header file needed for: +// UNPADDED_SIZE_MIN +// UNPADDED_SIZE_MAX +// vli_ceil4 +#include "common/index.h" + + +#define MEMLIMIT (UINT64_C(1) << 20) + +static uint8_t *decode_buffer; +static size_t decode_buffer_size = 0; +static lzma_index *decode_test_index; + + +static void +test_lzma_index_memusage(void) +{ + // The return value from lzma_index_memusage is an approximation + // of the amount of memory needed for lzma_index for a given + // amount of Streams and Blocks. It will be an upperbound, + // so this test will mostly sanity check and error check the + // function. + + // The maximum number of Streams should be UINT32_MAX in the + // current implementation even though the parameter is lzma_vli. + 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); + + // Number of Streams must be non-zero + assert_uint_eq(lzma_index_memusage(0, 1), UINT64_MAX); + + // Number of Blocks CAN be zero + assert_uint(lzma_index_memusage(1, 0), !=, UINT64_MAX); + + // Arbitrary values for Stream and Block should work without error + // and should always increase + uint64_t previous = 1; + lzma_vli streams = 1; + lzma_vli blocks = 1; + + // Test 100 different increasing values for Streams and Block + for (int i = 0; i < 100; i++) { + uint64_t current = lzma_index_memusage(streams, blocks); + assert_uint(current, >, previous); + previous = current; + streams += 29; + blocks += 107; + } + + // Force integer overflow in calculation (should result in an error) + assert_uint_eq(lzma_index_memusage(UINT32_MAX, LZMA_VLI_MAX), + UINT64_MAX); +} + + +static void +test_lzma_index_memused(void) +{ + // Very similar to test_lzma_index_memusage above since + // lzma_index_memused is essentially a wrapper for + // lzma_index_memusage + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test with empty Index + assert_uint(lzma_index_memused(idx), <, UINT64_MAX); + + // Append small Blocks and then test again (should pass). + for (lzma_vli i = 0; i < 10; i++) + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint(lzma_index_memused(idx), <, UINT64_MAX); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_append(void) +{ + // Basic input-ouput test done here. + // Less trivial tests for this function are done throughout + // other tests. + + // First test with NULL lzma_index + assert_lzma_ret(lzma_index_append(NULL, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test with invalid Unpadded Size + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN - 1, 1), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MAX + 1, 1), LZMA_PROG_ERROR); + + // Test with invalid Uncompressed Size + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MAX, LZMA_VLI_MAX + 1), + LZMA_PROG_ERROR); + + // Test expected successful Block appends + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, + 2), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, + 3), LZMA_OK); + + lzma_index_end(idx, NULL); + + // Test compressed .xz file size growing too large. This also tests + // a failing assert fixed in 68bda971bb8b666a009331455fcedb4e18d837a4. + // Should result in LZMA_DATA_ERROR. + idx = lzma_index_init(NULL); + + // The calculation for maximum unpadded size is to make room for the + // second stream when lzma_index_cat() is called. The + // 4 * LZMA_STREAM_HEADER_SIZE is for the header and footer of + // both streams. The extra 24 bytes are for the size of the indexes + // for both streams. This allows us to maximize the unpadded sum + // during the lzma_index_append() call after the indexes have been + // concatenated. + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MAX + - ((4 * LZMA_STREAM_HEADER_SIZE) + 24), 1), LZMA_OK); + + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_cat(second, idx, NULL), LZMA_OK); + + assert_lzma_ret(lzma_index_append(second, NULL, UNPADDED_SIZE_MAX, 1), + LZMA_DATA_ERROR); + + lzma_index_end(second, NULL); + + // Test uncompressed size growing too large. + // Should result in LZMA_DATA_ERROR. + idx = lzma_index_init(NULL); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_DATA_ERROR); + + lzma_index_end(idx, NULL); + + // Currently not testing for error case when the size of the Index + // grows too large to be stored. This was not practical to test for + // since too many Blocks needed to be created to cause this. +} + + +static void +test_lzma_index_stream_flags(void) +{ + // Only trivial tests done here testing for basic functionality. + // More in-depth testing for this function will be done in + // test_lzma_index_checks. + + // Testing for NULL inputs + assert_lzma_ret(lzma_index_stream_flags(NULL, NULL), + LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_lzma_ret(lzma_index_stream_flags(idx, NULL), + LZMA_PROG_ERROR); + + lzma_stream_flags stream_flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_checks(void) +{ + // Tests should still pass, even if some of the check types + // are disabled. + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_stream_flags stream_flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_NONE + }; + + // First set the check type to None + 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); + + // 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); + + // 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); + + // 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); + + // Create second lzma_index and cat to first + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Set the check type to CRC32 for the second lzma_index + stream_flags.check = LZMA_CHECK_CRC32; + assert_lzma_ret(lzma_index_stream_flags(second, &stream_flags), + LZMA_OK); + + assert_uint_eq(lzma_index_checks(second), + UINT32_C(1) << LZMA_CHECK_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)); + + // Change the check type of the second Stream to SHA256 + stream_flags.check = LZMA_CHECK_SHA256; + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + + // Index should now have only SHA256 + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_SHA256); + + // Test with a third Stream + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + stream_flags.check = LZMA_CHECK_CRC64; + assert_lzma_ret(lzma_index_stream_flags(third, &stream_flags), + LZMA_OK); + + assert_uint_eq(lzma_index_checks(third), + UINT32_C(1) << LZMA_CHECK_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_end(idx, NULL); +} + + +static void +test_lzma_index_stream_padding(void) +{ + // Test NULL lzma_index + assert_lzma_ret(lzma_index_stream_padding(NULL, 0), + LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test Stream Padding not a multiple of 4 + assert_lzma_ret(lzma_index_stream_padding(idx, 3), + LZMA_PROG_ERROR); + + // Test Stream Padding too large + assert_lzma_ret(lzma_index_stream_padding(idx, LZMA_VLI_MAX - 3), + LZMA_DATA_ERROR); + + // Test Stream Padding valid + assert_lzma_ret(lzma_index_stream_padding(idx, 0x1000), + LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 4), + LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 0), + LZMA_OK); + + // Test Stream Padding causing the file size to grow too large + assert_lzma_ret(lzma_index_append(idx, NULL, + LZMA_VLI_MAX - 0x1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 0x1000), + LZMA_DATA_ERROR); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_count(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_uint_eq(lzma_index_stream_count(idx), 1); + + // Appending Blocks should not change the Stream count value + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_OK); + + assert_uint_eq(lzma_index_stream_count(idx), 1); + + // Test with multiple Streams + for (uint32_t i = 0; i < 100; i++) { + lzma_index *idx_cat = lzma_index_init(NULL); + assert_true(idx != NULL); + assert_lzma_ret(lzma_index_cat(idx, idx_cat, NULL), LZMA_OK); + assert_uint_eq(lzma_index_stream_count(idx), i + 2); + } + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_block_count(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_uint_eq(lzma_index_block_count(idx), 0); + + const uint32_t iterations = 0x1000; + for (uint32_t i = 0; i < iterations; i++) { + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_uint_eq(lzma_index_block_count(idx), i + 1); + } + + // Create new lzma_index with a few Blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint_eq(lzma_index_block_count(second), 3); + + // Concatenate the lzma_indexes together and the result should have + // the sum of the two individual counts. + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + assert_uint_eq(lzma_index_block_count(idx), iterations + 3); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint_eq(lzma_index_block_count(idx), iterations + 4); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Base size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 0 bytes Records + // 2 bytes Index Padding + // 4 bytes CRC32 + // Total: 8 bytes + assert_uint_eq(lzma_index_size(idx), 8); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // New size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 2 bytes Records + // 0 bytes Index Padding + // 4 bytes CRC32 + // Total: 8 bytes + assert_uint_eq(lzma_index_size(idx), 8); + + assert_lzma_ret(lzma_index_append(idx, NULL, + LZMA_VLI_MAX / 4, LZMA_VLI_MAX / 4), LZMA_OK); + + // New size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 20 bytes Records + // 2 bytes Index Padding + // 4 bytes CRC32 + // Total: 28 bytes + assert_uint_eq(lzma_index_size(idx), 28); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Stream size calculated by: + // Size of Stream Header (12 bytes) + // Size of all Blocks + // Size of the Index + // Size of the Stream Footer (12 bytes) + + // First test with empty Index + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 0 bytes + // Size of Index - 8 bytes + // Size of Stream Footer - 12 bytes + // Total: 32 bytes + assert_uint_eq(lzma_index_stream_size(idx), 32); + + // 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); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 3000 bytes + // Size of Index - 16 bytes + // Size of Stream Footer - 12 bytes + // Total: 3040 bytes + assert_uint_eq(lzma_index_stream_size(idx), 3040); + + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_uint_eq(lzma_index_stream_size(second), 32); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 1), LZMA_OK); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 1000 bytes + // Size of Index - 12 bytes + // Size of Stream Footer - 12 bytes + // Total: 1036 bytes + assert_uint_eq(lzma_index_stream_size(second), 1036); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 4000 bytes + // Size of Index - 20 bytes + // Size of Stream Footer - 12 bytes + // Total: 4044 bytes + assert_uint_eq(lzma_index_stream_size(idx), 4044); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_total_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // First test empty lzma_index. + // Result should be 0 since no Blocks have been added. + assert_uint_eq(lzma_index_total_size(idx), 0); + + // Add a few Blocks and retest after each append + 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_uint_eq(lzma_index_total_size(idx), 2000); + + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 3000); + + // Create second lzma_index and append Blocks to it. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_uint_eq(lzma_index_total_size(second), 0); + + assert_lzma_ret(lzma_index_append(second, NULL, 100, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(second), 100); + + assert_lzma_ret(lzma_index_append(second, NULL, 100, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(second), 200); + + // Concatenate the Streams together + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // The resulting total size should be the size of all Blocks + // from both Streams + assert_uint_eq(lzma_index_total_size(idx), 3200); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_file_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Should be the same as test_lzma_index_stream_size with + // only one Stream and no Stream Padding. + 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_uint_eq(lzma_index_file_size(idx), 3040); + + // Next add Stream Padding + assert_lzma_ret(lzma_index_stream_padding(idx, 1000), + LZMA_OK); + + assert_uint_eq(lzma_index_file_size(idx), 4040); + + // Create second lzma_index. + // Very similar to test_lzma_index_stream_size, but + // the values should include the headers of the second Stream. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_stream_size(second), 1036); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // .xz file size should be: + // Size of 2 Stream Headers - 12 * 2 bytes + // Size of all Blocks - 3000 + 1000 bytes + // Size of 2 Indexes - 16 + 12 bytes + // Size of Stream Padding - 1000 bytes + // Size of 2 Stream Footers - 12 * 2 bytes + // Total: 5076 bytes + assert_uint_eq(lzma_index_file_size(idx), 5076); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_uncompressed_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Empty lzma_index should have 0 uncompressed .xz file size. + assert_uint_eq(lzma_index_uncompressed_size(idx), 0); + + // Append a few small Blocks + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 100), LZMA_OK); + + assert_uint_eq(lzma_index_uncompressed_size(idx), 111); + + // Create another lzma_index + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Append a few small Blocks + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 2), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 20), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 200), LZMA_OK); + + assert_uint_eq(lzma_index_uncompressed_size(second), 222); + + // Concatenate second lzma_index to first + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // New uncompressed .xz file size should be the sum of the two Streams + assert_uint_eq(lzma_index_uncompressed_size(idx), 333); + + // Append one more Block to the lzma_index and ensure that + // it is properly updated + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 111), LZMA_OK); + assert_uint_eq(lzma_index_uncompressed_size(idx), 444); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_iter_init(void) +{ + // Testing basic init functionality. + // The init function should call rewind on the iterator. + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 1); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 2); + + lzma_index_iter_init(&iter, first); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 3); + + lzma_index_end(first, NULL); +} + + +static void +test_lzma_index_iter_rewind(void) +{ + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + // Append 3 Blocks and iterate over each. This is to test + // the LZMA_INDEX_ITER_BLOCK mode. + for (uint32_t i = 0; i < 3; i++) { + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.number_in_file, i + 1); + } + + // Rewind back to the beginning and iterate over the Blocks again + lzma_index_iter_rewind(&iter); + + // Should be able to re-iterate over the Blocks again. + for (uint32_t i = 0; i < 3; i++) { + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.number_in_file, i + 1); + } + + // Next concatenate two more lzma_indexes, iterate over them, + // rewind, and iterate over them again. This is to test + // the LZMA_INDEX_ITER_STREAM mode. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + + assert_uint_eq(iter.stream.number, 3); + + lzma_index_iter_rewind(&iter); + + for (uint32_t i = 0; i < 3; i++) { + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, i + 1); + } + + lzma_index_end(first, NULL); +} + + +static void +test_lzma_index_iter_next(void) +{ + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + // First test bad mode values + for (uint32_t i = LZMA_INDEX_ITER_NONEMPTY_BLOCK + 1; i < 100; i++) + assert_true(lzma_index_iter_next(&iter, i)); + + // Test iterating over Blocks + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN * 2, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN * 3, 100), LZMA_OK); + + // For Blocks, need to verify: + // - number_in_file (overall Block number) + // - compressed_file_offset + // - uncompressed_file_offset + // - number_in_stream (Block number relative to current Stream) + // - compressed_stream_offset + // - uncompressed_stream_offset + // - uncompressed_size + // - unpadded_size + // - total_size + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 1); + + // Should start right after the Stream Header + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + assert_uint_eq(iter.block.number_in_stream, 1); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_stream_offset, 0); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN); + assert_uint_eq(iter.block.total_size, vli_ceil4(UNPADDED_SIZE_MIN)); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 2); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN)); + assert_uint_eq(iter.block.uncompressed_file_offset, 1); + assert_uint_eq(iter.block.number_in_stream, 2); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN)); + assert_uint_eq(iter.block.uncompressed_stream_offset, 1); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN * 2); + assert_uint_eq(iter.block.total_size, vli_ceil4(UNPADDED_SIZE_MIN * 2)); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 3); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2)); + assert_uint_eq(iter.block.uncompressed_file_offset, 11); + assert_uint_eq(iter.block.number_in_stream, 3); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2)); + assert_uint_eq(iter.block.uncompressed_stream_offset, 11); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN * 3); + assert_uint_eq(iter.block.total_size, + vli_ceil4(UNPADDED_SIZE_MIN * 3)); + + // Only three Blocks were added, so this should return true + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + const lzma_vli second_stream_compressed_start = + LZMA_STREAM_HEADER_SIZE * 2 + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2) + + vli_ceil4(UNPADDED_SIZE_MIN * 3) + + lzma_index_size(first); + const lzma_vli second_stream_uncompressed_start = 1 + 10 + 100; + + // Test iterating over Streams. + // The second Stream will have 0 Blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Set Stream Flags for Stream 2 + lzma_stream_flags flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + + assert_lzma_ret(lzma_index_stream_flags(second, &flags), LZMA_OK); + + // The Second stream will have 8 bytes of Stream Padding + assert_lzma_ret(lzma_index_stream_padding(second, 8), LZMA_OK); + + const lzma_vli second_stream_index_size = lzma_index_size(second); + + // The third Stream will have 2 Blocks + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_append(third, NULL, 32, 20), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, 64, 40), LZMA_OK); + + const lzma_vli third_stream_index_size = lzma_index_size(third); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + // For Streams, need to verify: + // - flags (Stream Flags) + // - number (Stream count) + // - block_count + // - compressed_offset + // - uncompressed_offset + // - compressed_size + // - uncompressed_size + // - padding (Stream Padding) + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Verify Stream + assert_uint_eq(iter.stream.flags->backward_size, + LZMA_BACKWARD_SIZE_MIN); + assert_uint_eq(iter.stream.flags->check, LZMA_CHECK_CRC32); + assert_uint_eq(iter.stream.number, 2); + assert_uint_eq(iter.stream.block_count, 0); + assert_uint_eq(iter.stream.compressed_offset, + second_stream_compressed_start); + assert_uint_eq(iter.stream.uncompressed_offset, + second_stream_uncompressed_start); + assert_uint_eq(iter.stream.compressed_size, + LZMA_STREAM_HEADER_SIZE * 2 + + second_stream_index_size); + assert_uint_eq(iter.stream.uncompressed_size, 0); + assert_uint_eq(iter.stream.padding, 8); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Verify Stream + const lzma_vli third_stream_compressed_start = + second_stream_compressed_start + + LZMA_STREAM_HEADER_SIZE * 2 + + 8 + // Stream padding + second_stream_index_size; + const lzma_vli third_stream_uncompressed_start = + second_stream_uncompressed_start; + + assert_uint_eq(iter.stream.number, 3); + assert_uint_eq(iter.stream.block_count, 2); + assert_uint_eq(iter.stream.compressed_offset, + third_stream_compressed_start); + assert_uint_eq(iter.stream.uncompressed_offset, + third_stream_uncompressed_start); + assert_uint_eq(iter.stream.compressed_size, + LZMA_STREAM_HEADER_SIZE * 2 + + 96 + // Total compressed size + third_stream_index_size); + assert_uint_eq(iter.stream.uncompressed_size, 60); + assert_uint_eq(iter.stream.padding, 0); + + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Even after a failing call to next with ITER_STREAM mode, + // should still be able to iterate over the 2 Blocks in + // Stream 3. + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // 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. + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Rewind to test LZMA_INDEX_ITER_ANY + lzma_index_iter_rewind(&iter); + + // Iterate past the first three Blocks + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Iterate past the next Stream + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Iterate past the next Stream + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Last call should fail + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Rewind to test LZMA_INDEX_ITER_NONEMPTY_BLOCK + lzma_index_iter_rewind(&iter); + + // Iterate past the first three Blocks + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Skip past the next Stream which has no Blocks. + // We will get to the first Block of the third Stream. + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Iterate past the second (the last) Block in the third Stream + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Last call should fail since there is nothing left to iterate over. + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + lzma_index_end(first, NULL); +} + + +static void +test_lzma_index_iter_locate(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, idx); + + // Cannot locate anything from an empty Index. + assert_true(lzma_index_iter_locate(&iter, 0)); + assert_true(lzma_index_iter_locate(&iter, 555)); + + // One empty Record: nothing is found since there's no uncompressed + // data. + assert_lzma_ret(lzma_index_append(idx, NULL, 16, 0), LZMA_OK); + assert_true(lzma_index_iter_locate(&iter, 0)); + + // Non-empty Record and we can find something. + assert_lzma_ret(lzma_index_append(idx, NULL, 32, 5), LZMA_OK); + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + // Still cannot find anything past the end. + assert_true(lzma_index_iter_locate(&iter, 5)); + + // Add the third Record. + assert_lzma_ret(lzma_index_append(idx, NULL, 40, 11), LZMA_OK); + + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_false(lzma_index_iter_locate(&iter, 2)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_locate(&iter, 5)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_false(lzma_index_iter_locate(&iter, 5 + 11 - 1)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_true(lzma_index_iter_locate(&iter, 5 + 11)); + assert_true(lzma_index_iter_locate(&iter, 5 + 15)); + + // Large Index + lzma_index_end(idx, NULL); + idx = lzma_index_init(NULL); + assert_true(idx != NULL); + 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), + 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.total_size, 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, 4); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_locate(&iter, 3)); + 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, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + // Second Record + assert_false(lzma_index_iter_locate(&iter, 4)); + 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, + LZMA_STREAM_HEADER_SIZE + 4 + 8); + assert_uint_eq(iter.block.uncompressed_file_offset, 4); + + // Last Record + assert_false(lzma_index_iter_locate( + &iter, lzma_index_uncompressed_size(idx) - 1)); + 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, + lzma_index_total_size(idx) + + LZMA_STREAM_HEADER_SIZE - 4 * 5555 - 8); + assert_uint_eq(iter.block.uncompressed_file_offset, + lzma_index_uncompressed_size(idx) - 4 * 5555); + + // Allocation chunk boundaries. See INDEX_GROUP_SIZE in + // liblzma/common/index.c. + const uint32_t group_multiple = 256 * 4; + const uint32_t radius = 8; + const uint32_t start = group_multiple - radius; + lzma_vli ubase = 0; + lzma_vli tbase = 0; + uint32_t n; + for (n = 1; n < start; ++n) { + ubase += n * 4; + tbase += n * 4 + 8; + } + + while (n < start + 2 * radius) { + assert_false(lzma_index_iter_locate(&iter, ubase + n * 4)); + + assert_uint_eq(iter.block.compressed_file_offset, + tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, + ubase + n * 4); + + tbase += n * 4 + 8; + ubase += n * 4; + ++n; + + assert_uint_eq(iter.block.total_size, n * 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, n * 4); + } + + // Do it also backwards. + while (n > start) { + assert_false(lzma_index_iter_locate( + &iter, ubase + (n - 1) * 4)); + + assert_uint_eq(iter.block.total_size, n * 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, n * 4); + + --n; + tbase -= n * 4 + 8; + ubase -= n * 4; + + assert_uint_eq(iter.block.compressed_file_offset, + tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, + ubase + n * 4); + } + + // Test locating in concatenated Index. + lzma_index_end(idx, NULL); + idx = lzma_index_init(NULL); + assert_true(idx != NULL); + lzma_index_iter_init(&iter, idx); + 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); + assert_uint_eq(iter.block.uncompressed_size, 1); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + group_multiple * 8); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_cat(void) +{ + // Most complex tests for this function are done in other tests. + // This will mostly test basic functionality. + + lzma_index *dest = lzma_index_init(NULL); + assert_true(dest != NULL); + + lzma_index *src = lzma_index_init(NULL); + assert_true(src != NULL); + + // First test NULL dest or src + assert_lzma_ret(lzma_index_cat(NULL, NULL, NULL), LZMA_PROG_ERROR); + 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 + 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); + + dest = lzma_index_init(NULL); + assert_true(dest != NULL); + + src = lzma_index_init(NULL); + assert_true(src != NULL); + + assert_lzma_ret(lzma_index_append(dest, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(src, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + assert_lzma_ret(lzma_index_cat(dest, src, NULL), LZMA_DATA_ERROR); + + lzma_index_end(dest, NULL); + lzma_index_end(src, NULL); +} + + +// Helper function for test_lzma_index_dup(). +static bool +index_is_equal(const lzma_index *a, const lzma_index *b) +{ + // Compare only the Stream and Block sizes and offsets. + lzma_index_iter ra, rb; + lzma_index_iter_init(&ra, a); + lzma_index_iter_init(&rb, b); + + while (true) { + bool reta = lzma_index_iter_next(&ra, LZMA_INDEX_ITER_ANY); + bool retb = lzma_index_iter_next(&rb, LZMA_INDEX_ITER_ANY); + + // If both iterators finish at the same time, then the Indexes + // are identical. + if (reta) + return retb; + + if (ra.stream.number != rb.stream.number + || ra.stream.block_count + != rb.stream.block_count + || ra.stream.compressed_offset + != rb.stream.compressed_offset + || ra.stream.uncompressed_offset + != rb.stream.uncompressed_offset + || ra.stream.compressed_size + != rb.stream.compressed_size + || ra.stream.uncompressed_size + != rb.stream.uncompressed_size + || ra.stream.padding + != rb.stream.padding) + return false; + + if (ra.stream.block_count == 0) + continue; + + if (ra.block.number_in_file != rb.block.number_in_file + || ra.block.compressed_file_offset + != rb.block.compressed_file_offset + || ra.block.uncompressed_file_offset + != rb.block.uncompressed_file_offset + || ra.block.number_in_stream + != rb.block.number_in_stream + || ra.block.compressed_stream_offset + != rb.block.compressed_stream_offset + || ra.block.uncompressed_stream_offset + != rb.block.uncompressed_stream_offset + || ra.block.uncompressed_size + != rb.block.uncompressed_size + || ra.block.unpadded_size + != rb.block.unpadded_size + || ra.block.total_size + != rb.block.total_size) + return false; + } +} + + +// Allocator that succeeds for the first two allocation but fails the rest. +static void * +my_alloc(void *opaque, size_t a, size_t b) +{ + (void)opaque; + + static unsigned count = 0; + if (++count > 2) + return NULL; + + return malloc(a * b); +} + +static const lzma_allocator test_index_dup_alloc = { &my_alloc, NULL, NULL }; + + +static void +test_lzma_index_dup(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test for the bug fix 21515d79d778b8730a434f151b07202d52a04611: + // liblzma: Fix lzma_index_dup() for empty Streams. + assert_lzma_ret(lzma_index_stream_padding(idx, 4), LZMA_OK); + lzma_index *copy = lzma_index_dup(idx, NULL); + assert_true(copy != NULL); + assert_true(index_is_equal(idx, copy)); + lzma_index_end(copy, NULL); + + // Test for the bug fix 3bf857edfef51374f6f3fffae3d817f57d3264a0: + // liblzma: Fix a memory leak in error path of lzma_index_dup(). + // Use Valgrind to see that there are no leaks. + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); + + assert_true(lzma_index_dup(idx, &test_index_dup_alloc) == NULL); + + // Test a few streams and blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_stream_padding(second, 16), LZMA_OK); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 10, 40), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 20, 400), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 30, 4000), LZMA_OK); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(idx, third, NULL), LZMA_OK); + + copy = lzma_index_dup(idx, NULL); + assert_true(copy != NULL); + assert_true(index_is_equal(idx, copy)); + + lzma_index_end(copy, NULL); + 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, + const size_t buffer_size) +{ + lzma_index_iter iter; + lzma_index_iter_init(&iter, idx); + + size_t buffer_pos = 0; + + // Verify Index Indicator + assert_uint_eq(buffer[buffer_pos++], 0); + + // Get Number of Records + lzma_vli number_of_records = 0; + lzma_vli block_count = 0; + assert_lzma_ret(lzma_vli_decode(&number_of_records, NULL, buffer, + &buffer_pos, buffer_size), LZMA_OK); + + while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)) { + // Verify each Record (Unpadded Size, then Uncompressed Size). + // Verify Unpadded Size. + lzma_vli unpadded_size, uncompressed_size; + assert_lzma_ret(lzma_vli_decode(&unpadded_size, + NULL, buffer, &buffer_pos, + buffer_size), LZMA_OK); + assert_uint_eq(unpadded_size, + iter.block.unpadded_size); + + // Verify Uncompressed Size + assert_lzma_ret(lzma_vli_decode(&uncompressed_size, + NULL, buffer, &buffer_pos, + buffer_size), LZMA_OK); + assert_uint_eq(uncompressed_size, + iter.block.uncompressed_size); + + block_count++; + } + + // Verify Number of Records + assert_uint_eq(number_of_records, block_count); + + // Verify Index Padding + for (; buffer_pos % 4 != 0; buffer_pos++) + assert_uint_eq(buffer[buffer_pos], 0); + + // Verify CRC32 + uint32_t crc32 = lzma_crc32(buffer, buffer_pos, 0); + assert_uint_eq(read32le(buffer + buffer_pos), crc32); +} + + +// In a few places the Index size is needed as a size_t but lzma_index_size() +// returns lzma_vli. +static size_t +get_index_size(const lzma_index *idx) +{ + const lzma_vli size = lzma_index_size(idx); + assert_uint(size, <, SIZE_MAX); + return (size_t)size; +} +#endif + + +static void +test_lzma_index_encoder(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_stream strm = LZMA_STREAM_INIT; + + // First do basic NULL checks + assert_lzma_ret(lzma_index_encoder(NULL, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_encoder(&strm, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_encoder(NULL, idx), LZMA_PROG_ERROR); + + // Append three small Blocks + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); + + // Encode this lzma_index into a buffer + size_t buffer_size = get_index_size(idx); + uint8_t *buffer = tuktest_malloc(buffer_size); + + assert_lzma_ret(lzma_index_encoder(&strm, idx), LZMA_OK); + + strm.avail_out = buffer_size; + strm.next_out = buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint_eq(strm.avail_out, 0); + + lzma_end(&strm); + + verify_index_buffer(idx, buffer, buffer_size); + + // Test with multiple Streams concatenated into 1 Index + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Include 1 Block + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN * 4, 20), LZMA_OK); + + // Include Stream Padding + assert_lzma_ret(lzma_index_stream_padding(second, 16), LZMA_OK); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + buffer_size = get_index_size(idx); + buffer = tuktest_malloc(buffer_size); + assert_lzma_ret(lzma_index_encoder(&strm, idx), LZMA_OK); + + strm.avail_out = buffer_size; + strm.next_out = buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint_eq(strm.avail_out, 0); + + verify_index_buffer(idx, buffer, buffer_size); + + lzma_index_end(idx, NULL); + lzma_end(&strm); +#endif +} + +static void +generate_index_decode_buffer(void) +{ +#ifdef HAVE_ENCODERS + decode_test_index = lzma_index_init(NULL); + if (decode_test_index == NULL) + return; + + // 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; + + size_t size = 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; +#endif +} + + +#ifdef HAVE_DECODERS +static void +decode_index(const uint8_t *buffer, const size_t size, lzma_stream *strm, + lzma_ret expected_error) +{ + strm->avail_in = size; + strm->next_in = buffer; + assert_lzma_ret(lzma_code(strm, LZMA_FINISH), expected_error); +} +#endif + + +static void +test_lzma_index_decoder(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + if (decode_buffer_size == 0) + assert_skip("Could not initialize decode test buffer"); + + lzma_stream strm = LZMA_STREAM_INIT; + + assert_lzma_ret(lzma_index_decoder(NULL, NULL, MEMLIMIT), + 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); + + // Do actual decode + lzma_index *idx; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + + decode_index(decode_buffer, decode_buffer_size, &strm, + LZMA_STREAM_END); + + // Compare results with expected + assert_true(index_is_equal(decode_test_index, idx)); + + lzma_index_end(idx, NULL); + + // Test again with too low memory limit + assert_lzma_ret(lzma_index_decoder(&strm, &idx, 0), LZMA_OK); + + decode_index(decode_buffer, decode_buffer_size, &strm, + LZMA_MEMLIMIT_ERROR); + + uint8_t *corrupt_buffer = tuktest_malloc(decode_buffer_size); + memcpy(corrupt_buffer, decode_buffer, decode_buffer_size); + + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + + // First corrupt the Index Indicator + corrupt_buffer[0] ^= 1; + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[0] ^= 1; + + // Corrupt something in the middle of Index + corrupt_buffer[decode_buffer_size / 2] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size / 2] ^= 1; + + // Corrupt CRC32 + corrupt_buffer[decode_buffer_size - 1] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size - 1] ^= 1; + + // Corrupt Index Padding by setting it to non-zero + corrupt_buffer[decode_buffer_size - 5] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size - 1] ^= 1; + + lzma_end(&strm); +#endif +} + + +static void +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 + // around lzma_index_encoder() anyway. + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); + + size_t buffer_size = get_index_size(idx); + uint8_t *buffer = tuktest_malloc(buffer_size); + size_t out_pos = 1; + + // First test bad arguments + assert_lzma_ret(lzma_index_buffer_encode(NULL, NULL, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, NULL, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + 0), LZMA_PROG_ERROR); + out_pos = 0; + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + 1), LZMA_BUF_ERROR); + + // Do encoding + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + buffer_size), LZMA_OK); + assert_uint_eq(out_pos, buffer_size); + + // Validate results + verify_index_buffer(idx, buffer, buffer_size); + + lzma_index_end(idx, NULL); +#endif +} + + +static void +test_lzma_index_buffer_decode(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + if (decode_buffer_size == 0) + assert_skip("Could not initialize decode test buffer"); + + // Simple test since test_lzma_index_decoder() covers most of the + // lzma_index_buffer_decode() code anyway. + + // First test NULL checks + 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; + + assert_lzma_ret(lzma_index_buffer_decode(&idx, NULL, NULL, NULL, + NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + NULL, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, 0), LZMA_DATA_ERROR); + + in_pos = 1; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, 0), LZMA_PROG_ERROR); + in_pos = 0; + + // Test expected successful decode + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size), LZMA_OK); + + assert_true(index_is_equal(decode_test_index, idx)); + + lzma_index_end(idx, NULL); + + // Test too small memlimit + in_pos = 0; + memlimit = 1; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size), + LZMA_MEMLIMIT_ERROR); + assert_uint(memlimit, >, 1); + assert_uint(memlimit, <, MEMLIMIT); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + generate_index_decode_buffer(); + tuktest_run(test_lzma_index_memusage); + tuktest_run(test_lzma_index_memused); + tuktest_run(test_lzma_index_append); + tuktest_run(test_lzma_index_stream_flags); + tuktest_run(test_lzma_index_checks); + tuktest_run(test_lzma_index_stream_padding); + tuktest_run(test_lzma_index_stream_count); + tuktest_run(test_lzma_index_block_count); + tuktest_run(test_lzma_index_size); + tuktest_run(test_lzma_index_stream_size); + tuktest_run(test_lzma_index_total_size); + tuktest_run(test_lzma_index_file_size); + tuktest_run(test_lzma_index_uncompressed_size); + tuktest_run(test_lzma_index_iter_init); + tuktest_run(test_lzma_index_iter_rewind); + tuktest_run(test_lzma_index_iter_next); + tuktest_run(test_lzma_index_iter_locate); + tuktest_run(test_lzma_index_cat); + tuktest_run(test_lzma_index_dup); + tuktest_run(test_lzma_index_encoder); + tuktest_run(test_lzma_index_decoder); + tuktest_run(test_lzma_index_buffer_encode); + tuktest_run(test_lzma_index_buffer_decode); + lzma_index_end(decode_test_index, NULL); + return tuktest_end(); +} diff --git a/tests/test_index_hash.c b/tests/test_index_hash.c new file mode 100644 index 0000000..f3c6e8f --- /dev/null +++ b/tests/test_index_hash.c @@ -0,0 +1,386 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_index_hash.c +/// \brief Tests src/liblzma/common/index_hash.c API functions +/// +/// \note No test included for lzma_index_hash_end since it +/// would be trivial unless tested for memory leaks +/// with something like valgrind +// +// Author: Jia Tan +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + +// Needed for UNPADDED_SIZE_MIN and UNPADDED_SIZE_MAX macro definitions +// and index_size and vli_ceil4 helper functions +#include "common/index.h" + + +static void +test_lzma_index_hash_init(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + // First test with NULL index_hash. + // This should create a fresh index_hash. + lzma_index_hash *index_hash = lzma_index_hash_init(NULL, NULL); + assert_true(index_hash != NULL); + + // Next test with non-NULL index_hash. + lzma_index_hash *second_hash = lzma_index_hash_init(index_hash, NULL); + + // It should not create a new index_hash pointer. + // Instead it must just re-init the first index_hash. + assert_true(index_hash == second_hash); + + lzma_index_hash_end(index_hash, NULL); +#endif +} + + +static void +test_lzma_index_hash_append(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + // Test all invalid parameters + assert_lzma_ret(lzma_index_hash_append(NULL, 0, 0), + LZMA_PROG_ERROR); + + // Test NULL index_hash + assert_lzma_ret(lzma_index_hash_append(NULL, UNPADDED_SIZE_MIN, + LZMA_VLI_MAX), LZMA_PROG_ERROR); + + // Test with invalid Unpadded Size + lzma_index_hash *index_hash = lzma_index_hash_init(NULL, NULL); + assert_true(index_hash != NULL); + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN - 1, LZMA_VLI_MAX), + LZMA_PROG_ERROR); + + // Test with invalid Uncompressed Size + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX + 1), + LZMA_PROG_ERROR); + + // First append a Record describing a small Block. + // This should succeed. + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // Append another small Record. + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // Append a Record that would cause the compressed size to grow + // too big + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MAX, 1), LZMA_DATA_ERROR); + + lzma_index_hash_end(index_hash, NULL); +#endif +} + + +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +// Fill an index_hash with unpadded and uncompressed VLIs +// by calling lzma_index_hash_append +static void +fill_index_hash(lzma_index_hash *index_hash, const lzma_vli *unpadded_sizes, + const lzma_vli *uncomp_sizes, uint32_t block_count) +{ + for (uint32_t i = 0; i < block_count; ++i) + assert_lzma_ret(lzma_index_hash_append(index_hash, + unpadded_sizes[i], uncomp_sizes[i]), LZMA_OK); +} + + +// Set the contents of buf to the expected Index based on the +// .xz specification. This needs the unpadded and uncompressed VLIs +// to correctly create the Index. +static void +generate_index(uint8_t *buf, const lzma_vli *unpadded_sizes, + const lzma_vli *uncomp_sizes, uint32_t block_count, + size_t index_max_size) +{ + size_t in_pos = 0; + size_t out_pos = 0; + + // First set Index Indicator + buf[out_pos++] = INDEX_INDICATOR; + + // Next write out Number of Records + assert_lzma_ret(lzma_vli_encode(block_count, &in_pos, buf, + &out_pos, index_max_size), LZMA_STREAM_END); + + // Next write out each Record. + // A Record consists of Unpadded Size and Uncompressed Size + // written next to each other as VLIs. + for (uint32_t i = 0; i < block_count; ++i) { + in_pos = 0; + assert_lzma_ret(lzma_vli_encode(unpadded_sizes[i], &in_pos, + buf, &out_pos, index_max_size), LZMA_STREAM_END); + in_pos = 0; + assert_lzma_ret(lzma_vli_encode(uncomp_sizes[i], &in_pos, + buf, &out_pos, index_max_size), LZMA_STREAM_END); + } + + // Add Index Padding + lzma_vli rounded_out_pos = vli_ceil4(out_pos); + memzero(buf + out_pos, rounded_out_pos - out_pos); + out_pos = rounded_out_pos; + + // Add the CRC32 + write32le(buf + out_pos, lzma_crc32(buf, out_pos, 0)); + out_pos += 4; + + assert_uint_eq(out_pos, index_max_size); +} +#endif + + +static void +test_lzma_index_hash_decode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + lzma_index_hash *index_hash = lzma_index_hash_init(NULL, NULL); + assert_true(index_hash != NULL); + + size_t in_pos = 0; + + // Six valid values for the Unpadded Size fields in an Index + const lzma_vli unpadded_sizes[6] = { + UNPADDED_SIZE_MIN, + 1000, + 4000, + 8000, + 16000, + 32000 + }; + + // Six valid values for the Uncompressed Size fields in an Index + const lzma_vli uncomp_sizes[6] = { + 1, + 500, + 8000, + 20, + 1, + 500 + }; + + // Add two Records to an index_hash + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 2); + + const lzma_vli size_two_records = lzma_index_hash_size(index_hash); + assert_uint(size_two_records, >, 0); + uint8_t *index_two_records = tuktest_malloc(size_two_records); + + generate_index(index_two_records, unpadded_sizes, uncomp_sizes, 2, + size_two_records); + + // First test for basic buffer size error + in_pos = size_two_records + 1; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_two_records, &in_pos, + size_two_records), LZMA_BUF_ERROR); + + // Next test for invalid Index Indicator + in_pos = 0; + index_two_records[0] ^= 1; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_two_records, &in_pos, + size_two_records), LZMA_DATA_ERROR); + index_two_records[0] ^= 1; + + // Next verify the index_hash as expected + in_pos = 0; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_two_records, &in_pos, + size_two_records), LZMA_STREAM_END); + + // Next test an index_hash with three Records + index_hash = lzma_index_hash_init(index_hash, NULL); + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 3); + + const lzma_vli size_three_records = lzma_index_hash_size( + index_hash); + assert_uint(size_three_records, >, 0); + uint8_t *index_three_records = tuktest_malloc(size_three_records); + + generate_index(index_three_records, unpadded_sizes, uncomp_sizes, + 3, size_three_records); + + in_pos = 0; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_three_records, &in_pos, + size_three_records), LZMA_STREAM_END); + + // Next test an index_hash with five Records + index_hash = lzma_index_hash_init(index_hash, NULL); + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 5); + + const lzma_vli size_five_records = lzma_index_hash_size( + index_hash); + assert_uint(size_five_records, >, 0); + uint8_t *index_five_records = tuktest_malloc(size_five_records); + + generate_index(index_five_records, unpadded_sizes, uncomp_sizes, 5, + size_five_records); + + // Instead of testing all input at once, give input + // one byte at a time + in_pos = 0; + for (lzma_vli i = 0; i < size_five_records - 1; ++i) { + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_five_records, &in_pos, in_pos + 1), + LZMA_OK); + } + + // Last byte should return LZMA_STREAM_END + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_five_records, &in_pos, + in_pos + 1), LZMA_STREAM_END); + + // Next test if the index_hash is given an incorrect Unpadded + // Size. Should detect and report LZMA_DATA_ERROR + index_hash = lzma_index_hash_init(index_hash, NULL); + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 5); + // The sixth Record will have an invalid Unpadded Size + assert_lzma_ret(lzma_index_hash_append(index_hash, + unpadded_sizes[5] + 1, + uncomp_sizes[5]), LZMA_OK); + + const lzma_vli size_six_records = lzma_index_hash_size( + index_hash); + + assert_uint(size_six_records, >, 0); + uint8_t *index_six_records = tuktest_malloc(size_six_records); + + generate_index(index_six_records, unpadded_sizes, uncomp_sizes, 6, + size_six_records); + in_pos = 0; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_six_records, &in_pos, + size_six_records), LZMA_DATA_ERROR); + + // Next test if the Index is corrupt (invalid CRC32). + // Should detect and report LZMA_DATA_ERROR + index_hash = lzma_index_hash_init(index_hash, NULL); + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 2); + + index_two_records[size_two_records - 1] ^= 1; + + in_pos = 0; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_two_records, &in_pos, + size_two_records), LZMA_DATA_ERROR); + + // Next test with Index and index_hash struct not matching + // a Record + index_hash = lzma_index_hash_init(index_hash, NULL); + fill_index_hash(index_hash, unpadded_sizes, uncomp_sizes, 2); + // Recalculate Index with invalid Unpadded Size + const lzma_vli unpadded_sizes_invalid[2] = { + unpadded_sizes[0], + unpadded_sizes[1] + 1 + }; + + generate_index(index_two_records, unpadded_sizes_invalid, + uncomp_sizes, 2, size_two_records); + + in_pos = 0; + assert_lzma_ret(lzma_index_hash_decode(index_hash, + index_two_records, &in_pos, + size_two_records), LZMA_DATA_ERROR); + + lzma_index_hash_end(index_hash, NULL); +#endif +} + + +static void +test_lzma_index_hash_size(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + lzma_index_hash *index_hash = lzma_index_hash_init(NULL, NULL); + assert_true(index_hash != NULL); + + // First test empty index_hash + // Expected size should be: + // Index Indicator - 1 byte + // Number of Records - 1 byte + // List of Records - 0 bytes + // Index Padding - 2 bytes + // CRC32 - 4 bytes + // Total - 8 bytes + assert_uint_eq(lzma_index_hash_size(index_hash), 8); + + // Append a Record describing a small Block to the index_hash + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // Expected size should be: + // Index Indicator - 1 byte + // Number of Records - 1 byte + // List of Records - 2 bytes + // Index Padding - 0 bytes + // CRC32 - 4 bytes + // Total - 8 bytes + lzma_vli expected_size = 8; + assert_uint_eq(lzma_index_hash_size(index_hash), expected_size); + + // Append additional small Record + assert_lzma_ret(lzma_index_hash_append(index_hash, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // Expected size should be: + // Index Indicator - 1 byte + // Number of Records - 1 byte + // List of Records - 4 bytes + // Index Padding - 2 bytes + // CRC32 - 4 bytes + // Total - 12 bytes + expected_size = 12; + assert_uint_eq(lzma_index_hash_size(index_hash), expected_size); + + // Append a larger Record to the index_hash (3 bytes for each VLI) + const lzma_vli three_byte_vli = 0x10000; + assert_lzma_ret(lzma_index_hash_append(index_hash, + three_byte_vli, three_byte_vli), LZMA_OK); + + // Expected size should be: + // Index Indicator - 1 byte + // Number of Records - 1 byte + // List of Records - 10 bytes + // Index Padding - 0 bytes + // CRC32 - 4 bytes + // Total - 16 bytes + expected_size = 16; + assert_uint_eq(lzma_index_hash_size(index_hash), expected_size); + + lzma_index_hash_end(index_hash, NULL); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + tuktest_run(test_lzma_index_hash_init); + tuktest_run(test_lzma_index_hash_append); + tuktest_run(test_lzma_index_hash_decode); + tuktest_run(test_lzma_index_hash_size); + return tuktest_end(); +} diff --git a/tests/test_lzip_decoder.c b/tests/test_lzip_decoder.c new file mode 100644 index 0000000..3743d43 --- /dev/null +++ b/tests/test_lzip_decoder.c @@ -0,0 +1,488 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_lzip_decoder.c +/// \brief Tests decoding lzip data +// +// Author: Jia Tan +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + +#ifdef HAVE_LZIP_DECODER + +// Memlimit large enough to pass all of the test files +#define MEMLIMIT (1U << 20) +#define DECODE_CHUNK_SIZE 1024 + + +// The uncompressed data in the test files are short US-ASCII strings. +// The tests check if the decompressed output is what it is expected to be. +// Storing the strings here as text would break the tests on EBCDIC systems +// and storing the strings as an array of hex values is inconvenient, so +// store the CRC32 values of the expected data instead. +// +// CRC32 value of "Hello\nWorld\n" +static const uint32_t hello_world_crc = 0x15A2A343; + +// CRC32 value of "Trailing garbage\n" +static const uint32_t trailing_garbage_crc = 0x87081A60; + + +// Helper function to decode a good file with no flags and plenty high memlimit +static void +basic_lzip_decode(const char *src, const uint32_t expected_crc) +{ + size_t file_size; + uint8_t *data = tuktest_file_from_srcdir(src, &file_size); + uint32_t checksum = 0; + + 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); + + strm.next_in = data; + strm.next_out = output_buffer; + strm.avail_out = DECODE_CHUNK_SIZE; + + // Feed 1 byte at a time to the decoder to look for any bugs + // when switching between decoding sequences + lzma_ret ret = LZMA_OK; + while (ret == LZMA_OK) { + strm.avail_in = 1; + ret = lzma_code(&strm, LZMA_RUN); + if (strm.avail_out == 0) { + 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; + } + } + + assert_lzma_ret(ret, LZMA_STREAM_END); + assert_uint_eq(strm.total_in, file_size); + + checksum = lzma_crc32(output_buffer, + (size_t)(strm.next_out - output_buffer), + checksum); + assert_uint_eq(checksum, expected_crc); + + lzma_end(&strm); +} + + +static void +test_options(void) +{ + // Test NULL stream + assert_lzma_ret(lzma_lzip_decoder(NULL, MEMLIMIT, 0), + LZMA_PROG_ERROR); + + // Test invalid flags + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, UINT32_MAX), + LZMA_OPTIONS_ERROR); + // Memlimit tests are done elsewhere +} + + +static void +test_v0_decode(void) +{ + // This tests if liblzma can decode lzip version 0 files. + // lzip 1.17 and older can decompress this, but lzip 1.18 + // and newer can no longer decode these files. + basic_lzip_decode("files/good-1-v0.lz", hello_world_crc); +} + + +static void +test_v1_decode(void) +{ + // This tests decoding a basic lzip v1 file + basic_lzip_decode("files/good-1-v1.lz", hello_world_crc); +} + + +// Helper function to decode a good file with trailing bytes after +// the lzip stream +static void +trailing_helper(const char *src, const uint32_t expected_data_checksum, + const uint32_t expected_trailing_checksum) +{ + size_t file_size; + uint32_t checksum = 0; + uint8_t *data = tuktest_file_from_srcdir(src, &file_size); + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, + LZMA_CONCATENATED), LZMA_OK); + + uint8_t *output_buffer = tuktest_malloc(DECODE_CHUNK_SIZE); + + strm.next_in = data; + strm.next_out = output_buffer; + strm.avail_in = file_size; + strm.avail_out = DECODE_CHUNK_SIZE; + + lzma_ret ret = LZMA_OK; + while (ret == LZMA_OK) { + ret = lzma_code(&strm, LZMA_RUN); + if (strm.avail_out == 0) { + 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; + } + } + + assert_lzma_ret(ret, LZMA_STREAM_END); + assert_uint(strm.total_in, <, file_size); + + checksum = lzma_crc32(output_buffer, + (size_t)(strm.next_out - output_buffer), + checksum); + + assert_uint_eq(checksum, expected_data_checksum); + + // Trailing data should be readable from strm.next_in + checksum = lzma_crc32(strm.next_in, strm.avail_in, 0); + assert_uint_eq(checksum, expected_trailing_checksum); + + lzma_end(&strm); +} + + +// Helper function to decode a bad file and compare to returned error to +// what the caller expects +static void +decode_expect_error(const char *src, lzma_ret expected_error) +{ + lzma_stream strm = LZMA_STREAM_INIT; + size_t file_size; + uint8_t *data = tuktest_file_from_srcdir(src, &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 = data; + strm.avail_out = DECODE_CHUNK_SIZE; + strm.next_out = output_buffer; + + lzma_ret ret = LZMA_OK; + + while (ret == LZMA_OK) { + // Discard output since we are only looking for errors + strm.next_out = output_buffer; + strm.avail_out = DECODE_CHUNK_SIZE; + if (strm.avail_in == 0) + ret = lzma_code(&strm, LZMA_FINISH); + else + ret = lzma_code(&strm, LZMA_RUN); + } + + assert_lzma_ret(ret, expected_error); + lzma_end(&strm); +} + + +static void +test_v0_trailing(void) +{ + trailing_helper("files/good-1-v0-trailing-1.lz", hello_world_crc, + trailing_garbage_crc); +} + + +static void +test_v1_trailing(void) +{ + trailing_helper("files/good-1-v1-trailing-1.lz", hello_world_crc, + trailing_garbage_crc); + + // The second files/good-1-v1-trailing-2.lz will have the same + // expected output and trailing output as + // files/good-1-v1-trailing-1.lz, but this tests if the prefix + // to the trailing data contains lzip magic bytes. + // When this happens, the expected behavior is to silently ignore + // the magic byte prefix and consume it from the input file. + trailing_helper("files/good-1-v1-trailing-2.lz", hello_world_crc, + trailing_garbage_crc); + + // Expect LZMA_BUF error if a file ends with the lzip magic bytes + // but does not contain any data after + decode_expect_error("files/bad-1-v1-trailing-magic.lz", + LZMA_BUF_ERROR); +} + + +static void +test_concatentated(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); + + 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.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + assert_uint_eq(strm.total_in, file_size); + + uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0); + assert_uint_eq(checksum, hello_world_crc); + + // 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); + + 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.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + assert_uint_eq(strm.total_in, file_size); + checksum = lzma_crc32(output_buffer, strm.total_out, 0); + assert_uint_eq(checksum, hello_world_crc); + + // The third file contains 2 v1 members + uint8_t *v1_v1 = tuktest_file_from_srcdir("files/good-2-v1-v1.lz", + &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.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + assert_uint_eq(strm.total_in, file_size); + checksum = lzma_crc32(output_buffer, strm.total_out, 0); + assert_uint_eq(checksum, hello_world_crc); + + lzma_end(&strm); +} + + +static void +test_crc(void) +{ + // Test invalid checksum + lzma_stream strm = LZMA_STREAM_INIT; + size_t file_size; + uint8_t *data = tuktest_file_from_srcdir("files/bad-1-v1-crc32.lz", + &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 = data; + strm.avail_out = DECODE_CHUNK_SIZE; + strm.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR); + + // Test ignoring the checksum value - should decode successfully + 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.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint_eq(strm.total_in, file_size); + + // Test tell check + 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.next_out = output_buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_GET_CHECK); + assert_uint_eq(lzma_get_check(&strm), LZMA_CHECK_CRC32); + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_DATA_ERROR); + lzma_end(&strm); +} + + +static void +test_invalid_magic_bytes(void) +{ + uint8_t lzip_id_string[] = { 0x4C, 0x5A, 0x49, 0x50 }; + lzma_stream strm = LZMA_STREAM_INIT; + + for (uint32_t i = 0; i < ARRAY_SIZE(lzip_id_string); i++) { + // Corrupt magic bytes + lzip_id_string[i] ^= 1; + uint8_t output_buffer[DECODE_CHUNK_SIZE]; + + assert_lzma_ret(lzma_lzip_decoder(&strm, MEMLIMIT, 0), + LZMA_OK); + + strm.next_in = lzip_id_string; + strm.avail_in = sizeof(lzip_id_string); + strm.next_out = output_buffer; + strm.avail_out = DECODE_CHUNK_SIZE; + + assert_lzma_ret(lzma_code(&strm, LZMA_RUN), + LZMA_FORMAT_ERROR); + + // Reset magic bytes + lzip_id_string[i] ^= 1; + } + + lzma_end(&strm); +} + + +static void +test_invalid_version(void) +{ + // The file contains a version number that is not 0 or 1, + // so it should cause an error + decode_expect_error("files/unsupported-1-v234.lz", + LZMA_OPTIONS_ERROR); +} + + +static void +test_invalid_dictionary_size(void) +{ + // First file has 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 + decode_expect_error("files/bad-1-v1-dict-2.lz", LZMA_DATA_ERROR); +} + + +static void +test_invalid_uncomp_size(void) +{ + // Test invalid v0 lzip file uncomp size + decode_expect_error("files/bad-1-v0-uncomp-size.lz", + LZMA_DATA_ERROR); + + // Test invalid v1 lzip file uncomp size + decode_expect_error("files/bad-1-v1-uncomp-size.lz", + LZMA_DATA_ERROR); +} + + +static void +test_invalid_member_size(void) +{ + decode_expect_error("files/bad-1-v1-member-size.lz", + LZMA_DATA_ERROR); +} + + +static void +test_invalid_memlimit(void) +{ + // A very low memlimit should prevent decoding. + // Should be able to update the memlimit after failing + size_t file_size; + uint8_t *data = tuktest_file_from_srcdir("files/good-1-v1.lz", + &file_size); + + uint8_t output_buffer[DECODE_CHUNK_SIZE]; + + lzma_stream strm = LZMA_STREAM_INIT; + + assert_lzma_ret(lzma_lzip_decoder(&strm, 1, 0), LZMA_OK); + + strm.next_in = data; + strm.avail_in = file_size; + strm.next_out = output_buffer; + strm.avail_out = DECODE_CHUNK_SIZE; + + 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 + assert_lzma_ret(lzma_memlimit_set(&strm, 100), LZMA_MEMLIMIT_ERROR); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT), LZMA_OK); + + // Finish decoding + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + assert_uint_eq(strm.total_in, file_size); + uint32_t checksum = lzma_crc32(output_buffer, strm.total_out, 0); + assert_uint_eq(checksum, hello_world_crc); + + lzma_end(&strm); +} +#endif + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + +#ifndef HAVE_LZIP_DECODER + tuktest_early_skip("lzip decoder disabled"); +#else + tuktest_run(test_options); + tuktest_run(test_v0_decode); + tuktest_run(test_v1_decode); + tuktest_run(test_v0_trailing); + tuktest_run(test_v1_trailing); + tuktest_run(test_concatentated); + tuktest_run(test_crc); + tuktest_run(test_invalid_magic_bytes); + tuktest_run(test_invalid_version); + tuktest_run(test_invalid_dictionary_size); + tuktest_run(test_invalid_uncomp_size); + tuktest_run(test_invalid_member_size); + tuktest_run(test_invalid_memlimit); + return tuktest_end(); +#endif + +} diff --git a/tests/test_memlimit.c b/tests/test_memlimit.c new file mode 100644 index 0000000..c45a44b --- /dev/null +++ b/tests/test_memlimit.c @@ -0,0 +1,173 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_memlimit.c +/// \brief Tests memory usage limit in decoders +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" +#include "mythread.h" + + +#define MEMLIMIT_TOO_LOW 1234U +#define MEMLIMIT_HIGH_ENOUGH (2U << 20) + + +static uint8_t *in; +static size_t in_size; + +#ifdef HAVE_DECODERS +static uint8_t out[8192]; +#endif + + +static void +test_memlimit_stream_decoder(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_stream_decoder(&strm, MEMLIMIT_TOO_LOW, 0), + LZMA_OK); + + strm.next_in = in; + strm.avail_in = in_size; + strm.next_out = out; + strm.avail_out = sizeof(out); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR); + + assert_uint_eq(lzma_memlimit_get(&strm), MEMLIMIT_TOO_LOW); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_TOO_LOW + 1), + LZMA_MEMLIMIT_ERROR); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_HIGH_ENOUGH), + LZMA_OK); + + // This fails before commit 660739f99ab211edec4071de98889fb32ed04e98 + // (liblzma <= 5.2.6, liblzma <= 5.3.3alpha). It was fixed in 5.2.7. + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + + lzma_end(&strm); +#endif +} + + +static void +test_memlimit_stream_decoder_mt(void) +{ +#ifndef MYTHREAD_ENABLED + assert_skip("Threading support disabled"); +#elif !defined(HAVE_DECODERS) + assert_skip("Decoder support disabled"); +#else + lzma_stream strm = LZMA_STREAM_INIT; + lzma_mt mt = { + .flags = 0, + .threads = 1, + .timeout = 0, + .memlimit_threading = 0, + .memlimit_stop = MEMLIMIT_TOO_LOW, + }; + + assert_lzma_ret(lzma_stream_decoder_mt(&strm, &mt), LZMA_OK); + + strm.next_in = in; + strm.avail_in = in_size; + strm.next_out = out; + strm.avail_out = sizeof(out); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR); + + assert_uint_eq(lzma_memlimit_get(&strm), MEMLIMIT_TOO_LOW); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_TOO_LOW + 1), + LZMA_MEMLIMIT_ERROR); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_HIGH_ENOUGH), + LZMA_OK); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + lzma_end(&strm); +#endif +} + + +static void +test_memlimit_alone_decoder(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + size_t alone_size; + uint8_t *alone_buf = tuktest_file_from_srcdir( + "files/good-unknown_size-with_eopm.lzma", &alone_size); + + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_alone_decoder(&strm, MEMLIMIT_TOO_LOW), LZMA_OK); + + strm.next_in = alone_buf; + strm.avail_in = alone_size; + strm.next_out = out; + strm.avail_out = sizeof(out); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR); + + assert_uint_eq(lzma_memlimit_get(&strm), MEMLIMIT_TOO_LOW); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_TOO_LOW + 1), + LZMA_MEMLIMIT_ERROR); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_HIGH_ENOUGH), + LZMA_OK); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + lzma_end(&strm); +#endif +} + + +static void +test_memlimit_auto_decoder(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + lzma_stream strm = LZMA_STREAM_INIT; + assert_lzma_ret(lzma_auto_decoder(&strm, MEMLIMIT_TOO_LOW, 0), + LZMA_OK); + + strm.next_in = in; + strm.avail_in = in_size; + strm.next_out = out; + strm.avail_out = sizeof(out); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_MEMLIMIT_ERROR); + + assert_uint_eq(lzma_memlimit_get(&strm), MEMLIMIT_TOO_LOW); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_TOO_LOW + 1), + LZMA_MEMLIMIT_ERROR); + assert_lzma_ret(lzma_memlimit_set(&strm, MEMLIMIT_HIGH_ENOUGH), + LZMA_OK); + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + lzma_end(&strm); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + + in = tuktest_file_from_srcdir("files/good-1-check-crc32.xz", &in_size); + + tuktest_run(test_memlimit_stream_decoder); + tuktest_run(test_memlimit_stream_decoder_mt); + tuktest_run(test_memlimit_alone_decoder); + tuktest_run(test_memlimit_auto_decoder); + + return tuktest_end(); +} diff --git a/tests/test_scripts.sh b/tests/test_scripts.sh new file mode 100755 index 0000000..ee82361 --- /dev/null +++ b/tests/test_scripts.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +############################################################################### +# +# 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 + +for i in XZ XZDIFF XZGREP; do + eval test -x "\$$i" && continue + exit 77 +done + +# If decompression support is missing, this test is skipped. +# 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 + : +else + echo "Decompression support is disabled, skipping this test." + exit 77 +fi + +PATH=`pwd`/../src/xz:$PATH +export PATH + +test -z "$srcdir" && srcdir=. +preimage=$srcdir/files/good-1-check-crc32.xz +samepostimage=$srcdir/files/good-1-check-crc64.xz +otherpostimage=$srcdir/files/good-1-lzma2-1.xz + +"$XZDIFF" "$preimage" "$samepostimage" >/dev/null +status=$? +if test "$status" != 0 ; then + echo "xzdiff with no changes exited with status $status != 0" + exit 1 +fi + +"$XZDIFF" "$preimage" "$otherpostimage" >/dev/null +status=$? +if test "$status" != 1 ; then + echo "xzdiff with changes exited with status $status != 1" + exit 1 +fi + +"$XZDIFF" "$preimage" "$srcdir/files/missing.xz" >/dev/null 2>&1 +status=$? +if test "$status" != 2 ; then + echo "xzdiff with missing operand exited with status $status != 2" + exit 1 +fi + +# The exit status must be 0 when a match was found at least from one file, +# and 1 when no match was found in any file. +cp "$srcdir/files/good-1-lzma2-1.xz" xzgrep_test_1.xz +cp "$srcdir/files/good-2-lzma2.xz" xzgrep_test_2.xz +for pattern in el Hello NOMATCH; do + for opts in "" "-l" "-h" "-H"; do + echo "=> xzgrep $opts $pattern <=" + "$XZGREP" $opts $pattern xzgrep_test_1.xz xzgrep_test_2.xz + echo retval $? + done +done > xzgrep_test_output 2>&1 + +if cmp -s "$srcdir/xzgrep_expected_output" xzgrep_test_output ; then + : +else + echo "unexpected output from xzgrep" + exit 1 +fi + +exit 0 diff --git a/tests/test_stream_flags.c b/tests/test_stream_flags.c new file mode 100644 index 0000000..2248e67 --- /dev/null +++ b/tests/test_stream_flags.c @@ -0,0 +1,479 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_stream_flags.c +/// \brief Tests Stream Header and Stream Footer coders +// +// 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" + + +// Size of the Stream Flags field +// (taken from src/liblzma/common/stream_flags_common.h) +#define XZ_STREAM_FLAGS_SIZE 2 + +#ifdef HAVE_ENCODERS +// Header and footer magic bytes for .xz file format +// (taken from src/liblzma/common/stream_flags_common.c) +static const uint8_t xz_header_magic[6] + = { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00 }; +static const uint8_t xz_footer_magic[2] = { 0x59, 0x5A }; +#endif + + +#ifdef HAVE_ENCODERS +static void +stream_header_encode_helper(lzma_check check) +{ + lzma_stream_flags flags = { + .version = 0, + .check = check, + }; + + uint8_t header[LZMA_STREAM_HEADER_SIZE]; + + // Encode Stream Header + assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); + + // Stream Header must start with Header Magic Bytes + const uint32_t magic_size = sizeof(xz_header_magic); + assert_array_eq(header, xz_header_magic, magic_size); + + // Next must come Stream Flags + const uint8_t *encoded_stream_flags = header + magic_size; + + // First byte is always null-byte. + // Second byte must have the Check ID in the lowest four bits + // and the highest four bits zero. + const uint8_t expected_stream_flags[] = { 0, check }; + assert_array_eq(encoded_stream_flags, expected_stream_flags, + XZ_STREAM_FLAGS_SIZE); + + // Last part is the CRC32 of the Stream Flags + const uint8_t *crc_ptr = encoded_stream_flags + XZ_STREAM_FLAGS_SIZE; + const uint32_t expected_crc = lzma_crc32(expected_stream_flags, + XZ_STREAM_FLAGS_SIZE, 0); + assert_uint_eq(read32le(crc_ptr), expected_crc); +} +#endif + + +static void +test_lzma_stream_header_encode(void) +{ +#ifndef HAVE_ENCODERS + assert_skip("Encoder support disabled"); +#else + for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) + stream_header_encode_helper(i); + + lzma_stream_flags flags = { + .version = 0, + .check = LZMA_CHECK_CRC32 + }; + + uint8_t header[LZMA_STREAM_HEADER_SIZE]; + + // Should fail if version > 0 + flags.version = 1; + assert_lzma_ret(lzma_stream_header_encode(&flags, header), + LZMA_OPTIONS_ERROR); + flags.version = 0; + + // Should fail if Check ID is invalid + flags.check = INVALID_LZMA_CHECK_ID; + assert_lzma_ret(lzma_stream_header_encode(&flags, header), + LZMA_PROG_ERROR); + flags.check = LZMA_CHECK_CRC32; + + // Should pass even if Backward Size is invalid + // because Stream Header doesn't have that field. + flags.backward_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); +#endif +} + + +#if defined(HAVE_ENCODERS) +static void +stream_footer_encode_helper(lzma_check check) +{ + lzma_stream_flags flags = { + .version = 0, + .check = check, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + }; + + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + + // Encode Stream Footer + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); + + // Stream Footer must start with CRC32 + const uint32_t crc = read32le(footer); + const uint32_t expected_crc = lzma_crc32(footer + sizeof(uint32_t), + LZMA_STREAM_HEADER_SIZE - (sizeof(uint32_t) + + sizeof(xz_footer_magic)), 0); + assert_uint_eq(crc, expected_crc); + + // Next the Backward Size + const uint32_t backwards_size = read32le(footer + sizeof(uint32_t)); + const uint32_t expected_backwards_size = flags.backward_size / 4 - 1; + assert_uint_eq(backwards_size, expected_backwards_size); + + // Next the Stream Flags + const uint8_t *stream_flags = footer + sizeof(uint32_t) * 2; + + // First byte must be null + assert_uint_eq(stream_flags[0], 0); + + // Second byte must have the Check ID in the lowest four bits + // and the highest four bits zero. + assert_uint_eq(stream_flags[1], check); + + // And ends with Footer Magic Bytes + const uint8_t *expected_footer_magic = stream_flags + + XZ_STREAM_FLAGS_SIZE; + assert_array_eq(expected_footer_magic, xz_footer_magic, + sizeof(xz_footer_magic)); +} +#endif + + +static void +test_lzma_stream_footer_encode(void) +{ +#ifndef HAVE_ENCODERS + assert_skip("Encoder support disabled"); +#else + for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) + stream_footer_encode_helper(i); + + lzma_stream_flags flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + + // Should fail if version > 0 + flags.version = 1; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), + LZMA_OPTIONS_ERROR); + flags.version = 0; + + // Should fail if Check ID is invalid + flags.check = INVALID_LZMA_CHECK_ID; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), + LZMA_PROG_ERROR); + + // Should fail if Backward Size is invalid + flags.backward_size -= 1; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), + LZMA_PROG_ERROR); + flags.backward_size += 2; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), + LZMA_PROG_ERROR); + flags.backward_size = LZMA_BACKWARD_SIZE_MAX + 4; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), + LZMA_PROG_ERROR); +#endif +} + + +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +static void +stream_header_decode_helper(lzma_check check) +{ + lzma_stream_flags flags = { + .version = 0, + .check = check + }; + + uint8_t header[LZMA_STREAM_HEADER_SIZE]; + + assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); + + lzma_stream_flags dest_flags; + assert_lzma_ret(lzma_stream_header_decode(&dest_flags, header), + LZMA_OK); + + // Version should be 0 + assert_uint_eq(dest_flags.version, 0); + + // Backward Size should be LZMA_VLI_UNKNOWN + assert_uint_eq(dest_flags.backward_size, LZMA_VLI_UNKNOWN); + + // Check ID must equal the argument given to this function. + assert_uint_eq(dest_flags.check, check); +} +#endif + + +static void +test_lzma_stream_header_decode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) + stream_header_decode_helper(i); + + lzma_stream_flags flags = { + .version = 0, + .check = LZMA_CHECK_CRC32 + }; + + uint8_t header[LZMA_STREAM_HEADER_SIZE]; + lzma_stream_flags dest; + + // First encode known flags to header buffer + assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK); + + // Should fail if magic bytes do not match + header[0] ^= 1; + assert_lzma_ret(lzma_stream_header_decode(&dest, header), + LZMA_FORMAT_ERROR); + header[0] ^= 1; + + // Should fail if a reserved bit is set + uint8_t *stream_flags = header + sizeof(xz_header_magic); + stream_flags[0] = 1; + + // Need to adjust CRC32 after making a change since the CRC32 + // is verified before decoding the Stream Flags field. + uint8_t *crc32_ptr = header + sizeof(xz_header_magic) + + XZ_STREAM_FLAGS_SIZE; + const uint32_t crc_orig = read32le(crc32_ptr); + uint32_t new_crc32 = lzma_crc32( + stream_flags, XZ_STREAM_FLAGS_SIZE, 0); + write32le(crc32_ptr, new_crc32); + assert_lzma_ret(lzma_stream_header_decode(&dest, header), + LZMA_OPTIONS_ERROR); + stream_flags[0] = 0; + write32le(crc32_ptr, crc_orig); + + // Should fail if upper bits of check ID are set + stream_flags[1] |= 0xF0; + new_crc32 = lzma_crc32(stream_flags, XZ_STREAM_FLAGS_SIZE, 0); + write32le(crc32_ptr, new_crc32); + assert_lzma_ret(lzma_stream_header_decode(&dest, header), + LZMA_OPTIONS_ERROR); + stream_flags[1] = flags.check; + write32le(crc32_ptr, crc_orig); + + // Should fail if CRC32 does not match. + // First, alter a byte in the Stream Flags. + stream_flags[0] = 1; + assert_lzma_ret(lzma_stream_header_decode(&dest, header), + LZMA_DATA_ERROR); + stream_flags[0] = 0; + + // Next, change the CRC32. + *crc32_ptr ^= 1; + assert_lzma_ret(lzma_stream_header_decode(&dest, header), + LZMA_DATA_ERROR); +#endif +} + + +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +static void +stream_footer_decode_helper(lzma_check check) +{ + lzma_stream_flags flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = check, + }; + + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); + + lzma_stream_flags dest_flags; + assert_lzma_ret(lzma_stream_footer_decode(&dest_flags, footer), + LZMA_OK); + + // Version should be 0. + assert_uint_eq(dest_flags.version, 0); + + // Backward Size should equal the value from the flags. + assert_uint_eq(dest_flags.backward_size, flags.backward_size); + + // Check ID must equal argument given to this function. + assert_uint_eq(dest_flags.check, check); +} +#endif + + +static void +test_lzma_stream_footer_decode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) + stream_footer_decode_helper(i); + + lzma_stream_flags flags = { + .version = 0, + .check = LZMA_CHECK_CRC32, + .backward_size = LZMA_BACKWARD_SIZE_MIN + }; + + uint8_t footer[LZMA_STREAM_HEADER_SIZE]; + lzma_stream_flags dest; + + // First encode known flags to the footer buffer + assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK); + + // Should fail if magic bytes do not match + footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1; + assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), + LZMA_FORMAT_ERROR); + footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1; + + // Should fail if a reserved bit is set. + // In the Stream Footer, the Stream Flags follow the CRC32 (4 bytes) + // and the Backward Size (4 bytes) + uint8_t *stream_flags = footer + sizeof(uint32_t) * 2; + stream_flags[0] = 1; + + // Need to adjust the CRC32 so it will not fail that check instead + uint8_t *crc32_ptr = footer; + const uint32_t crc_orig = read32le(crc32_ptr); + uint8_t *backward_size = footer + sizeof(uint32_t); + uint32_t new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) + + XZ_STREAM_FLAGS_SIZE, 0); + write32le(crc32_ptr, new_crc32); + assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), + LZMA_OPTIONS_ERROR); + stream_flags[0] = 0; + write32le(crc32_ptr, crc_orig); + + // Should fail if upper bits of check ID are set + stream_flags[1] |= 0xF0; + new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) + + XZ_STREAM_FLAGS_SIZE, 0); + write32le(crc32_ptr, new_crc32); + assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), + LZMA_OPTIONS_ERROR); + stream_flags[1] = flags.check; + write32le(crc32_ptr, crc_orig); + + // Should fail if CRC32 does not match. + // First, alter a byte in the Stream Flags. + stream_flags[0] = 1; + assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), + LZMA_DATA_ERROR); + stream_flags[0] = 0; + + // Next, change the CRC32 + *crc32_ptr ^= 1; + assert_lzma_ret(lzma_stream_footer_decode(&dest, footer), + LZMA_DATA_ERROR); +#endif +} + + +static void +test_lzma_stream_flags_compare(void) +{ + lzma_stream_flags first = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32, + }; + + lzma_stream_flags second = first; + + // First test should pass + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); + + // Altering either version should cause an error + first.version = 1; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_OPTIONS_ERROR); + second.version = 1; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_OPTIONS_ERROR); + first.version = 0; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_OPTIONS_ERROR); + second.version = 0; + + // Check types must be under the maximum + first.check = INVALID_LZMA_CHECK_ID; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + second.check = INVALID_LZMA_CHECK_ID; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + first.check = LZMA_CHECK_CRC32; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + second.check = LZMA_CHECK_CRC32; + + // Check types must be equal + for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) { + first.check = i; + if (i == second.check) + assert_lzma_ret(lzma_stream_flags_compare(&first, + &second), LZMA_OK); + else + assert_lzma_ret(lzma_stream_flags_compare(&first, + &second), LZMA_DATA_ERROR); + } + first.check = LZMA_CHECK_CRC32; + + // Backward Size comparison is skipped if either are LZMA_VLI_UNKNOWN + first.backward_size = LZMA_VLI_UNKNOWN; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); + second.backward_size = LZMA_VLI_MAX + 1; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK); + second.backward_size = LZMA_BACKWARD_SIZE_MIN; + + // Backward Sizes need to be valid + first.backward_size = LZMA_VLI_MAX + 4; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + second.backward_size = LZMA_VLI_MAX + 4; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + first.backward_size = LZMA_BACKWARD_SIZE_MIN; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + second.backward_size = LZMA_BACKWARD_SIZE_MIN; + + // Backward Sizes must be equal + second.backward_size = first.backward_size + 4; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_DATA_ERROR); + + // Should fail if Backward Sizes are > LZMA_BACKWARD_SIZE_MAX + // even though they are equal + first.backward_size = LZMA_BACKWARD_SIZE_MAX + 1; + second.backward_size = LZMA_BACKWARD_SIZE_MAX + 1; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + tuktest_run(test_lzma_stream_header_encode); + tuktest_run(test_lzma_stream_footer_encode); + tuktest_run(test_lzma_stream_header_decode); + tuktest_run(test_lzma_stream_footer_decode); + tuktest_run(test_lzma_stream_flags_compare); + return tuktest_end(); +} diff --git a/tests/test_vli.c b/tests/test_vli.c new file mode 100644 index 0000000..996b775 --- /dev/null +++ b/tests/test_vli.c @@ -0,0 +1,324 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_vli.c +/// \brief Tests liblzma vli functions +// +// Author: Jia Tan +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + + +// Pre-encoded VLI values for testing +// VLI can have between 1 and 9 bytes when encoded +// They are encoded little endian where all but the last +// byte must have the leading 1 bit set +#if defined(HAVE_ENCODERS) || defined(HAVE_DECODERS) +static const uint8_t one_byte[1] = {0x25}; +static const lzma_vli one_byte_value = 37; + +static const uint8_t two_bytes[2] = {0x80, 0x56}; +static const lzma_vli two_byte_value = 11008; + +static const uint8_t three_bytes[3] = {0x99, 0x92, 0x20}; +static const lzma_vli three_byte_value = 526617; + +static const uint8_t four_bytes[4] = {0x97, 0x83, 0x94, 0x47}; +static const lzma_vli four_byte_value = 149225879; + +static const uint8_t five_bytes[5] = {0xA6, 0x92, 0x88, 0x89, 0x32}; +static const lzma_vli five_byte_value = 13440780582; + +static const uint8_t six_bytes[6] = {0xA9, 0x84, 0x99, 0x82, 0x94, 0x12}; +static const lzma_vli six_byte_value = 623848604201; + +static const uint8_t seven_bytes[7] = {0x90, 0x80, 0x90, 0x80, 0x90, 0x80, + 0x79}; +static const lzma_vli seven_byte_value = 532167923073040; + +static const uint8_t eight_bytes[8] = {0x91, 0x87, 0xF2, 0xB2, 0xC2, 0xD2, + 0x93, 0x63}; +static const lzma_vli eight_byte_value = 55818443594433425; + +static const uint8_t nine_bytes[9] = {0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, + 0xE1, 0xF1, 0x1}; +static const lzma_vli nine_byte_value = 136100349976529025; +#endif + + +static void +test_lzma_vli_size(void) +{ + // First test invalid VLI values (should return 0) + // VLI UNKNOWN is an invalid VLI + assert_uint_eq(lzma_vli_size(LZMA_VLI_UNKNOWN), 0); + // Loop over a few VLI values just over the maximum + for (uint64_t i = LZMA_VLI_MAX + 1; i < LZMA_VLI_MAX + 10; i++) + assert_uint_eq(lzma_vli_size(i), 0); + + // Number should increment every seven set bits + lzma_vli vli = 1; + for (uint32_t i = 1; i < LZMA_VLI_BYTES_MAX; i++, vli <<= 7) { + // Test the base value and a few others around it + assert_uint_eq(lzma_vli_size(vli), i); + assert_uint_eq(lzma_vli_size(vli * 2), i); + assert_uint_eq(lzma_vli_size(vli + 10), i); + assert_uint_eq(lzma_vli_size(vli * 3 + 39), i); + } +} + + +#ifdef HAVE_ENCODERS +// Helper function for test_lzma_vli_encode +// Encodes an input VLI and compares against a pre-computed value +static void +encode_single_call_mode(lzma_vli input, const uint8_t *expected, + uint32_t expected_len) +{ + uint8_t out[LZMA_VLI_BYTES_MAX]; + size_t out_pos = 0; + assert_lzma_ret(lzma_vli_encode(input, NULL, out, &out_pos, + expected_len), LZMA_OK); + assert_uint_eq(out_pos, expected_len); + assert_array_eq(out, expected, expected_len); +} + + +// Helper function for test_lzma_vli_encode +// Encodes an input VLI one byte at a time with the multi call +// method. Then compares against a pre-computed value +static void +encode_multi_call_mode(lzma_vli input, const uint8_t *expected, + uint32_t expected_len) +{ + uint8_t out[LZMA_VLI_BYTES_MAX]; + size_t out_pos = 0; + size_t vli_pos = 0; + + for (uint32_t i = 1; i < expected_len; i++) { + assert_lzma_ret(lzma_vli_encode(input, &vli_pos, out, + &out_pos, i), LZMA_OK); + assert_uint_eq(out_pos, i); + assert_uint_eq(vli_pos, i); + } + assert_lzma_ret(lzma_vli_encode(input, &vli_pos, out, &out_pos, + expected_len), LZMA_STREAM_END); + assert_uint_eq(out_pos, expected_len); + assert_uint_eq(vli_pos, expected_len); + assert_array_eq(out, expected, expected_len); +} +#endif + + +static void +test_lzma_vli_encode(void) +{ +#ifndef HAVE_ENCODERS + assert_skip("Encoder support disabled"); +#else + size_t vli_pos = 0; + uint8_t out[LZMA_VLI_BYTES_MAX]; + uint8_t zeros[LZMA_VLI_BYTES_MAX]; + memzero(out, LZMA_VLI_BYTES_MAX); + memzero(zeros, LZMA_VLI_BYTES_MAX); + size_t out_pos = 0; + + // First test invalid input parameters + // VLI invalid + assert_lzma_ret(lzma_vli_encode(LZMA_VLI_UNKNOWN, &vli_pos, out, + &out_pos, sizeof(out)), LZMA_PROG_ERROR); + // Failure should not change params + assert_uint_eq(vli_pos, 0); + assert_uint_eq(out_pos, 0); + assert_array_eq(out, zeros, LZMA_VLI_BYTES_MAX); + + assert_lzma_ret(lzma_vli_encode(LZMA_VLI_MAX + 1, &vli_pos, out, + &out_pos, sizeof(out)), LZMA_PROG_ERROR); + assert_uint_eq(vli_pos, 0); + assert_uint_eq(out_pos, 0); + assert_array_eq(out, zeros, LZMA_VLI_BYTES_MAX); + + // 0 output size + assert_lzma_ret(lzma_vli_encode(one_byte_value, &vli_pos, out, + &out_pos, 0), LZMA_BUF_ERROR); + assert_uint_eq(vli_pos, 0); + assert_uint_eq(out_pos, 0); + assert_array_eq(out, zeros, LZMA_VLI_BYTES_MAX); + + // Size of VLI does not fit in buffer + size_t phony_out_pos = 3; + assert_lzma_ret(lzma_vli_encode(one_byte_value, NULL, out, + &phony_out_pos, 2), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_vli_encode(LZMA_VLI_MAX / 2, NULL, out, + &out_pos, 2), LZMA_PROG_ERROR); + + // Test single-call mode (using vli_pos as NULL) + encode_single_call_mode(one_byte_value, one_byte, + sizeof(one_byte)); + encode_single_call_mode(two_byte_value, two_bytes, + sizeof(two_bytes)); + encode_single_call_mode(three_byte_value, three_bytes, + sizeof(three_bytes)); + encode_single_call_mode(four_byte_value, four_bytes, + sizeof(four_bytes)); + encode_single_call_mode(five_byte_value, five_bytes, + sizeof(five_bytes)); + encode_single_call_mode(six_byte_value, six_bytes, + sizeof(six_bytes)); + encode_single_call_mode(seven_byte_value, seven_bytes, + sizeof(seven_bytes)); + encode_single_call_mode(eight_byte_value, eight_bytes, + sizeof(eight_bytes)); + encode_single_call_mode(nine_byte_value, nine_bytes, + sizeof(nine_bytes)); + + // Test multi-call mode + encode_multi_call_mode(one_byte_value, one_byte, + sizeof(one_byte)); + encode_multi_call_mode(two_byte_value, two_bytes, + sizeof(two_bytes)); + encode_multi_call_mode(three_byte_value, three_bytes, + sizeof(three_bytes)); + encode_multi_call_mode(four_byte_value, four_bytes, + sizeof(four_bytes)); + encode_multi_call_mode(five_byte_value, five_bytes, + sizeof(five_bytes)); + encode_multi_call_mode(six_byte_value, six_bytes, + sizeof(six_bytes)); + encode_multi_call_mode(seven_byte_value, seven_bytes, + sizeof(seven_bytes)); + encode_multi_call_mode(eight_byte_value, eight_bytes, + sizeof(eight_bytes)); + encode_multi_call_mode(nine_byte_value, nine_bytes, + sizeof(nine_bytes)); +#endif +} + + +#ifdef HAVE_DECODERS +static void +decode_single_call_mode(const uint8_t *input, uint32_t input_len, + lzma_vli expected) +{ + lzma_vli out = 0; + size_t in_pos = 0; + + assert_lzma_ret(lzma_vli_decode(&out, NULL, input, &in_pos, + input_len), LZMA_OK); + assert_uint_eq(in_pos, input_len); + assert_uint_eq(out, expected); +} + + +static void +decode_multi_call_mode(const uint8_t *input, uint32_t input_len, + lzma_vli expected) +{ + lzma_vli out = 0; + size_t in_pos = 0; + size_t vli_pos = 0; + + for (uint32_t i = 1; i < input_len; i++) { + assert_lzma_ret(lzma_vli_decode(&out, &vli_pos, input, + &in_pos, i), LZMA_OK); + assert_uint_eq(in_pos, i); + assert_uint_eq(vli_pos, i); + } + + assert_lzma_ret(lzma_vli_decode(&out, &vli_pos, input, &in_pos, + input_len), LZMA_STREAM_END); + assert_uint_eq(in_pos, input_len); + assert_uint_eq(vli_pos, input_len); + assert_uint_eq(out, expected); +} +#endif + + +static void +test_lzma_vli_decode(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + lzma_vli out = 0; + size_t in_pos = 0; + + // First test invalid input params + // 0 in_size + assert_lzma_ret(lzma_vli_decode(&out, NULL, one_byte, &in_pos, 0), + LZMA_DATA_ERROR); + assert_uint_eq(out, 0); + assert_uint_eq(in_pos, 0); + + // VLI encoded is invalid (last digit has leading 1 set) + uint8_t invalid_vli[3] = {0x80, 0x80, 0x80}; + assert_lzma_ret(lzma_vli_decode(&out, NULL, invalid_vli, &in_pos, + sizeof(invalid_vli)), LZMA_DATA_ERROR); + + // Bad vli_pos + size_t vli_pos = LZMA_VLI_BYTES_MAX; + assert_lzma_ret(lzma_vli_decode(&out, &vli_pos, invalid_vli, &in_pos, + sizeof(invalid_vli)), LZMA_PROG_ERROR); + + // Bad in_pos + in_pos = sizeof(invalid_vli); + assert_lzma_ret(lzma_vli_decode(&out, &in_pos, invalid_vli, &in_pos, + sizeof(invalid_vli)), LZMA_BUF_ERROR); + + // Test single call mode + decode_single_call_mode(one_byte, sizeof(one_byte), + one_byte_value); + decode_single_call_mode(two_bytes, sizeof(two_bytes), + two_byte_value); + decode_single_call_mode(three_bytes, sizeof(three_bytes), + three_byte_value); + decode_single_call_mode(four_bytes, sizeof(four_bytes), + four_byte_value); + decode_single_call_mode(five_bytes, sizeof(five_bytes), + five_byte_value); + decode_single_call_mode(six_bytes, sizeof(six_bytes), + six_byte_value); + decode_single_call_mode(seven_bytes, sizeof(seven_bytes), + seven_byte_value); + decode_single_call_mode(eight_bytes, sizeof(eight_bytes), + eight_byte_value); + decode_single_call_mode(nine_bytes, sizeof(nine_bytes), + nine_byte_value); + + // Test multi call mode + decode_multi_call_mode(one_byte, sizeof(one_byte), + one_byte_value); + decode_multi_call_mode(two_bytes, sizeof(two_bytes), + two_byte_value); + decode_multi_call_mode(three_bytes, sizeof(three_bytes), + three_byte_value); + decode_multi_call_mode(four_bytes, sizeof(four_bytes), + four_byte_value); + decode_multi_call_mode(five_bytes, sizeof(five_bytes), + five_byte_value); + decode_multi_call_mode(six_bytes, sizeof(six_bytes), + six_byte_value); + decode_multi_call_mode(seven_bytes, sizeof(seven_bytes), + seven_byte_value); + decode_multi_call_mode(eight_bytes, sizeof(eight_bytes), + eight_byte_value); + decode_multi_call_mode(nine_bytes, sizeof(nine_bytes), + nine_byte_value); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + tuktest_run(test_lzma_vli_size); + tuktest_run(test_lzma_vli_encode); + tuktest_run(test_lzma_vli_decode); + return tuktest_end(); +} diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..2d42700 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file tests.h +/// \brief Common definitions for test applications +// +// 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 +#define LZMA_TESTS_H + +#include "sysdefs.h" +#include "tuklib_integer.h" +#include "lzma.h" +#include "tuktest.h" + + +// Invalid value for the lzma_check enumeration. This must be positive +// but small enough to fit into signed char since the underlying type might +// one some platform be a signed char. +// +// Don't put LZMA_ at the beginning of the name so that it is obvious that +// this constant doesn't come from the API headers. +#define INVALID_LZMA_CHECK_ID ((lzma_check)(LZMA_CHECK_ID_MAX + 1)) + + +// This table and macro allow getting more readable error messages when +// comparing the lzma_ret enumeration values. +static const char enum_strings_lzma_ret[][24] = { + "LZMA_OK", + "LZMA_STREAM_END", + "LZMA_NO_CHECK", + "LZMA_UNSUPPORTED_CHECK", + "LZMA_GET_CHECK", + "LZMA_MEM_ERROR", + "LZMA_MEMLIMIT_ERROR", + "LZMA_FORMAT_ERROR", + "LZMA_OPTIONS_ERROR", + "LZMA_DATA_ERROR", + "LZMA_BUF_ERROR", + "LZMA_PROG_ERROR", + "LZMA_SEEK_NEEDED", +}; + +#define assert_lzma_ret(test_expr, ref_val) \ + assert_enum_eq(test_expr, ref_val, enum_strings_lzma_ret) + + +static const char enum_strings_lzma_check[][24] = { + "LZMA_CHECK_NONE", + "LZMA_CHECK_CRC32", + "LZMA_CHECK_UNKNOWN_2", + "LZMA_CHECK_UNKNOWN_3", + "LZMA_CHECK_CRC64", + "LZMA_CHECK_UNKNOWN_5", + "LZMA_CHECK_UNKNOWN_6", + "LZMA_CHECK_UNKNOWN_7", + "LZMA_CHECK_UNKNOWN_8", + "LZMA_CHECK_UNKNOWN_9", + "LZMA_CHECK_SHA256", + "LZMA_CHECK_UNKNOWN_11", + "LZMA_CHECK_UNKNOWN_12", + "LZMA_CHECK_UNKNOWN_13", + "LZMA_CHECK_UNKNOWN_14", + "LZMA_CHECK_UNKNOWN_15", +}; + +#define assert_lzma_check(test_expr, ref_val) \ + assert_enum_eq(test_expr, ref_val, enum_strings_lzma_check) + +#endif diff --git a/tests/tuktest.h b/tests/tuktest.h new file mode 100644 index 0000000..508eace --- /dev/null +++ b/tests/tuktest.h @@ -0,0 +1,1053 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file tuktest.h +/// \brief Helper macros for writing simple test programs +/// \version 2023-01-08 +/// +/// Some inspiration was taken from STest by Keith Nicholas. +/// +/// This is standard C99/C11 only and thus should be fairly portable +/// outside POSIX systems too. +/// +/// This supports putting multiple tests in a single test program +/// although it is perfectly fine to have only one test per program. +/// Each test can produce one of these results: +/// - Pass +/// - Fail +/// - Skip +/// - Hard error (the remaining tests, if any, are not run) +/// +/// By default this produces an exit status that is compatible with +/// Automake and Meson, and mostly compatible with CMake.[1] +/// If a test program contains multiple tests, only one exit code can +/// be returned. Of the following, the first match is used: +/// - 99 if any test returned a hard error +/// - stdlib.h's EXIT_FAILURE if at least one test failed +/// - 77 if at least one test was skipped or no tests were run at all +/// - stdlib.h's EXIT_SUCCESS (0 on POSIX); that is, if none of the above +/// are true then there was at least one test to run and none of them +/// failed, was skipped, or returned a hard error. +/// +/// A summary of tests being run and their results are printed to stdout. +/// If you want ANSI coloring for the output, #define TUKTEST_COLOR. +/// If you only want output when something goes wrong, #define TUKTEST_QUIET. +/// +/// The downside of the above mapping is that it cannot indicate if +/// some tests were skipped and some passed. If that is likely to +/// happen it may be better to split into multiple test programs (one +/// test per program) or use the TAP mode described below. +/// +/// By using #define TUKTEST_TAP before #including this file the +/// output will be Test Anything Protocol (TAP) version 12 compatible +/// and the exit status will always be EXIT_SUCCESS. This can be easily +/// used with Automake via its tap-driver.sh. Meson supports TAP natively. +/// TAP's todo-directive isn't supported for now, mostly because it's not +/// trivially convertible to the exit-status reporting method. +/// +/// If TUKTEST_TAP is used, TUKTEST_QUIET and TUKTEST_COLOR are ignored. +/// +/// The main() function may look like this (remember to include config.h +/// or such files too if needed!): +/// +/// #include "tuktest.h" +/// +/// int main(int argc, char **argv) +/// { +/// tuktest_start(argc, argv); +/// +/// if (!is_package_foo_available()) +/// tuktest_early_skip("Optional package foo is not available"); +/// +/// if (!do_common_initializations()) +/// tuktest_error("Error during common initializations"); +/// +/// tuktest_run(testfunc1); +/// tuktest_run(testfunc2); +/// +/// return tuktest_end(); +/// } +/// +/// Using exit(tuktest_end()) as a pair to tuktest_start() is OK too. +/// +/// Each test function called via tuktest_run() should be of type +/// "void testfunc1(void)". The test functions should use the +/// various assert_CONDITION() macros. The current test stops if +/// an assertion fails (this is implemented with setjmp/longjmp). +/// Execution continues from the next test unless the failure was +/// due to assert_error() (indicating a hard error) which makes +/// the program exit() without running any remaining tests. +/// +/// Search for "define assert" in this file to find the explanations +/// of the available assertion macros. +/// +/// IMPORTANT: +/// +/// - The assert_CONDITION() macros may only be used by code that is +/// called via tuktest_run()! This includes the function named in +/// the tuktest_run() call and functions called further from there. +/// (The assert_CONDITION() macros depend on setup code in tuktest_run() +/// and other use results in undefined behavior.) +/// +/// - tuktest_start(), tuktest_early_skip, tuktest_run(), and tuktest_end() +/// must not be used in the tests called via tuktest_run()! (tuktest_end() +/// is called more freely internally by this file but such use isn't part +/// of the API.) +/// +/// - tuktest_error(), tuktest_malloc(), tuktest_free(), +/// tuktest_file_from_srcdir(), and tuktest_file_from_builddir() +/// can be used everywhere after tuktest_start() has been called. +/// (In tests running under tuktest_run(), assert_error() can be used +/// instead of tuktest_error() when a hard error occurs.) +/// +/// - Everything else is for internal use only. +/// +/// Footnotes: +/// +/// [1] As of 2022-06-02: +/// See the Automake manual "info (automake)Scripts-based Testsuites" or: +/// https://www.gnu.org/software/automake/manual/automake.html#Scripts_002dbased-Testsuites +/// +/// 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: +/// +/// set_tests_properties(foo_test_name PROPERTIES SKIP_RETURN_CODE 77) +/// +/// See: +/// https://cmake.org/cmake/help/latest/command/set_tests_properties.html +/// https://cmake.org/cmake/help/latest/prop_test/SKIP_RETURN_CODE.html +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TUKTEST_H +#define TUKTEST_H + +#include <stddef.h> + +// On some (too) old systems inttypes.h doesn't exist or isn't good enough. +// Include it conditionally so that any portability tricks can be done before +// tuktest.h is included. On any modern system inttypes.h is fine as is. +#ifndef PRIu64 +# include <inttypes.h> +#endif + +#include <setjmp.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define TUKTEST_GNUC_REQ(major, minor) \ + ((__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)) \ + || __GNUC__ > (major)) +#else +# define TUKTEST_GNUC_REQ(major, minor) 0 +#endif + + +// This is silencing warnings about unused functions. Not all test programs +// need all functions from this header. +#if TUKTEST_GNUC_REQ(3, 0) +# 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) +# pragma GCC diagnostic ignored "-Wformat-zero-length" +#endif + + +// Types and printf format macros to use in integer assertions and also for +// printing size_t values (C99's %zu isn't available on very old systems). +typedef int64_t tuktest_int; +typedef uint64_t tuktest_uint; +#define TUKTEST_PRId PRId64 +#define TUKTEST_PRIu PRIu64 +#define TUKTEST_PRIX PRIX64 + + +// When TAP mode isn't used, Automake-compatible exit statuses are used. +#define TUKTEST_EXIT_PASS EXIT_SUCCESS +#define TUKTEST_EXIT_FAIL EXIT_FAILURE +#define TUKTEST_EXIT_SKIP 77 +#define TUKTEST_EXIT_ERROR 99 + + +enum tuktest_result { + TUKTEST_PASS, + TUKTEST_FAIL, + TUKTEST_SKIP, + TUKTEST_ERROR, +}; + + +#ifdef TUKTEST_TAP +# undef TUKTEST_QUIET +# undef TUKTEST_COLOR +# undef TUKTEST_TAP +# define TUKTEST_TAP 1 +# define TUKTEST_STR_PASS "ok -" +# define TUKTEST_STR_FAIL "not ok -" +# define TUKTEST_STR_SKIP "ok - # SKIP" +# define TUKTEST_STR_ERROR "Bail out!" +#else +# define TUKTEST_TAP 0 +# ifdef TUKTEST_COLOR +# define TUKTEST_COLOR_PASS "\x1B[0;32m" +# define TUKTEST_COLOR_FAIL "\x1B[0;31m" +# define TUKTEST_COLOR_SKIP "\x1B[1;34m" +# define TUKTEST_COLOR_ERROR "\x1B[0;35m" +# define TUKTEST_COLOR_TOTAL "\x1B[1m" +# define TUKTEST_COLOR_OFF "\x1B[m" +# define TUKTEST_COLOR_IF(cond, color) ((cond) ? (color) : "" ) +# else +# define TUKTEST_COLOR_PASS "" +# define TUKTEST_COLOR_FAIL "" +# define TUKTEST_COLOR_SKIP "" +# define TUKTEST_COLOR_ERROR "" +# define TUKTEST_COLOR_TOTAL "" +# define TUKTEST_COLOR_OFF "" +# define TUKTEST_COLOR_IF(cond, color) "" +# endif +# define TUKTEST_COLOR_ADD(str, color) color str TUKTEST_COLOR_OFF +# define TUKTEST_STR_PASS \ + TUKTEST_COLOR_ADD("PASS:", TUKTEST_COLOR_PASS) +# define TUKTEST_STR_FAIL \ + TUKTEST_COLOR_ADD("FAIL:", TUKTEST_COLOR_FAIL) +# define TUKTEST_STR_SKIP \ + TUKTEST_COLOR_ADD("SKIP:", TUKTEST_COLOR_SKIP) +# define TUKTEST_STR_ERROR \ + TUKTEST_COLOR_ADD("ERROR:", TUKTEST_COLOR_ERROR) +#endif + +// NOTE: If TUKTEST_TAP is defined then TUKTEST_QUIET will get undefined above. +#ifndef TUKTEST_QUIET +# define TUKTEST_QUIET 0 +#else +# undef TUKTEST_QUIET +# define TUKTEST_QUIET 1 +#endif + + +// Counts of the passed, failed, skipped, and hard-errored tests. +// This is indexed with the enumeration constants from enum tuktest_result. +static unsigned tuktest_stats[4] = { 0, 0, 0, 0 }; + +// Copy of argc and argv from main(). These are set by tuktest_start(). +static int tuktest_argc = 0; +static char **tuktest_argv = NULL; + +// Name of the currently-running test. This exists because it's nice +// to print the main test function name even if the failing test-assertion +// fails in a function called by the main test function. +static const char *tuktest_name = NULL; + +// longjmp() target for when a test-assertion fails. +static jmp_buf tuktest_jmpenv; + + +// This declaration is needed for tuktest_malloc(). +static int tuktest_end(void); + + +// Internal helper for handling hard errors both inside and +// outside tuktest_run(). +#define tuktest_error_impl(filename, line, ...) \ +do { \ + tuktest_print_result_prefix(TUKTEST_ERROR, filename, line); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + ++tuktest_stats[TUKTEST_ERROR]; \ + exit(tuktest_end()); \ +} while (0) + + +// printf() is without checking its return value in many places. This function +// is called before exiting to check the status of stdout and catch errors. +static void +tuktest_catch_stdout_errors(void) +{ + if (ferror(stdout) || fclose(stdout)) { + fputs("Error while writing to stdout\n", stderr); + exit(TUKTEST_EXIT_ERROR); + } +} + + +// A simplified basename()-like function that is good enough for +// cleaning up __FILE__. This supports / and \ as path separator. +// If the path separator is wrong then the full path will be printed; +// it's a cosmetic problem only. +static const char * +tuktest_basename(const char *filename) +{ + for (const char *p = filename + strlen(filename); p > filename; --p) + if (*p == '/' || *p == '\\') + return p + 1; + + return filename; +} + + +// Internal helper that prints the prefix of the fail/skip/error message line. +static void +tuktest_print_result_prefix(enum tuktest_result result, + const char *filename, unsigned line) +{ + // This is never called with TUKTEST_PASS but I kept it here anyway. + const char *result_str + = result == TUKTEST_PASS ? TUKTEST_STR_PASS + : result == TUKTEST_FAIL ? TUKTEST_STR_FAIL + : result == TUKTEST_SKIP ? TUKTEST_STR_SKIP + : TUKTEST_STR_ERROR; + + const char *short_filename = tuktest_basename(filename); + + if (tuktest_name != NULL) + printf("%s %s [%s:%u] ", result_str, tuktest_name, + short_filename, line); + else + printf("%s [%s:%u] ", result_str, short_filename, line); +} + + +// An entry for linked list of memory allocations. +struct tuktest_malloc_record { + struct tuktest_malloc_record *next; + void *p; +}; + +// Linked list of per-test allocations. This is used when under tuktest_run(). +// These allocations are freed in tuktest_run() and, in case of a hard error, +// also in tuktest_end(). +static struct tuktest_malloc_record *tuktest_malloc_test = NULL; + +// Linked list of global allocations. This is used allocations are made +// outside tuktest_run(). These are freed in tuktest_end(). +static struct tuktest_malloc_record *tuktest_malloc_global = NULL; + + +/// A wrapper for malloc() that never return NULL and the allocated memory is +/// automatically freed at the end of tuktest_run() (if allocation was done +/// within a test) or early in tuktest_end() (if allocation was done outside +/// tuktest_run()). +/// +/// If allocation fails, a hard error is reported and this function won't +/// return. Possible other tests won't be run (this will call exit()). +#define tuktest_malloc(size) tuktest_malloc_impl(size, __FILE__, __LINE__) + +static void * +tuktest_malloc_impl(size_t size, const char *filename, unsigned line) +{ + void *p = malloc(size == 0 ? 1 : size); + struct tuktest_malloc_record *r = malloc(sizeof(*r)); + + if (p == NULL || r == NULL) { + free(r); + free(p); + + // Avoid %zu for portability to very old systems that still + // can compile C99 code. + tuktest_error_impl(filename, line, + "tuktest_malloc(%" TUKTEST_PRIu ") failed", + (tuktest_uint)size); + } + + r->p = p; + + if (tuktest_name == NULL) { + // We were called outside tuktest_run(). + r->next = tuktest_malloc_global; + tuktest_malloc_global = r; + } else { + // We were called under tuktest_run(). + r->next = tuktest_malloc_test; + tuktest_malloc_test = r; + } + + return p; +} + + +/// Frees memory allocated using tuktest_malloc(). Usually this isn't needed +/// as the memory is freed automatically. +/// +/// NULL is silently ignored. +/// +/// NOTE: Under tuktest_run() only memory allocated there can be freed. +/// That is, allocations done outside tuktest_run() can only be freed +/// outside tuktest_run(). +#define tuktest_free(ptr) tuktest_free_impl(ptr, __FILE__, __LINE__) + +static void +tuktest_free_impl(void *p, const char *filename, unsigned line) +{ + if (p == NULL) + return; + + struct tuktest_malloc_record **r = tuktest_name != NULL + ? &tuktest_malloc_test : &tuktest_malloc_global; + + while (*r != NULL) { + struct tuktest_malloc_record *tmp = *r; + + if (tmp->p == p) { + *r = tmp->next; + free(p); + free(tmp); + return; + } + + r = &tmp->next; + } + + tuktest_error_impl(filename, line, "tuktest_free: " + "Allocation matching the pointer was not found"); +} + + +// Frees all allocates in the given record list. The argument must be +// either &tuktest_malloc_test or &tuktest_malloc_global. +static void +tuktest_free_all(struct tuktest_malloc_record **r) +{ + while (*r != NULL) { + struct tuktest_malloc_record *tmp = *r; + *r = tmp->next; + free(tmp->p); + free(tmp); + } +} + + +/// Initialize the test framework. No other functions or macros +/// from this file may be called before calling this. +/// +/// If the arguments from main() aren't available, use 0 and NULL. +/// If these are set, then only a subset of tests can be run by +/// specifying their names on the command line. +#define tuktest_start(argc, argv) \ +do { \ + tuktest_argc = argc; \ + tuktest_argv = argv; \ + if (!TUKTEST_TAP && !TUKTEST_QUIET) \ + printf("=== %s ===\n", tuktest_basename(__FILE__)); \ +} while (0) + + +/// If it can be detected early that no tests can be run, this macro can +/// be called after tuktest_start() but before any tuktest_run() to print +/// a reason why the tests were skipped. Note that this macro calls exit(). +/// +/// Using "return tuktest_end();" in main() when no tests were run has +/// the same result as tuktest_early_skip() except that then no reason +/// for the skipping can be printed. +#define tuktest_early_skip(...) \ +do { \ + printf("%s [%s:%u] ", \ + TUKTEST_TAP ? "1..0 # SKIP" : TUKTEST_STR_SKIP, \ + tuktest_basename(__FILE__), __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + if (!TUKTEST_TAP && !TUKTEST_QUIET) \ + printf("=== END ===\n"); \ + tuktest_catch_stdout_errors(); \ + exit(TUKTEST_TAP ? EXIT_SUCCESS : TUKTEST_EXIT_SKIP); \ +} while (0) + + +/// Some test programs need to do initializations before or between +/// calls to tuktest_run(). If such initializations unexpectedly fail, +/// tuktest_error() can be used to report it as a hard error outside +/// test functions, for example, in main(). Then the remaining tests +/// won't be run (this macro calls exit()). +/// +/// Typically tuktest_error() would be used before any tuktest_run() +/// calls but it is also possible to use tuktest_error() after one or +/// more tests have been run with tuktest_run(). This is in contrast to +/// tuktest_early_skip() which must never be called after tuktest_run(). +/// +/// NOTE: tuktest_start() must have been called before tuktest_error(). +/// +/// NOTE: This macro can be called from test functions running under +/// tuktest_run() but assert_error() is somewhat preferred in that context. +#define tuktest_error(...) tuktest_error_impl(__FILE__, __LINE__, __VA_ARGS__) + + +/// At the end of main() one should have "return tuktest_end();" which +/// prints the stats or the TAP plan, and handles the exit status. +/// Using exit(tuktest_end()) is OK too. +/// +/// If the test program can detect early that all tests must be skipped, +/// then tuktest_early_skip() may be useful so that the reason why the +/// tests were skipped can be printed. +static int +tuktest_end(void) +{ + tuktest_free_all(&tuktest_malloc_test); + tuktest_free_all(&tuktest_malloc_global); + + unsigned total_tests = 0; + for (unsigned i = 0; i <= TUKTEST_ERROR; ++i) + total_tests += tuktest_stats[i]; + + if (tuktest_stats[TUKTEST_ERROR] == 0 && tuktest_argc > 1 + && (unsigned)(tuktest_argc - 1) > total_tests) { + printf(TUKTEST_STR_ERROR " Fewer tests were run than " + "specified on the command line. " + "Was a test name mistyped?\n"); + ++tuktest_stats[TUKTEST_ERROR]; + } + +#if TUKTEST_TAP + // Print the plan only if no "Bail out!" has occurred. + // Print the skip directive if no tests were run. + // We cannot know the reason for the skip here though + // (see tuktest_early_skip()). + if (tuktest_stats[TUKTEST_ERROR] == 0) + printf("1..%u%s\n", total_tests, + total_tests == 0 ? " # SKIP" : ""); + + tuktest_catch_stdout_errors(); + return EXIT_SUCCESS; +#else + if (!TUKTEST_QUIET) + printf("---\n" + "%s# TOTAL: %u" TUKTEST_COLOR_OFF "\n" + "%s# PASS: %u" TUKTEST_COLOR_OFF "\n" + "%s# SKIP: %u" TUKTEST_COLOR_OFF "\n" + "%s# FAIL: %u" TUKTEST_COLOR_OFF "\n" + "%s# ERROR: %u" TUKTEST_COLOR_OFF "\n" + "=== END ===\n", + TUKTEST_COLOR_TOTAL, + total_tests, + TUKTEST_COLOR_IF( + tuktest_stats[TUKTEST_PASS] > 0, + TUKTEST_COLOR_PASS), + tuktest_stats[TUKTEST_PASS], + TUKTEST_COLOR_IF( + tuktest_stats[TUKTEST_SKIP] > 0, + TUKTEST_COLOR_SKIP), + tuktest_stats[TUKTEST_SKIP], + TUKTEST_COLOR_IF( + tuktest_stats[TUKTEST_FAIL] > 0, + TUKTEST_COLOR_FAIL), + tuktest_stats[TUKTEST_FAIL], + TUKTEST_COLOR_IF( + tuktest_stats[TUKTEST_ERROR] > 0, + TUKTEST_COLOR_ERROR), + tuktest_stats[TUKTEST_ERROR]); + + tuktest_catch_stdout_errors(); + + if (tuktest_stats[TUKTEST_ERROR] > 0) + return TUKTEST_EXIT_ERROR; + + if (tuktest_stats[TUKTEST_FAIL] > 0) + return TUKTEST_EXIT_FAIL; + + if (tuktest_stats[TUKTEST_SKIP] > 0 || total_tests == 0) + return TUKTEST_EXIT_SKIP; + + return TUKTEST_EXIT_PASS; +#endif +} + + +/// Runs the specified test function. Requires that tuktest_start() +/// has already been called and that tuktest_end() has NOT been called yet. +#define tuktest_run(testfunc) \ + tuktest_run_test(&(testfunc), #testfunc) + +tuktest_maybe_unused +static void +tuktest_run_test(void (*testfunc)(void), const char *testfunc_str) +{ + // If any command line arguments were given, only the test functions + // named on the command line will be run. + if (tuktest_argc > 1) { + int i = 1; + while (strcmp(tuktest_argv[i], testfunc_str) != 0) + if (++i == tuktest_argc) + return; + } + + // This is set so that failed assertions can print the correct + // test name even when the assertion is in a helper function + // called by the test function. + tuktest_name = testfunc_str; + + // The way setjmp() may be called is very restrictive. + // A switch statement is one of the few conforming ways + // to get the value passed to longjmp(); doing something + // like "int x = setjmp(env)" is NOT allowed (undefined behavior). + switch (setjmp(tuktest_jmpenv)) { + case 0: + testfunc(); + ++tuktest_stats[TUKTEST_PASS]; + if (!TUKTEST_QUIET) + printf(TUKTEST_STR_PASS " %s\n", tuktest_name); + break; + + case TUKTEST_FAIL: + ++tuktest_stats[TUKTEST_FAIL]; + break; + + case TUKTEST_SKIP: + ++tuktest_stats[TUKTEST_SKIP]; + break; + + default: + ++tuktest_stats[TUKTEST_ERROR]; + exit(tuktest_end()); + } + + tuktest_free_all(&tuktest_malloc_test); + tuktest_name = NULL; +} + + +// Maximum allowed file size in tuktest_file_from_* macros and functions. +#ifndef TUKTEST_FILE_SIZE_MAX +# define TUKTEST_FILE_SIZE_MAX (64L << 20) +#endif + +/// Allocates memory and reads the specified file into a buffer. +/// If the environment variable srcdir is set, it will be prefixed +/// to the filename. Otherwise the filename is used as is (and so +/// the behavior is identical to tuktest_file_from_builddir() below). +/// +/// On success the a pointer to malloc'ed memory is returned. +/// The size of the allocation and the file is stored in *size. +/// +/// If anything goes wrong, a hard error is reported and this function +/// won't return. Possible other tests won't be run (this will call exit()). +/// +/// Empty files and files over TUKTEST_FILE_SIZE_MAX are rejected. +/// The assumption is that something is wrong in these cases. +/// +/// This function can be called either from outside the tests (like in main()) +/// or from tests run via tuktest_run(). Remember to free() the memory to +/// keep Valgrind happy. +#define tuktest_file_from_srcdir(filename, sizeptr) \ + tuktest_file_from_x(getenv("srcdir"), filename, sizeptr, \ + __FILE__, __LINE__) + +/// Like tuktest_file_from_srcdir except this reads from the current directory. +#define tuktest_file_from_builddir(filename, sizeptr) \ + tuktest_file_from_x(NULL, filename, sizeptr, __FILE__, __LINE__) + +// Internal helper for the macros above. +tuktest_maybe_unused +static void * +tuktest_file_from_x(const char *prefix, const char *filename, size_t *size, + const char *prog_filename, unsigned prog_line) +{ + // If needed: buffer for holding prefix + '/' + filename + '\0'. + char *alloc_name = NULL; + + // Buffer for the data read from the file. + void *buf = NULL; + + // File being read + FILE *f = NULL; + + // Error message to use under the "error:" label. + const char *error_msg = NULL; + + if (filename == NULL) { + error_msg = "Filename is NULL"; + filename = "(NULL)"; + goto error; + } + + if (filename[0] == '\0') { + error_msg = "Filename is an empty string"; + filename = "(empty string)"; + goto error; + } + + if (size == NULL) { + error_msg = "The size argument is NULL"; + goto error; + } + + // If a prefix was given, construct the full filename. + if (prefix != NULL && prefix[0] != '\0') { + const size_t prefix_len = strlen(prefix); + const size_t filename_len = strlen(filename); + + const size_t alloc_name_size + = prefix_len + 1 + filename_len + 1; + alloc_name = tuktest_malloc_impl(alloc_name_size, + prog_filename, prog_line); + + memcpy(alloc_name, prefix, prefix_len); + alloc_name[prefix_len] = '/'; + memcpy(alloc_name + prefix_len + 1, filename, filename_len); + alloc_name[prefix_len + 1 + filename_len] = '\0'; + + // Set filename to point to the new string. alloc_name + // can be freed unconditionally as it is NULL if a prefix + // wasn't specified. + filename = alloc_name; + } + + f = fopen(filename, "rb"); + if (f == NULL) { + error_msg = "Failed to open the file"; + goto error; + } + + // Get the size of the file and store it in *size. + // + // We assume that the file isn't big and even reject very big files. + // There is no need to use fseeko/ftello from POSIX to support + // large files. Using standard C functions is portable outside POSIX. + if (fseek(f, 0, SEEK_END) != 0) { + error_msg = "Seeking failed (fseek end)"; + goto error; + } + + const long end = ftell(f); + if (end < 0) { + error_msg = "Seeking failed (ftell)"; + goto error; + } + + if (end == 0) { + error_msg = "File is empty"; + goto error; + } + + if (end > TUKTEST_FILE_SIZE_MAX) { + error_msg = "File size exceeds TUKTEST_FILE_SIZE_MAX"; + goto error; + } + + *size = (size_t)end; + rewind(f); + + buf = tuktest_malloc_impl(*size, prog_filename, prog_line); + + const size_t amount = fread(buf, 1, *size, f); + if (ferror(f)) { + error_msg = "Read error"; + goto error; + } + + if (amount != *size) { + error_msg = "File is smaller than indicated by ftell()"; + goto error; + } + + const int fclose_ret = fclose(f); + f = NULL; + if (fclose_ret != 0) { + error_msg = "Error closing the file"; + goto error; + } + + tuktest_free(alloc_name); + return buf; + +error: + if (f != NULL) + (void)fclose(f); + + tuktest_error_impl(prog_filename, prog_line, + "tuktest_file_from_x: %s: %s\n", filename, error_msg); +} + + +// Internal helper for assert_fail, assert_skip, and assert_error. +#define tuktest_print_and_jump(result, ...) \ +do { \ + tuktest_print_result_prefix(result, __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + longjmp(tuktest_jmpenv, result); \ +} while (0) + + +/// Unconditionally fails the test (non-zero exit status if not using TAP). +/// Execution will continue from the next test. +/// +/// A printf format string is supported. +/// If no extra message is wanted, use "" as the argument. +#define assert_fail(...) tuktest_print_and_jump(TUKTEST_FAIL, __VA_ARGS__) + + +/// Skips the test (exit status 77 if not using TAP). +/// Execution will continue from the next test. +/// +/// If you can detect early that no tests can be run, tuktest_early_skip() +/// might be a better way to skip the test(s). Especially in TAP mode this +/// makes a difference as with assert_skip() it will list a skipped specific +/// test name but with tuktest_early_skip() it will indicate that the whole +/// test program was skipped (with tuktest_early_skip() the TAP plan will +/// indicate zero tests). +/// +/// A printf format string is supported. +/// If no extra message is wanted, use "" as the argument. +#define assert_skip(...) tuktest_print_and_jump(TUKTEST_SKIP, __VA_ARGS__) + + +/// Hard error (exit status 99 if not using TAP). +/// The remaining tests in this program will not be run or reported. +/// +/// A printf format string is supported. +/// If no extra message is wanted, use "" as the argument. +#define assert_error(...) tuktest_print_and_jump(TUKTEST_ERROR, __VA_ARGS__) + + +/// Fails the test if the test expression doesn't evaluate to false. +#define assert_false(test_expr) \ +do { \ + if (test_expr) \ + assert_fail("assert_fail: '%s' is true but should be false", \ + #test_expr); \ +} while (0) + + +/// Fails the test if the test expression doesn't evaluate to true. +#define assert_true(test_expr) \ +do { \ + if (!(test_expr)) \ + assert_fail("assert_true: '%s' is false but should be true", \ + #test_expr); \ +} while (0) + + +/// Fails the test if comparing the signed integer expressions using the +/// specified comparison operator evaluates to false. For example, +/// assert_int(foobar(), >=, 0) fails the test if 'foobar() >= 0' isn't true. +/// For good error messages, the first argument should be the test expression +/// and the third argument the reference value (usually a constant). +/// +/// For equality (==) comparison there is a assert_int_eq() which +/// might be more convenient to use. +#define assert_int(test_expr, cmp_op, ref_value) \ +do { \ + const tuktest_int v_test_ = (test_expr); \ + const tuktest_int v_ref_ = (ref_value); \ + if (!(v_test_ cmp_op v_ref_)) \ + assert_fail("assert_int: '%s == %" TUKTEST_PRId \ + "' but expected '... %s %" TUKTEST_PRId "'", \ + #test_expr, v_test_, #cmp_op, v_ref_); \ +} while (0) + + +/// Like assert_int() but for unsigned integers. +/// +/// For equality (==) comparison there is a assert_uint_eq() which +/// might be more convenient to use. +#define assert_uint(test_expr, cmp_op, ref_value) \ +do { \ + const tuktest_uint v_test_ = (test_expr); \ + const tuktest_uint v_ref_ = (ref_value); \ + if (!(v_test_ cmp_op v_ref_)) \ + assert_fail("assert_uint: '%s == %" TUKTEST_PRIu \ + "' but expected '... %s %" TUKTEST_PRIu "'", \ + #test_expr, v_test_, #cmp_op, v_ref_); \ +} while (0) + + +/// Fails the test if test expression doesn't equal the expected +/// signed integer value. +#define assert_int_eq(test_expr, ref_value) \ + assert_int(test_expr, ==, ref_value) + + +/// Fails the test if test expression doesn't equal the expected +/// unsigned integer value. +#define assert_uint_eq(test_expr, ref_value) \ + assert_uint(test_expr, ==, ref_value) + + +/// Fails the test if the test expression doesn't equal the expected +/// enumeration value. This is like assert_int_eq() but the error message +/// shows the enumeration constant names instead of their numeric values +/// as long as the values are non-negative and not big. +/// +/// The third argument must be a table of string pointers. A pointer to +/// a pointer doesn't work because this determines the number of elements +/// in the array using sizeof. For example: +/// +/// const char *my_enum_names[] = { "MY_FOO", "MY_BAR", "MY_BAZ" }; +/// assert_enum_eq(some_func_returning_my_enum(), MY_BAR, my_enum_names); +/// +/// (If the reference value is out of bounds, both values are printed as +/// an integer. If only test expression is out of bounds, it is printed +/// as an integer and the reference as a string. Otherwise both are printed +/// as a string.) +#define assert_enum_eq(test_expr, ref_value, enum_strings) \ +do { \ + const tuktest_int v_test_ = (test_expr); \ + const tuktest_int v_ref_ = (ref_value); \ + if (v_test_ != v_ref_) { \ + const int array_len_ = (int)(sizeof(enum_strings) \ + / sizeof((enum_strings)[0])); \ + if (v_ref_ < 0 || v_ref_ >= array_len_) \ + assert_fail("assert_enum_eq: '%s == %" TUKTEST_PRId \ + "' but expected " \ + "'... == %" TUKTEST_PRId "'", \ + #test_expr, v_test_, v_ref_); \ + else if (v_test_ < 0 || v_test_ >= array_len_) \ + assert_fail("assert_enum_eq: '%s == %" TUKTEST_PRId \ + "' but expected '... == %s'", \ + #test_expr, v_test_, \ + (enum_strings)[v_ref_]); \ + else \ + assert_fail("assert_enum_eq: '%s == %s' " \ + "but expected '... = %s'", \ + #test_expr, (enum_strings)[v_test_], \ + (enum_strings)[v_ref_]); \ + } \ +} while (0) + + +/// Fails the test if the specified bit isn't set in the test expression. +#define assert_bit_set(test_expr, bit) \ +do { \ + const tuktest_uint v_test_ = (test_expr); \ + const unsigned v_bit_ = (bit); \ + const tuktest_uint v_mask_ = (tuktest_uint)1 << v_bit_; \ + if (!(v_test_ & v_mask_)) \ + assert_fail("assert_bit_set: '%s == 0x%" TUKTEST_PRIX \ + "' but bit %u (0x%" TUKTEST_PRIX ") " \ + "is not set", \ + #test_expr, v_test_, v_bit_, v_mask_); \ +} while (0) + + +/// Fails the test if the specified bit is set in the test expression. +#define assert_bit_not_set(test_expr, bit) \ +do { \ + const tuktest_uint v_test_ = (test_expr); \ + const unsigned v_bit_ = (bit); \ + const tuktest_uint v_mask_ = (tuktest_uint)1 << v_bit_; \ + if (v_test_ & v_mask_) \ + assert_fail("assert_bit_not_set: '%s == 0x%" TUKTEST_PRIX \ + "' but bit %u (0x%" TUKTEST_PRIX ") is set", \ + #test_expr, v_test_, v_bit_, v_mask_); \ +} while (0) + + +/// Fails the test if unless all bits that are set in the bitmask are also +/// set in the test expression. +#define assert_bitmask_set(test_expr, mask) \ +do { \ + const tuktest_uint v_mask_ = (mask); \ + const tuktest_uint v_test_ = (test_expr) & v_mask_; \ + if (v_test_ != v_mask_) \ + assert_fail("assert_bitmask_set: " \ + "'((%s) & 0x%" TUKTEST_PRIX ") == " \ + "0x%" TUKTEST_PRIX "' but expected " \ + "'... == 0x%" TUKTEST_PRIX "'", \ + #test_expr, v_mask_, v_test_, v_mask_); \ +} while (0) + + +/// Fails the test if any of the bits that are set in the bitmask are also +/// set in the test expression. +#define assert_bitmask_not_set(test_expr, mask) \ +do { \ + const tuktest_uint v_mask_ = (mask); \ + const tuktest_uint v_test_ = (test_expr) & v_mask_; \ + if (v_test_ != 0) \ + assert_fail("assert_bitmask_not_set: "\ + "'((%s) & 0x%" TUKTEST_PRIX ") == " \ + "0x%" TUKTEST_PRIX "' but expected " \ + "'... == 0'", \ + #test_expr, v_mask_, v_test_); \ +} while (0) + + +// Internal helper to add common code for string assertions. +#define tuktest_str_helper1(macro_name, test_expr, ref_value) \ + const char *v_test_ = (test_expr); \ + const char *v_ref_ = (ref_value); \ + if (v_test_ == NULL) \ + assert_fail(macro_name ": Test expression '%s' is NULL", \ + #test_expr); \ + if (v_ref_ == NULL) \ + assert_fail(macro_name ": Reference value '%s' is NULL", \ + #ref_value) + + +// Internal helper to add common code for string assertions and to check +// that the reference value isn't an empty string. +#define tuktest_str_helper2(macro_name, test_expr, ref_value) \ + tuktest_str_helper1(macro_name, test_expr, ref_value); \ + if (v_ref_[0] == '\0') \ + assert_fail(macro_name ": Reference value is an empty string") + + +/// Fails the test if the test expression evaluates to string that doesn't +/// equal to the expected string. +#define assert_str_eq(test_expr, ref_value) \ +do { \ + tuktest_str_helper1("assert_str_eq", test_expr, ref_value); \ + if (strcmp(v_ref_, v_test_) != 0) \ + assert_fail("assert_str_eq: '%s' evaluated to '%s' " \ + "but expected '%s'", \ + #test_expr, v_test_, v_ref_); \ +} while (0) + + +/// Fails the test if the test expression evaluates to a string that doesn't +/// contain the reference value as a substring. Also fails the test if +/// the reference value is an empty string. +#define assert_str_contains(test_expr, ref_value) \ +do { \ + tuktest_str_helper2("assert_str_contains", test_expr, ref_value); \ + if (strstr(v_test_, v_ref_) == NULL) \ + assert_fail("assert_str_contains: '%s' evaluated to '%s' " \ + "which doesn't contain '%s'", \ + #test_expr, v_test_, v_ref_); \ +} while (0) + + +/// Fails the test if the test expression evaluates to a string that +/// contains the reference value as a substring. Also fails the test if +/// the reference value is an empty string. +#define assert_str_doesnt_contain(test_expr, ref_value) \ +do { \ + tuktest_str_helper2("assert_str_doesnt_contain", \ + test_expr, ref_value); \ + if (strstr(v_test_, v_ref_) != NULL) \ + assert_fail("assert_str_doesnt_contain: " \ + "'%s' evaluated to '%s' which contains '%s'", \ + #test_expr, v_test_, v_ref_); \ +} while (0) + + +/// Fails the test if the first array_size elements of the test array +/// don't equal to correct_array. +/// +/// NOTE: This avoids %zu for portability to very old systems that still +/// can compile C99 code. +#define assert_array_eq(test_array, correct_array, array_size) \ +do { \ + for (size_t i_ = 0; i_ < (array_size); ++i_) \ + if ((test_array)[i_] != (correct_array)[i_]) \ + assert_fail("assert_array_eq: " \ + "%s[%" TUKTEST_PRIu "] != "\ + "%s[%" TUKTEST_PRIu "] " \ + "but should be equal", \ + #test_array, (tuktest_uint)i_, \ + #correct_array, (tuktest_uint)i_); \ +} while (0) + +#endif diff --git a/tests/xzgrep_expected_output b/tests/xzgrep_expected_output new file mode 100644 index 0000000..e531d93 --- /dev/null +++ b/tests/xzgrep_expected_output @@ -0,0 +1,39 @@ +=> xzgrep el <= +xzgrep_test_1.xz:elit, sed do eiusmod tempor incididunt ut +xzgrep_test_1.xz:in voluptate velit esse cillum dolore eu +xzgrep_test_2.xz:Hello +retval 0 +=> xzgrep -l el <= +xzgrep_test_1.xz +xzgrep_test_2.xz +retval 0 +=> xzgrep -h el <= +elit, sed do eiusmod tempor incididunt ut +in voluptate velit esse cillum dolore eu +Hello +retval 0 +=> xzgrep -H el <= +xzgrep_test_1.xz:elit, sed do eiusmod tempor incididunt ut +xzgrep_test_1.xz:in voluptate velit esse cillum dolore eu +xzgrep_test_2.xz:Hello +retval 0 +=> xzgrep Hello <= +xzgrep_test_2.xz:Hello +retval 0 +=> xzgrep -l Hello <= +xzgrep_test_2.xz +retval 0 +=> xzgrep -h Hello <= +Hello +retval 0 +=> xzgrep -H Hello <= +xzgrep_test_2.xz:Hello +retval 0 +=> xzgrep NOMATCH <= +retval 1 +=> xzgrep -l NOMATCH <= +retval 1 +=> xzgrep -h NOMATCH <= +retval 1 +=> xzgrep -H NOMATCH <= +retval 1 |