diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:12:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:12:04 +0000 |
commit | eac54b7c4aec25060d7bd856f7cdc290943d6aae (patch) | |
tree | 9a6d81c9f88df4698e746d63d14ddafeddd918b8 /tests | |
parent | Initial commit. (diff) | |
download | xz-utils-upstream.tar.xz xz-utils-upstream.zip |
Adding upstream version 5.4.1.upstream/5.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
130 files changed, 7997 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..b1a2378 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,79 @@ +## +## 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 \ + -I$(top_builddir)/lib + +LDADD = $(top_builddir)/src/liblzma/liblzma.la + +if COND_GNULIB +LDADD += $(top_builddir)/lib/libgnu.a +endif + +LDADD += $(LTLIBINTL) + +check_PROGRAMS = \ + create_compress_files \ + test_check \ + test_hardware \ + test_stream_flags \ + test_filter_flags \ + 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_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..c329d00 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,1337 @@ +# 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@ +@COND_GNULIB_TRUE@am__append_1 = $(top_builddir)/lib/libgnu.a +check_PROGRAMS = create_compress_files$(EXEEXT) test_check$(EXEEXT) \ + test_hardware$(EXEEXT) test_stream_flags$(EXEEXT) \ + test_filter_flags$(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_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_2) +@COND_SCRIPTS_TRUE@am__append_2 = 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__append_1) \ + $(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__append_1) \ + $(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__append_1) \ + $(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__append_1) $(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__append_1) \ + $(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__append_1) $(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__append_1) $(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__append_1) $(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__append_1) \ + $(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__append_1) $(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__append_1) \ + $(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__append_1) $(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_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_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_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 \ + -I$(top_builddir)/lib + +LDADD = $(top_builddir)/src/liblzma/liblzma.la $(am__append_1) \ + $(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_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_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_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_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_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..747925b --- /dev/null +++ b/tests/test_block_header.c @@ -0,0 +1,514 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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; + + +#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 = 0x1000; + 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 = 0x1000; + 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 + memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX); + memzero(&decoded_block, sizeof(lzma_block)); + decoded_block.filters = decoded_filters; + decoded_block.check = LZMA_CHECK_CRC32; + + // 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); + + memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX); + memzero(&decoded_block, sizeof(lzma_block)); + decoded_block.filters = decoded_filters; + decoded_block.check = LZMA_CHECK_CRC32; + + // 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); + + memzero(out, LZMA_BLOCK_HEADER_SIZE_MAX); + memzero(&decoded_block, sizeof(lzma_block)); + decoded_block.filters = decoded_filters; + decoded_block.check = LZMA_CHECK_CRC32; + + // 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); + + memzero(&decoded_block, sizeof(lzma_block)); + decoded_block.filters = decoded_filters; + decoded_block.check = LZMA_CHECK_CRC32; + decoded_block.header_size = lzma_block_header_size_decode(out[0]); + + // 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); + + // Test bad check type + decoded_block.check = LZMA_CHECK_ID_MAX + 1; + 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..bc52f40 --- /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 int 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 (int 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 (unsigned int 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(LZMA_CHECK_ID_MAX + 1), 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..61d20ce --- /dev/null +++ b/tests/test_compress.sh @@ -0,0 +1,149 @@ +#!/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" ../config.h > /dev/null || return + grep "define HAVE_DECODER_$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 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..64d7308 --- /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" ../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 grep 'define HAVE_LZIP_DECODER' ../config.h > /dev/null ; 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..5cfccea --- /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 termianted 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 Properites 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_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..31b958d --- /dev/null +++ b/tests/test_index.c @@ -0,0 +1,736 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file test_index.c +/// \brief Tests functions handling the lzma_index structure +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "tests.h" + +#define MEMLIMIT (LZMA_VLI_C(1) << 20) + +#define SMALL_COUNT 3 +#define BIG_COUNT 5555 + + +static lzma_index * +create_empty(void) +{ + lzma_index *i = lzma_index_init(NULL); + expect(i != NULL); + return i; +} + + +static lzma_index * +create_small(void) +{ + lzma_index *i = lzma_index_init(NULL); + expect(i != NULL); + expect(lzma_index_append(i, NULL, 101, 555) == LZMA_OK); + expect(lzma_index_append(i, NULL, 602, 777) == LZMA_OK); + expect(lzma_index_append(i, NULL, 804, 999) == LZMA_OK); + return i; +} + + +static lzma_index * +create_big(void) +{ + lzma_index *i = lzma_index_init(NULL); + expect(i != NULL); + + lzma_vli total_size = 0; + lzma_vli uncompressed_size = 0; + + // Add pseudo-random sizes (but always the same size values). + uint32_t n = 11; + for (size_t j = 0; j < BIG_COUNT; ++j) { + n = 7019 * n + 7607; + const uint32_t t = n * 3011; + expect(lzma_index_append(i, NULL, t, n) == LZMA_OK); + total_size += (t + 3) & ~LZMA_VLI_C(3); + uncompressed_size += n; + } + + expect(lzma_index_block_count(i) == BIG_COUNT); + expect(lzma_index_total_size(i) == total_size); + expect(lzma_index_uncompressed_size(i) == uncompressed_size); + expect(lzma_index_total_size(i) + lzma_index_size(i) + + 2 * LZMA_STREAM_HEADER_SIZE + == lzma_index_stream_size(i)); + + return i; +} + + +static bool +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 (reta) + return !(reta ^ 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; + } +} + + +static void +test_equal(void) +{ + lzma_index *a = create_empty(); + lzma_index *b = create_small(); + lzma_index *c = create_big(); + expect(a && b && c); + + expect(is_equal(a, a)); + expect(is_equal(b, b)); + expect(is_equal(c, c)); + + expect(!is_equal(a, b)); + expect(!is_equal(a, c)); + expect(!is_equal(b, c)); + + lzma_index_end(a, NULL); + lzma_index_end(b, NULL); + lzma_index_end(c, NULL); +} + + +static void +test_overflow(void) +{ + // Integer overflow tests + lzma_index *i = create_empty(); + + expect(lzma_index_append(i, NULL, LZMA_VLI_MAX - 5, 1234) + == LZMA_DATA_ERROR); + + // TODO + + lzma_index_end(i, NULL); +} + + +static void +test_copy(const lzma_index *i) +{ + lzma_index *d = lzma_index_dup(i, NULL); + expect(d != NULL); + expect(is_equal(i, d)); + lzma_index_end(d, NULL); +} + + +static void +test_read(lzma_index *i) +{ + lzma_index_iter r; + lzma_index_iter_init(&r, i); + + // Try twice so we see that rewinding works. + for (size_t j = 0; j < 2; ++j) { + lzma_vli total_size = 0; + lzma_vli uncompressed_size = 0; + lzma_vli stream_offset = LZMA_STREAM_HEADER_SIZE; + lzma_vli uncompressed_offset = 0; + uint32_t count = 0; + + while (!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)) { + ++count; + + total_size += r.block.total_size; + uncompressed_size += r.block.uncompressed_size; + + expect(r.block.compressed_file_offset + == stream_offset); + expect(r.block.uncompressed_file_offset + == uncompressed_offset); + + stream_offset += r.block.total_size; + uncompressed_offset += r.block.uncompressed_size; + } + + expect(lzma_index_total_size(i) == total_size); + expect(lzma_index_uncompressed_size(i) == uncompressed_size); + expect(lzma_index_block_count(i) == count); + + lzma_index_iter_rewind(&r); + } +} + + +static void +test_code(lzma_index *i) +{ +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) + const size_t alloc_size = 128 * 1024; + uint8_t *buf = malloc(alloc_size); + expect(buf != NULL); + + // Encode + lzma_stream strm = LZMA_STREAM_INIT; + expect(lzma_index_encoder(&strm, i) == LZMA_OK); + const lzma_vli index_size = lzma_index_size(i); + succeed(coder_loop(&strm, NULL, 0, buf, index_size, + LZMA_STREAM_END, LZMA_RUN)); + + // Decode + lzma_index *d; + expect(lzma_index_decoder(&strm, &d, MEMLIMIT) == LZMA_OK); + expect(d == NULL); + succeed(decoder_loop(&strm, buf, index_size)); + + expect(is_equal(i, d)); + + lzma_index_end(d, NULL); + lzma_end(&strm); + + // Decode with hashing + lzma_index_hash *h = lzma_index_hash_init(NULL, NULL); + expect(h != NULL); + lzma_index_iter r; + lzma_index_iter_init(&r, i); + while (!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)) + expect(lzma_index_hash_append(h, r.block.unpadded_size, + r.block.uncompressed_size) == LZMA_OK); + size_t pos = 0; + while (pos < index_size - 1) + expect(lzma_index_hash_decode(h, buf, &pos, pos + 1) + == LZMA_OK); + expect(lzma_index_hash_decode(h, buf, &pos, pos + 1) + == LZMA_STREAM_END); + + lzma_index_hash_end(h, NULL); + + // Encode buffer + size_t buf_pos = 1; + expect(lzma_index_buffer_encode(i, buf, &buf_pos, index_size) + == LZMA_BUF_ERROR); + expect(buf_pos == 1); + + succeed(lzma_index_buffer_encode(i, buf, &buf_pos, index_size + 1)); + expect(buf_pos == index_size + 1); + + // Decode buffer + buf_pos = 1; + uint64_t memlimit = MEMLIMIT; + expect(lzma_index_buffer_decode(&d, &memlimit, NULL, buf, &buf_pos, + index_size) == LZMA_DATA_ERROR); + expect(buf_pos == 1); + expect(d == NULL); + + succeed(lzma_index_buffer_decode(&d, &memlimit, NULL, buf, &buf_pos, + index_size + 1)); + expect(buf_pos == index_size + 1); + expect(is_equal(i, d)); + + lzma_index_end(d, NULL); + + free(buf); +#else + (void)i; +#endif +} + + +static void +test_many(lzma_index *i) +{ + test_copy(i); + test_read(i); + test_code(i); +} + + +static void +test_cat(void) +{ + lzma_index *a, *b, *c, *d, *e, *f; + lzma_index_iter r; + + // Empty Indexes + a = create_empty(); + b = create_empty(); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_block_count(a) == 0); + expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); + expect(lzma_index_file_size(a) + == 2 * (2 * LZMA_STREAM_HEADER_SIZE + 8)); + lzma_index_iter_init(&r, a); + expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); + + b = create_empty(); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_block_count(a) == 0); + expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); + expect(lzma_index_file_size(a) + == 3 * (2 * LZMA_STREAM_HEADER_SIZE + 8)); + + b = create_empty(); + c = create_empty(); + expect(lzma_index_stream_padding(b, 4) == LZMA_OK); + expect(lzma_index_cat(b, c, NULL) == LZMA_OK); + expect(lzma_index_block_count(b) == 0); + expect(lzma_index_stream_size(b) == 2 * LZMA_STREAM_HEADER_SIZE + 8); + expect(lzma_index_file_size(b) + == 2 * (2 * LZMA_STREAM_HEADER_SIZE + 8) + 4); + + expect(lzma_index_stream_padding(a, 8) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_block_count(a) == 0); + expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); + expect(lzma_index_file_size(a) + == 5 * (2 * LZMA_STREAM_HEADER_SIZE + 8) + 4 + 8); + + expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); + lzma_index_iter_rewind(&r); + expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); + lzma_index_end(a, NULL); + + // Small Indexes + a = create_small(); + lzma_vli stream_size = lzma_index_stream_size(a); + lzma_index_iter_init(&r, a); + for (int i = SMALL_COUNT; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + b = create_small(); + expect(lzma_index_stream_padding(a, 4) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_file_size(a) == stream_size * 2 + 4); + expect(lzma_index_stream_size(a) > stream_size); + expect(lzma_index_stream_size(a) < stream_size * 2); + for (int i = SMALL_COUNT; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + lzma_index_iter_rewind(&r); + for (int i = SMALL_COUNT * 2; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + b = create_small(); + c = create_small(); + expect(lzma_index_stream_padding(b, 8) == LZMA_OK); + expect(lzma_index_cat(b, c, NULL) == LZMA_OK); + expect(lzma_index_stream_padding(a, 12) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_file_size(a) == stream_size * 4 + 4 + 8 + 12); + + expect(lzma_index_block_count(a) == SMALL_COUNT * 4); + for (int i = SMALL_COUNT * 2; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + lzma_index_iter_rewind(&r); + for (int i = SMALL_COUNT * 4; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + lzma_index_end(a, NULL); + + // Mix of empty and small + a = create_empty(); + b = create_small(); + expect(lzma_index_stream_padding(a, 4) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + lzma_index_iter_init(&r, a); + for (int i = SMALL_COUNT; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + lzma_index_end(a, NULL); + + // Big Indexes + a = create_big(); + stream_size = lzma_index_stream_size(a); + b = create_big(); + expect(lzma_index_stream_padding(a, 4) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_file_size(a) == stream_size * 2 + 4); + expect(lzma_index_stream_size(a) > stream_size); + expect(lzma_index_stream_size(a) < stream_size * 2); + + b = create_big(); + c = create_big(); + expect(lzma_index_stream_padding(b, 8) == LZMA_OK); + expect(lzma_index_cat(b, c, NULL) == LZMA_OK); + expect(lzma_index_stream_padding(a, 12) == LZMA_OK); + expect(lzma_index_cat(a, b, NULL) == LZMA_OK); + expect(lzma_index_file_size(a) == stream_size * 4 + 4 + 8 + 12); + + lzma_index_iter_init(&r, a); + for (int i = BIG_COUNT * 4; i >= 0; --i) + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) + ^ (i == 0)); + + lzma_index_end(a, NULL); + + // Test for the bug fix 3d5a99ca373a4e86faf671226ca6487febb9eeac. + // lzma_index_checks would previously only return the checks + // for the last stream that was concatenated to the index. + d = create_small(); + e = create_small(); + f = create_small(); + + lzma_stream_flags crc32_flags = { + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + expect(lzma_index_stream_flags(d, &crc32_flags) == LZMA_OK); + + lzma_stream_flags crc64_flags = { + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC64 + }; + expect(lzma_index_stream_flags(e, &crc64_flags) == LZMA_OK); + + lzma_stream_flags sha256_flags = { + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_SHA256 + }; + expect(lzma_index_stream_flags(f, &sha256_flags) == LZMA_OK); + + expect(lzma_index_checks(d) == (1U << LZMA_CHECK_CRC32)); + expect(lzma_index_checks(e) == (1U << LZMA_CHECK_CRC64)); + expect(lzma_index_checks(f) == (1U << LZMA_CHECK_SHA256)); + + expect(lzma_index_cat(d, e, NULL) == LZMA_OK); + expect(lzma_index_checks(d) == ((1U << LZMA_CHECK_CRC32) | + (1U << LZMA_CHECK_CRC64))); + + expect(lzma_index_cat(d, f, NULL) == LZMA_OK); + expect(lzma_index_checks(d) == ((1U << LZMA_CHECK_CRC32) | + (1U << LZMA_CHECK_CRC64) | + (1U << LZMA_CHECK_SHA256))); + + lzma_index_end(d, NULL); + +} + + +static void +test_locate(void) +{ + lzma_index *i = lzma_index_init(NULL); + expect(i != NULL); + lzma_index_iter r; + lzma_index_iter_init(&r, i); + + // Cannot locate anything from an empty Index. + expect(lzma_index_iter_locate(&r, 0)); + expect(lzma_index_iter_locate(&r, 555)); + + // One empty Record: nothing is found since there's no uncompressed + // data. + expect(lzma_index_append(i, NULL, 16, 0) == LZMA_OK); + expect(lzma_index_iter_locate(&r, 0)); + + // Non-empty Record and we can find something. + expect(lzma_index_append(i, NULL, 32, 5) == LZMA_OK); + expect(!lzma_index_iter_locate(&r, 0)); + expect(r.block.total_size == 32); + expect(r.block.uncompressed_size == 5); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16); + expect(r.block.uncompressed_file_offset == 0); + + // Still cannot find anything past the end. + expect(lzma_index_iter_locate(&r, 5)); + + // Add the third Record. + expect(lzma_index_append(i, NULL, 40, 11) == LZMA_OK); + + expect(!lzma_index_iter_locate(&r, 0)); + expect(r.block.total_size == 32); + expect(r.block.uncompressed_size == 5); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16); + expect(r.block.uncompressed_file_offset == 0); + + expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); + expect(r.block.total_size == 40); + expect(r.block.uncompressed_size == 11); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16 + 32); + expect(r.block.uncompressed_file_offset == 5); + + expect(!lzma_index_iter_locate(&r, 2)); + expect(r.block.total_size == 32); + expect(r.block.uncompressed_size == 5); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16); + expect(r.block.uncompressed_file_offset == 0); + + expect(!lzma_index_iter_locate(&r, 5)); + expect(r.block.total_size == 40); + expect(r.block.uncompressed_size == 11); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16 + 32); + expect(r.block.uncompressed_file_offset == 5); + + expect(!lzma_index_iter_locate(&r, 5 + 11 - 1)); + expect(r.block.total_size == 40); + expect(r.block.uncompressed_size == 11); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 16 + 32); + expect(r.block.uncompressed_file_offset == 5); + + expect(lzma_index_iter_locate(&r, 5 + 11)); + expect(lzma_index_iter_locate(&r, 5 + 15)); + + // Large Index + lzma_index_end(i, NULL); + i = lzma_index_init(NULL); + expect(i != NULL); + lzma_index_iter_init(&r, i); + + for (size_t n = 4; n <= 4 * 5555; n += 4) + expect(lzma_index_append(i, NULL, n + 8, n) == LZMA_OK); + + expect(lzma_index_block_count(i) == 5555); + + // First Record + expect(!lzma_index_iter_locate(&r, 0)); + expect(r.block.total_size == 4 + 8); + expect(r.block.uncompressed_size == 4); + expect(r.block.compressed_file_offset == LZMA_STREAM_HEADER_SIZE); + expect(r.block.uncompressed_file_offset == 0); + + expect(!lzma_index_iter_locate(&r, 3)); + expect(r.block.total_size == 4 + 8); + expect(r.block.uncompressed_size == 4); + expect(r.block.compressed_file_offset == LZMA_STREAM_HEADER_SIZE); + expect(r.block.uncompressed_file_offset == 0); + + // Second Record + expect(!lzma_index_iter_locate(&r, 4)); + expect(r.block.total_size == 2 * 4 + 8); + expect(r.block.uncompressed_size == 2 * 4); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + 4 + 8); + expect(r.block.uncompressed_file_offset == 4); + + // Last Record + expect(!lzma_index_iter_locate( + &r, lzma_index_uncompressed_size(i) - 1)); + expect(r.block.total_size == 4 * 5555 + 8); + expect(r.block.uncompressed_size == 4 * 5555); + expect(r.block.compressed_file_offset == lzma_index_total_size(i) + + LZMA_STREAM_HEADER_SIZE - 4 * 5555 - 8); + expect(r.block.uncompressed_file_offset + == lzma_index_uncompressed_size(i) - 4 * 5555); + + // Allocation chunk boundaries. See INDEX_GROUP_SIZE in + // liblzma/common/index.c. + const size_t group_multiple = 256 * 4; + const size_t radius = 8; + const size_t start = group_multiple - radius; + lzma_vli ubase = 0; + lzma_vli tbase = 0; + size_t n; + for (n = 1; n < start; ++n) { + ubase += n * 4; + tbase += n * 4 + 8; + } + + while (n < start + 2 * radius) { + expect(!lzma_index_iter_locate(&r, ubase + n * 4)); + + expect(r.block.compressed_file_offset == tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + expect(r.block.uncompressed_file_offset == ubase + n * 4); + + tbase += n * 4 + 8; + ubase += n * 4; + ++n; + + expect(r.block.total_size == n * 4 + 8); + expect(r.block.uncompressed_size == n * 4); + } + + // Do it also backwards. + while (n > start) { + expect(!lzma_index_iter_locate(&r, ubase + (n - 1) * 4)); + + expect(r.block.total_size == n * 4 + 8); + expect(r.block.uncompressed_size == n * 4); + + --n; + tbase -= n * 4 + 8; + ubase -= n * 4; + + expect(r.block.compressed_file_offset == tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + expect(r.block.uncompressed_file_offset == ubase + n * 4); + } + + // Test locating in concatenated Index. + lzma_index_end(i, NULL); + i = lzma_index_init(NULL); + expect(i != NULL); + lzma_index_iter_init(&r, i); + for (n = 0; n < group_multiple; ++n) + expect(lzma_index_append(i, NULL, 8, 0) == LZMA_OK); + expect(lzma_index_append(i, NULL, 16, 1) == LZMA_OK); + expect(!lzma_index_iter_locate(&r, 0)); + expect(r.block.total_size == 16); + expect(r.block.uncompressed_size == 1); + expect(r.block.compressed_file_offset + == LZMA_STREAM_HEADER_SIZE + group_multiple * 8); + expect(r.block.uncompressed_file_offset == 0); + + lzma_index_end(i, NULL); +} + + +static void +test_corrupt(void) +{ +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) + const size_t alloc_size = 128 * 1024; + uint8_t *buf = malloc(alloc_size); + expect(buf != NULL); + lzma_stream strm = LZMA_STREAM_INIT; + + lzma_index *i = create_empty(); + expect(lzma_index_append(i, NULL, 0, 1) == LZMA_PROG_ERROR); + lzma_index_end(i, NULL); + + // Create a valid Index and corrupt it in different ways. + i = create_small(); + expect(lzma_index_encoder(&strm, i) == LZMA_OK); + succeed(coder_loop(&strm, NULL, 0, buf, 20, + LZMA_STREAM_END, LZMA_RUN)); + lzma_index_end(i, NULL); + + // Wrong Index Indicator + buf[0] ^= 1; + expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); + succeed(decoder_loop_ret(&strm, buf, 1, LZMA_DATA_ERROR)); + buf[0] ^= 1; + + // Wrong Number of Records and thus CRC32 fails. + --buf[1]; + expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); + succeed(decoder_loop_ret(&strm, buf, 10, LZMA_DATA_ERROR)); + ++buf[1]; + + // Padding not NULs + buf[15] ^= 1; + expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); + succeed(decoder_loop_ret(&strm, buf, 16, LZMA_DATA_ERROR)); + + lzma_end(&strm); + free(buf); +#endif +} + + +// 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 my_allocator = { &my_alloc, NULL, NULL }; + + +int +main(void) +{ + test_equal(); + + test_overflow(); + + lzma_index *i = create_empty(); + test_many(i); + lzma_index_end(i, NULL); + + i = create_small(); + test_many(i); + lzma_index_end(i, NULL); + + i = create_big(); + test_many(i); + lzma_index_end(i, NULL); + + test_cat(); + + test_locate(); + + test_corrupt(); + + // Test for the bug fix 21515d79d778b8730a434f151b07202d52a04611: + // liblzma: Fix lzma_index_dup() for empty Streams. + i = create_empty(); + expect(lzma_index_stream_padding(i, 4) == LZMA_OK); + test_copy(i); + lzma_index_end(i, 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. + i = create_small(); + expect(lzma_index_dup(i, &my_allocator) == NULL); + lzma_index_end(i, NULL); + + return 0; +} 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..306de74 --- /dev/null +++ b/tests/test_lzip_decoder.c @@ -0,0 +1,475 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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, + 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, 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, + 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, + 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..b8ec546 --- /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 (int 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 = LZMA_CHECK_ID_MAX + 1; + 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 (int 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 = LZMA_CHECK_ID_MAX + 1; + 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 (int 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 (int 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 = LZMA_CHECK_ID_MAX + 1; + assert_lzma_ret(lzma_stream_flags_compare(&first, &second), + LZMA_PROG_ERROR); + second.check = LZMA_CHECK_ID_MAX + 1; + 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 (uint32_t 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..793dcf2 --- /dev/null +++ b/tests/test_vli.c @@ -0,0 +1,322 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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 +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; + + +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 agaist 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..4d6169b --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,141 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \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" + + +#define memcrap(buf, size) memset(buf, 0xFD, size) + + +// TODO: Remove these three macros once all tests have been converted. +#define expect(test) ((test) ? 0 : (fprintf(stderr, "%s:%d: %s\n", \ + __FILE__, __LINE__, #test), abort(), 0)) + +#define succeed(test) expect(!(test)) + +#define fail(test) expect(test) + + +// 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) + + +static inline bool +coder_loop(lzma_stream *strm, uint8_t *in, size_t in_size, + uint8_t *out, size_t out_size, + lzma_ret expected_ret, lzma_action finishing_action) +{ + size_t in_left = in_size; + size_t out_left = out_size > 0 ? out_size + 1 : 0; + lzma_action action = LZMA_RUN; + lzma_ret ret; + + strm->next_in = NULL; + strm->avail_in = 0; + strm->next_out = NULL; + strm->avail_out = 0; + + while (true) { + if (in_left > 0) { + if (--in_left == 0) + action = finishing_action; + + strm->next_in = in++; + strm->avail_in = 1; + } + + if (out_left > 0) { + --out_left; + strm->next_out = out++; + strm->avail_out = 1; + } + + ret = lzma_code(strm, action); + if (ret != LZMA_OK) + break; + } + + bool error = false; + + if (ret != expected_ret) + error = true; + + if (strm->total_in != in_size || strm->total_out != out_size) + error = true; + + return error; +} + + +static inline bool +decoder_loop_ret(lzma_stream *strm, uint8_t *in, size_t in_size, + lzma_ret expected_ret) +{ + return coder_loop(strm, in, in_size, NULL, 0, expected_ret, LZMA_RUN); +} + + +static inline bool +decoder_loop(lzma_stream *strm, uint8_t *in, size_t in_size) +{ + return coder_loop(strm, in, in_size, NULL, 0, + LZMA_STREAM_END, LZMA_RUN); +} + +#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 |