#!/bin/bash

set -ux

# Some simple tests of a capser-ized initramfs.

# The basic idea is to make an image that has /sbin/init overwritten
# to just gather some data and shutdown again and boot it in qemu
# system emulation (not KVM, so it can be run in the autopkgtest
# infrastructure without hoping nested kvm works).

# The current qemu command lines only work on intel. That should be
# fixed :)

arch=$(dpkg --print-architecture)
[ $arch = amd64 ] || [ $arch = i386 ] || exit 0

./debian/tests/download-image
ret=$?
if [ $ret -eq 100 ]; then
        # This indicates that a rootfs was not found (maybe very early
        # in development for this cycle?), skip in this case.
        exit 0
elif [ $ret -ne 0 ]; then
        exit $ret
fi

set -e

basecmdline="boot=casper console=ttyS0"

mkinitramfs -o casper-initrd

kernel=$(echo /boot/vmlinu*-"$(uname -r)")

## Helper functions

run () {
    ./debian/tests/run-image kernel=$kernel initrd=casper-initrd \
                             image=$1 cmdline="$basecmdline ${2-}" \
                             output=result
}

partcount () {
    # Count the partitions in a disk image.
    local img=$1
    local linecount=$(sfdisk -ql $1 | wc -l)
    echo $((linecount - 1))
}

expand_image () {
    # Expand image so there is space for persistence partition
    local img=$1
    size=$(stat --format=%s $1)
    truncate -s $((size*2)) $1
}

_loop_devs=()
_mounts=()

setup_image () {
    local dev
    dev="$(losetup -Pf --show $1)"
    _loop_devs=("${dev}" "${_loop_devs[@]}")
    partprobe $dev
    udevadm settle
    echo $dev
}

do_mount () {
    local mountpoint="${!#}"
    mkdir "${mountpoint}"
    mount "$@"
    _mounts=("${mountpoint}" "${_mounts[@]}")

}

cleanup_mounts_and_devices () {
    for mount in "${_mounts[@]}"; do
        umount "$mount" || true
        rm -rf "$mount"
    done
    _mounts=()
    for dev in "${_loop_devs[@]}"; do
        losetup -d "$dev"
    done
    _loop_devs=()
}

check_files_exist () {
    local mountpoint=$1
    shift
    for file in "${@}"; do
        if [ ! -e $mountpoint/$file ]; then
                echo "file $file not found under $mountpoint"
                ls -lR $mountpoint
                exit 1
        fi
    done
}

## Tests

test_basic () {
    echo "### Running basic test"

    ./debian/tests/prep-image image.img "o lsblk.txt lsblk"
    run image.img ""
    grep -q /rofs result/lsblk.txt
}

test_auto_log_persistence () {
    echo "### Testing auto log persistence"

    ./debian/tests/prep-image image.img "touch /var/log/hello /var/crash/report.crash"

    expand_image image.img

    partsbefore=$(partcount image.img)

    run image.img ""

    partsafter=$(partcount image.img)
    if [ $((partsafter - partsbefore)) -ne 1 ]; then
            echo "did not create extra partition"
            exit 1
    fi

    dev="$(setup_image image.img)"
    lsblk "${dev}p${partsafter}"
    label=$(lsblk -no label "${dev}p${partsafter}")
    if [ "$label" != casper-rw ]; then
            echo "created filesystem does not have correct label"
            exit 1
    fi

    do_mount "${dev}p${partsafter}" mnt
    check_files_exist mnt log/hello crash/report.crash
}


test_explicit_persistence () {
    echo "### Testing explicit persistence "

    ./debian/tests/prep-image image.img "if [ -e /hello ]; then o hello.txt cat /hello; else echo content > /hello; fi"

    expand_image image.img

    run image.img "persistent"

    partsafter=$(partcount image.img)

    dev="$(setup_image image.img)"
    do_mount "${dev}p${partsafter}" mnt
    check_files_exist mnt upper/hello
    cleanup_mounts_and_devices

    run image.img "persistent"
    grep -q content result/hello.txt
}


test_implicit_then_explicit_persistence () {
    echo "### Testing implicit then explicit persistence "

    ./debian/tests/prep-image image.img "touch /hello; touch /var/log/hello"

    expand_image image.img

    run image.img ""

    partsafter=$(partcount image.img)
    dev="$(setup_image image.img)"
    do_mount "${dev}p${partsafter}" mnt
    check_files_exist mnt log/hello
    cleanup_mounts_and_devices

    run image.img "persistent"

    partsafter=$(partcount image.img)
    dev="$(setup_image image.img)"
    do_mount "${dev}p${partsafter}" mnt
    check_files_exist mnt log/hello upper/hello upper/var/log/hello
}

test_no_persistence () {
    echo "### Testing disabling of automatic persistence"

    ./debian/tests/prep-image image.img "touch /var/log/hello"

    expand_image image.img

    partsbefore=$(partcount image.img)

    run image.img "nopersistent"

    partsafter=$(partcount image.img)
    if [ $partsafter -ne $partsbefore ]; then
            echo "nopersistent did not suppress creation of partition"
            exit 1
    fi

    dev="$(setup_image image.img)"
    maxend=$(sfdisk $dev -l -q -o end | tail -n +2 | sort -n | tail -n1)
    start=$(((maxend + 1 + 0xfff) & ~0xfff))
    echo "start=$start" | sfdisk $dev -a
    mkfs.ext4 -L "casper-rw" -F "${dev}p$((partsafter+1))"
    cleanup_mounts_and_devices

    run image.img "nopersistent debug"

    partsafter=$(partcount image.img)
    dev="$(setup_image image.img)"
    do_mount "${dev}p${partsafter}" mnt
    if [ -e mnt/log/hello ]; then
        echo "nopersistent did not suppress use of casper-rw partition"
    fi
}

TESTS="test_basic test_auto_log_persistence test_explicit_persistence
       test_implicit_then_explicit_persistence test_no_persistence"

for f in $TESTS; do
    eval $f
    cleanup_mounts_and_devices
done
