diff --git a/configure.ac b/configure.ac index 34364c8..a94d021 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.71]) AC_INIT( [liblnk], - [20230928], + [20231001], [joachim.metz@gmail.com]) AC_CONFIG_SRCDIR( diff --git a/liblnk.nuspec b/liblnk.nuspec index 82693ed..21cda89 100644 --- a/liblnk.nuspec +++ b/liblnk.nuspec @@ -2,7 +2,7 @@ liblnk - 20230928 + 20231001 Joachim Metz joachimmetz LGPL-3.0-or-later @@ -10,7 +10,7 @@ false liblnk Library to access the Windows Shortcut File (LNK) format - Release of liblnk 20230928 + Release of liblnk 20231001 Copyright (C) 2009-2023 native diff --git a/tests/lnk_test_libcpath.h b/tests/lnk_test_libcpath.h index 7534744..20abda8 100644 --- a/tests/lnk_test_libcpath.h +++ b/tests/lnk_test_libcpath.h @@ -1,7 +1,7 @@ /* - * The internal libcpath header + * The libcpath header wrapper * - * Copyright (C) 2009-2022, Joachim Metz + * Copyright (C) 2009-2023, Joachim Metz * * Refer to AUTHORS for acknowledgements. * diff --git a/tests/runtests.py b/tests/runtests.py index 0a9484d..6b05f11 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -2,22 +2,7 @@ # # Script to run Python test scripts. # -# Copyright (C) 2009-2023, Joachim Metz -# -# Refer to AUTHORS for acknowledgements. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . +# Version: 20231001 import glob import os @@ -25,6 +10,28 @@ import unittest +test_profile = ".pylnk" +input_glob = "*.lnk" +option_sets = [] + + +def ReadIgnoreList(test_profile): + """Reads the test profile ignore file if it exists. + + Args: + test_profile (str): test profile. + + Returns: + set[str]: ignore list. + """ + ignore_file_path = os.path.join("tests", "input", test_profile, "ignore") + if os.path.isfile(ignore_file_path): + with open(ignore_file_path, "r", encoding="utf-8") as file_object: + return set([line.strip() for line in file_object.readlines()]) + + return set() + + if __name__ == "__main__": print(f"Using Python version {sys.version!s}") @@ -33,30 +40,37 @@ test_scripts = test_loader.discover("tests", pattern="*.py") - test_profile = ".pylnk" - input_glob = "*.lnk" - - ignore_list = set() - - ignore_file_path = f"tests/input/{test_profile}/ignore" - if os.path.isfile(ignore_file_path): - with open(ignore_file_path, "r", encoding="utf-8") as file_object: - ignore_list = set([line.strip() for line in file_object.readlines()]) + ignore_list = ReadIgnoreList(test_profile) + test_set = None source_file = None - for test_set in glob.glob("tests/input/*"): - test_set = test_set.rsplit('/', maxsplit=1)[-1] + for test_set in glob.glob(os.path.join("tests", "input", "*")): + test_set = test_set.rsplit(os.path.sep, maxsplit=1)[-1] if not test_set or test_set[0] == '.' or test_set in ignore_list: continue - source_files = glob.glob(f"tests/input/{test_set:s}/{input_glob:s}") + source_files = glob.glob(os.path.join( + "tests", "input", test_set, input_glob)) if source_files: source_file = source_files[0] break setattr(unittest, "source", source_file) + for option_set in option_sets: + test_options = None + + test_file = os.path.basename(source_file) + test_options_file_path = os.path.join( + "tests", "input", test_profile, test_set, + f"{test_file:s}.{option_set:s}") + if os.path.isfile(test_options_file_path): + with open(test_options_file_path, "r", encoding="utf-8") as file_object: + test_options = file_object.read().strip() + + setattr(unittest, option_set, test_options) + test_results = test_runner.run(test_scripts) if not test_results.wasSuccessful(): sys.exit(1) diff --git a/tests/test_library.sh b/tests/test_library.sh index 8a2a84a..1b809af 100755 --- a/tests/test_library.sh +++ b/tests/test_library.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Tests library functions and types. # -# Version: 20230410 +# Version: 20231001 EXIT_SUCCESS=0; EXIT_FAILURE=1; @@ -9,7 +9,7 @@ EXIT_IGNORE=77; LIBRARY_TESTS="data_block data_string distributed_link_tracker_properties error file_header io_handle known_folder_location link_target_identifier location_information notify special_folder_location"; LIBRARY_TESTS_WITH_INPUT="file support"; -OPTION_SETS=""; +OPTION_SETS=(); INPUT_GLOB="*"; @@ -78,47 +78,52 @@ run_test_with_input() local TEST_SET_DIRECTORY=$(get_test_set_directory "${TEST_PROFILE_DIRECTORY}" "${TEST_SET_INPUT_DIRECTORY}"); - local OLDIFS=${IFS}; - - # IFS="\n" is not supported by all platforms. - IFS=" -"; - if test -f "${TEST_SET_DIRECTORY}/files"; then - for INPUT_FILE in `cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"`; + IFS=$'\n' read -a INPUT_FILES <<< $(cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"); + else + IFS=$'\n' read -a INPUT_FILES <<< $(ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}); + fi + for INPUT_FILE in "${INPUT_FILES[@]}"; + do + if test "${OSTYPE}" = "msys"; + then + # A test executable built with MinGW expects a Windows path. + INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; + fi + local TESTED_WITH_OPTIONS=0; + + for OPTION_SET in ${OPTION_SETS[@]}; do - if test "${OSTYPE}" = "msys"; - then - # A test executable built with MinGW expects a Windows path. - INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; - fi - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; - RESULT=$?; + local TEST_DATA_OPTION_FILE=$(get_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); - if test ${RESULT} -ne ${EXIT_SUCCESS}; + if test -f ${TEST_DATA_OPTION_FILE}; then - break; + TESTED_WITH_OPTIONS=1; + + IFS=" " read -a OPTIONS <<< $(read_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); + + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SET}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" "${OPTIONS[@]}"; + RESULT=$?; + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi fi done - else - for INPUT_FILE in `ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}`; - do - if test "${OSTYPE}" = "msys"; - then - # A test executable built with MinGW expects a Windows path. - INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; - fi - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; + + if test ${TESTED_WITH_OPTIONS} -eq 0; + then + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; RESULT=$?; + fi - if test ${RESULT} -ne ${EXIT_SUCCESS}; - then - break; - fi - done - fi - IFS=${OLDIFS}; + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + done if test ${RESULT} -ne ${EXIT_SUCCESS}; then diff --git a/tests/test_lnkinfo.sh b/tests/test_lnkinfo.sh index e4350b6..82499f5 100755 --- a/tests/test_lnkinfo.sh +++ b/tests/test_lnkinfo.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Info tool testing script # -# Version: 20230410 +# Version: 20231001 EXIT_SUCCESS=0; EXIT_FAILURE=1; @@ -9,7 +9,7 @@ EXIT_IGNORE=77; PROFILES=("lnkinfo"); OPTIONS_PER_PROFILE=(""); -OPTION_SETS=""; +OPTION_SETS=(); INPUT_GLOB="*"; @@ -71,7 +71,7 @@ do IGNORE_LIST=$(read_ignore_list "${TEST_PROFILE_DIRECTORY}"); - IFS=" " read -a OPTIONS <<< ${OPTIONS_PER_PROFILE[${PROFILE_INDEX}]}; + IFS=" " read -a PROFILE_OPTIONS <<< ${OPTIONS_PER_PROFILE[${PROFILE_INDEX}]}; RESULT=${EXIT_SUCCESS}; @@ -89,8 +89,49 @@ do fi TEST_SET_DIRECTORY=$(get_test_set_directory "${TEST_PROFILE_DIRECTORY}" "${TEST_SET_INPUT_DIRECTORY}"); - run_test_on_test_set_with_options "${TEST_SET_DIRECTORY}" "lnkinfo" "with_stdout_reference" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${OPTIONS[@]}"; - RESULT=$?; + RESULT=${EXIT_SUCCESS}; + + if test -f "${TEST_SET_DIRECTORY}/files"; + then + IFS=$'\n' read -a INPUT_FILES <<< $(cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"); + else + IFS=$'\n' read -a INPUT_FILES <<< $(ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}); + fi + for INPUT_FILE in "${INPUT_FILES[@]}"; + do + TESTED_WITH_OPTIONS=0; + + for OPTION_SET in ${OPTION_SETS[@]}; + do + TEST_DATA_OPTION_FILE=$(get_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); + + if test -f ${TEST_DATA_OPTION_FILE}; + then + TESTED_WITH_OPTIONS=1; + + IFS=" " read -a OPTIONS <<< $(read_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); + + run_test_on_input_file "${TEST_SET_DIRECTORY}" "lnkinfo" "with_stdout_reference" "${OPTION_SET}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" "${PROFILE_OPTIONS[@]}" "${OPTIONS[@]}"; + RESULT=$?; + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + fi + done + + if test ${TESTED_WITH_OPTIONS} -eq 0; + then + run_test_on_input_file "${TEST_SET_DIRECTORY}" "lnkinfo" "with_stdout_reference" "" "${TEST_EXECUTABLE}" "${INPUT_FILE}" "${PROFILE_OPTIONS[@]}"; + RESULT=$?; + fi + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + done # Ignore failures due to corrupted data. if test "${TEST_SET}" = "corrupted"; diff --git a/tests/test_python_module.sh b/tests/test_python_module.sh index c455a7a..b9dbe68 100755 --- a/tests/test_python_module.sh +++ b/tests/test_python_module.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Tests Python module functions and types. # -# Version: 20230410 +# Version: 20231001 EXIT_SUCCESS=0; EXIT_FAILURE=1; @@ -9,7 +9,7 @@ EXIT_IGNORE=77; TEST_FUNCTIONS=""; TEST_FUNCTIONS_WITH_INPUT="data_block file support"; -OPTION_SETS=""; +OPTION_SETS=(); TEST_TOOL_DIRECTORY="."; INPUT_GLOB="*"; @@ -68,37 +68,47 @@ test_python_function_with_input() local TEST_SET_DIRECTORY=$(get_test_set_directory "${TEST_PROFILE_DIRECTORY}" "${TEST_SET_INPUT_DIRECTORY}"); - local OLDIFS=${IFS}; - - # IFS="\n"; is not supported by all platforms. - IFS=" -"; - if test -f "${TEST_SET_DIRECTORY}/files"; then - for INPUT_FILE in `cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"`; + IFS=$'\n' read -a INPUT_FILES <<< $(cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"); + else + IFS=$'\n' read -a INPUT_FILES <<< $(ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}); + fi + for INPUT_FILE in "${INPUT_FILES[@]}"; + do + local TESTED_WITH_OPTIONS=0; + + for OPTION_SET in ${OPTION_SETS[@]}; do - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_SCRIPT}" "${INPUT_FILE}"; - RESULT=$?; + local TEST_DATA_OPTION_FILE=$(get_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); - if test ${RESULT} -ne ${EXIT_SUCCESS}; + if test -f ${TEST_DATA_OPTION_FILE}; then - break; + TESTED_WITH_OPTIONS=1; + + IFS=" " read -a OPTIONS <<< $(read_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); + + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SET}" "${TEST_SCRIPT}" "${INPUT_FILE}" "${OPTIONS[@]}"; + RESULT=$?; + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi fi done - else - for INPUT_FILE in `ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}`; - do - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_SCRIPT}" "${INPUT_FILE}"; + + if test ${TESTED_WITH_OPTIONS} -eq 0; + then + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "" "${TEST_SCRIPT}" "${INPUT_FILE}"; RESULT=$?; + fi - if test ${RESULT} -ne ${EXIT_SUCCESS}; - then - break; - fi - done - fi - IFS=${OLDIFS}; + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + done if test ${RESULT} -ne ${EXIT_SUCCESS}; then diff --git a/tests/test_runner.sh b/tests/test_runner.sh index 6313971..9c365b5 100644 --- a/tests/test_runner.sh +++ b/tests/test_runner.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Bash functions to run an executable for testing. # -# Version: 20230410 +# Version: 20231001 # # When CHECK_WITH_ASAN is set to a non-empty value the test executable # is run with asan, otherwise it is run without. @@ -1258,35 +1258,22 @@ run_test_on_test_set_with_options() local RESULT=${EXIT_SUCCESS}; - # IFS="\n"; is not supported by all platforms. - IFS=" -"; - if test -f "${TEST_SET_DIRECTORY}/files"; then - for INPUT_FILE in `cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"`; - do - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "${TEST_MODE}" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" ${ARGUMENTS[@]}; - RESULT=$?; - - if test ${RESULT} -ne ${EXIT_SUCCESS}; - then - break; - fi - done + IFS=$'\n' INPUT_FILES=( $(cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?") ); else - for INPUT_FILE in `ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}`; - do - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "${TEST_MODE}" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" ${ARGUMENTS[@]}; - RESULT=$?; - - if test ${RESULT} -ne ${EXIT_SUCCESS}; - then - break; - fi - done + IFS=$'\n' INPUT_FILES=( $(ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}) ); fi - IFS=${OLDIFS}; + for INPUT_FILE in ${INPUT_FILES[@]}; + do + run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "${TEST_MODE}" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" ${ARGUMENTS[@]}; + RESULT=$?; + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + done return ${RESULT}; } diff --git a/tests/test_tools.sh b/tests/test_tools.sh index f6d7469..e149ec7 100755 --- a/tests/test_tools.sh +++ b/tests/test_tools.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Tests tools functions and types. # -# Version: 20230410 +# Version: 20231001 EXIT_SUCCESS=0; EXIT_FAILURE=1; @@ -9,7 +9,7 @@ EXIT_IGNORE=77; TOOLS_TESTS="info_handle output path_string signal"; TOOLS_TESTS_WITH_INPUT=""; -OPTION_SETS=""; +OPTION_SETS=(); INPUT_GLOB="*"; @@ -78,47 +78,52 @@ run_test_with_input() local TEST_SET_DIRECTORY=$(get_test_set_directory "${TEST_PROFILE_DIRECTORY}" "${TEST_SET_INPUT_DIRECTORY}"); - local OLDIFS=${IFS}; - - # IFS="\n" is not supported by all platforms. - IFS=" -"; - if test -f "${TEST_SET_DIRECTORY}/files"; then - for INPUT_FILE in `cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"`; + IFS=$'\n' read -a INPUT_FILES <<< $(cat ${TEST_SET_DIRECTORY}/files | sed "s?^?${TEST_SET_INPUT_DIRECTORY}/?"); + else + IFS=$'\n' read -a INPUT_FILES <<< $(ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}); + fi + for INPUT_FILE in "${INPUT_FILES[@]}"; + do + if test "${OSTYPE}" = "msys"; + then + # A test executable built with MinGW expects a Windows path. + INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; + fi + local TESTED_WITH_OPTIONS=0; + + for OPTION_SET in ${OPTION_SETS[@]}; do - if test "${OSTYPE}" = "msys"; - then - # A test executable built with MinGW expects a Windows path. - INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; - fi - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; - RESULT=$?; + local TEST_DATA_OPTION_FILE=$(get_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); - if test ${RESULT} -ne ${EXIT_SUCCESS}; + if test -f ${TEST_DATA_OPTION_FILE}; then - break; + TESTED_WITH_OPTIONS=1; + + IFS=" " read -a OPTIONS <<< $(read_test_data_option_file "${TEST_SET_DIRECTORY}" "${INPUT_FILE}" "${OPTION_SET}"); + + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SET}" "${TEST_EXECUTABLE}" "${INPUT_FILE}" "${OPTIONS[@]}"; + RESULT=$?; + + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi fi done - else - for INPUT_FILE in `ls -1d ${TEST_SET_INPUT_DIRECTORY}/${INPUT_GLOB}`; - do - if test "${OSTYPE}" = "msys"; - then - # A test executable built with MinGW expects a Windows path. - INPUT_FILE=`echo ${INPUT_FILE} | sed 's?/?\\\\?g'`; - fi - run_test_on_input_file_with_options "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "${OPTION_SETS}" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; + + if test ${TESTED_WITH_OPTIONS} -eq 0; + then + run_test_on_input_file "${TEST_SET_DIRECTORY}" "${TEST_DESCRIPTION}" "default" "" "${TEST_EXECUTABLE}" "${INPUT_FILE}"; RESULT=$?; + fi - if test ${RESULT} -ne ${EXIT_SUCCESS}; - then - break; - fi - done - fi - IFS=${OLDIFS}; + if test ${RESULT} -ne ${EXIT_SUCCESS}; + then + break; + fi + done if test ${RESULT} -ne ${EXIT_SUCCESS}; then