#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
shopt -s nullglob
shopt -s globstar

if [[ -n "${1:-}" ]]; then
    generator=$1
elif [[ -x /usr/lib/systemd/system-generators/systemd-fstab-generator ]]; then
    generator=/usr/lib/systemd/system-generators/systemd-fstab-generator
elif [[ -x /lib/systemd/system-generators/systemd-fstab-generator ]]; then
    generator=/lib/systemd/system-generators/systemd-fstab-generator
else
    exit 1
fi

src="$(dirname "$0")/testdata/test-fstab-generator"

# fsck(8) is located in /usr/sbin on Debian
PATH=$PATH:/usr/sbin

# systemd-pcrfs@.service could be enabled or not, depending on the host state
# of the host system. Override the measurement to avoid the issue.
export SYSTEMD_FORCE_MEASURE=0

test_one() (
    local initrd input out exp i j k dir fname expf

    input=${1?}
    initrd=${2?}

    : "*** Running $input (initrd=$initrd)"

    out=$(mktemp --tmpdir --directory "test-fstab-generator.XXXXXXXXXX")
    # shellcheck disable=SC2064
    trap "rm -rf '$out'" EXIT INT QUIT PIPE

    exp="${input%.input}.expected"
    if [[ "${input##*/}" =~ swap ]] && systemd-detect-virt --container >/dev/null; then
        exp="${exp}.container"
    fi
    if [[ "$initrd" == no ]]; then
        exp="${exp}.sysroot"
    fi

    if [[ "${input##*/}" =~ \.fstab\.input ]]; then
        SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" $generator "$out" "$out" "$out"
    else
        SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" $generator "$out" "$out" "$out"
    fi

    # The option x-systemd.growfs creates symlink to system's systemd-growfs@.service in .mount.wants directory.
    # Also, when $initrd is no, symlink to systemd-remount-fs.service is created.
    # The system that the test is currently running on may not have or may have outdated unit file.
    # Let's replace the symlink with an empty file.
    for i in "$out"/*/systemd-growfs@*.service "$out"/local-fs.target.wants/systemd-remount-fs.service; do
        [[ -L "$i" ]] || continue
        rm "$i"
        touch "$i"
    done

    if [[ "${input##*/}" =~ \.fstab\.input ]]; then
        for i in "$out"/*.{automount,mount,swap}; do
            sed -i -e 's:SourcePath=.*$:SourcePath=/etc/fstab:' "$i"
        done
    fi

    # .deb packager seems to dislike files named with backslash. So, as a workaround, we store files
    # without backslash in .expected.
    for i in "$out"/**/*\\*.{mount,swap}; do
        k="${i//\\/}"
        if [[ "$i" != "$k" ]]; then
            if [[ -f "$i" ]]; then
                mv "$i" "$k"
            elif [[ -L "$i" ]]; then
                dest=$(readlink "$i")
                rm "$i"
                ln -s "${dest//\\/}" "$k"
            fi
        fi
    done

    # We do not store empty directory.
    if [[ -z "$(ls -A "$out")" && ! -d "$exp" ]]; then
        return 0
    fi

    # We store empty files rather than dead symlinks, so that they don't get pruned when packaged up, so compare
    # the list of filenames rather than their content
    if ! diff -u <(find "$out" -printf '%P\n' | sort) <(find "$exp" -printf '%P\n' | sort); then
        : "**** Unexpected output for $input (initrd=$initrd)"
        return 1
    fi

    # Check the main units.
    if ! diff -u "$out" "$exp"; then
        : "**** Unexpected output for $input (initrd=$initrd)"
        return 1
    fi

    # Also check drop-ins.
    for i in "$out"/*; do
        [[ -d "$i" ]] || continue

        dir="${i##*/}"

        for j in "$i"/*; do
            fname="${j##*/}"
            expf="$exp/$dir/$fname"

            if [[ -L "$j" && ! -e "$j" ]]; then
                # For dead symlink, we store an empty file.
                if [[ ! -e "$expf" || -n "$(cat "$expf")" ]]; then
                    : "**** Unexpected symlink $j created by $input (initrd=$initrd)"
                    return 1
                fi
                continue
            fi

            if ! diff -u "$j" "$expf"; then
                : "**** Unexpected output in $j for $input (initrd=$initrd)"
                return 1
            fi
        done
    done

    return 0
)

for f in "$src"/test-*.input; do
    # If /mnt is a symlink, then the expected output from this
    # test scenario will not match the actual output
    if test "$f" = "$src/test-18-options.fstab.input" -a "$(readlink /mnt)" != "/mnt"
    then
        echo "Skip $f because /mnt is a symlink"
        continue
    fi

    test_one "$f" yes
    test_one "$f" no
done
