#!/bin/bash

#=================================================

break_before_continue () {

    if [ $interactive -eq 1 ]
    then
        echo "To execute one command:"
        echo "     sudo lxc-attach -n $LXC_NAME -- command"
        echo "To establish a ssh connection:"
        echo "     ssh -t $LXC_NAME"

        read -p "Press a key to delete the application and continue...." < /dev/tty
    fi
}

start_test () {

    total_number_of_test=$(grep -c "=1$" $test_serie_dir/tests_to_perform)

    log_title "$1 [Test $current_test_number/$total_number_of_test]"

    # Increment the value of the current test
    current_test_number=$((current_test_number+1))
}

RUN_YUNOHOST_CMD() {

    # --output-as none is to disable the json-like output for some commands like backup create
    LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --output-as none --debug $1" \
        | grep --line-buffered -v --extended-regexp '^[0-9]+\s+.{1,15}DEBUG' \
        | grep -v 'processing action'

    returncode=${PIPESTATUS[0]}
    check_witness_files
    return $returncode
}

SET_RESULT() {
    sed --in-place "s/RESULT_$1=.*$/RESULT_$1=$2/g" $test_serie_dir/results
}

SET_RESULT_IF_NONE_YET() {
    if [ $(GET_RESULT $1) -eq 0 ]
    then
        sed --in-place "s/RESULT_$1=.*$/RESULT_$1=$2/g" $test_serie_dir/results
    fi
}

GET_RESULT() {
    grep "RESULT_$1=" $test_serie_dir/results | awk -F= '{print $2}'
}

#=================================================
# Install and remove an app
#=================================================

INSTALL_APP () {

    local install_args="$(cat "$test_serie_dir/install_args")"
    for arg_override in "$@"
    do
        key="$(echo $arg_override | cut -d '=' -f 1)"
        value="$(echo $arg_override | cut -d '=' -f 2-)"
        install_args=$(echo $install_args | sed "s@$key=[^&]*\&@$key=$value\&@")
    done

    # Uses the default snapshot
    current_snapshot=snap0

    # Exec the pre-install instruction, if there one
    preinstall_script_template="$test_serie_dir/preinstall.sh.template"
    if [ -n "$(cat $preinstall_script_template)" ]
    then
        log_small_title "Pre installation request"
        # Start the lxc container
        LXC_START "true"
        # Copy all the instructions into a script
        preinstall_script="$test_serie_dir/preinstall.sh"
        cp "$preinstall_script_template" "$preinstall_script"
        chmod +x "$preinstall_script"
        # Hydrate the template with variables
        sed -i "s/\$USER/$TEST_USER/" "$preinstall_script"
        sed -i "s/\$DOMAIN/$DOMAIN/" "$preinstall_script"
        sed -i "s/\$SUBDOMAIN/$SUBDOMAIN/" "$preinstall_script"
        sed -i "s/\$PASSWORD/$YUNO_PWD/" "$preinstall_script"
        # Copy the pre-install script into the container.
        scp -rq "$preinstall_script" "$LXC_NAME":
        # Then execute the script to execute the pre-install commands.
        LXC_START "./preinstall.sh >&2"
    fi

    # Install the application in a LXC container
    RUN_YUNOHOST_CMD "app install --force ./app_folder/ -a '$install_args'"

    # yunohost_result gets the return code of the installation
    yunohost_result=$?

    # Print the result of the install command
    if [ $yunohost_result -eq 0 ]; then
        log_debug "Installation successful."
    else
        log_error "Installation failed. ($yunohost_result)"
    fi

    return $yunohost_result
}

LOAD_SNAPSHOT_OR_INSTALL_APP () {
    # Try to find an existing snapshot for this install, or make an install

    # If it's a root install
    if [ "$check_path" = "/" ]
    then
        install_type="root"
        snapshot=$root_snapshot
        snapshot_id=2
    else
        install_type="subpath"
        snapshot=$subpath_snapshot
        snapshot_id=1
    fi

    # Create a snapshot if needed
    if [ -z "$snapshot" ]
    then
        # Create a snapshot for this installation, to be able to reuse it instead of a new installation.
        # But only if this installation has worked fine
        if INSTALL_APP
        then
            log_debug "Creating a snapshot for $install_type installation."
            CREATE_LXC_SNAPSHOT $snapshot_id
            root_snapshot=snap$snapshot_id
        fi
    else
        # Or uses an existing snapshot
        log_debug "Reusing an existing snapshot for $install_type installation."
        LOAD_LXC_SNAPSHOT $snapshot
    fi
}

REMOVE_APP () {
    # Remove an application

    break_before_continue

    log_small_title "Removing the app..."

    # Remove the application from the LXC container
    RUN_YUNOHOST_CMD "app remove $app_id"

    # yunohost_remove gets the return code of the deletion
    local yunohost_remove=$?

    # Print the result of the remove command
    if [ "$yunohost_remove" -eq 0 ]; then
        log_debug "Remove successful."
    else
        log_error "Remove failed. ($yunohost_remove)"
    fi

    return $yunohost_remove
}

#=================================================
# Try to access the app by its url
#=================================================

VALIDATE_THAT_APP_CAN_BE_ACCESSED () {

    local app_id_to_check=${1:-$app_id}

    # Not checking this if this ain't relevant for the current test / app
    if [ $enable_validate_that_app_can_be_accessed == "true" ]
    then
        curl_error=0
        yuno_portal=0
        return
    fi

    log_small_title "Validating that the app can (or cannot) be accessed with its url..."

    # Force a skipped_uris if public mode is not set
    if [ "$install_type" != "private" ] && [ "$install_type" != "public" ]
    then
        log_warning "Forcing public access using a skipped_uris setting"
        # Add a skipped_uris on / for the app
        RUN_YUNOHOST_CMD "app setting $app_id_to_check skipped_uris -v \"/\""
        # Regen the config of sso
        RUN_YUNOHOST_CMD "app ssowatconf"
    fi

    # curl_error indicate the result of curl test
    curl_error=0
    # 503 Service Unavailable can would have some time to work.
    local http503=0
    # yuno_portal equal 1 if the test fall on the portal
    yuno_portal=0

    # Try to access to the url in 2 times, with a final / and without
    i=1;
    while [ $i -ne 3 ]
    do

        # First time, try without final /
        if [ $i -eq 1 ]
        then
            # If the last character is /
            if [ "${check_path: -1}" = "/" ]
            then
                # Remove it
                local curl_check_path="${check_path:0:${#check_path}-1}"
            else
                curl_check_path=$check_path
            fi

            # The next loop will try the second test
            i=2
        elif [ $i -eq 2 ]
        then
            # Second time, try with the final /

            # If the last character isn't /
            if [ "${check_path: -1}" != "/" ]
            then
                # Add it
                curl_check_path="$check_path/"
            else
                curl_check_path=$check_path
            fi

            # The next loop will break the while loop
            i=3
        fi

        # Remove the previous curl output
        rm -f "./url_output"

        # Call curl to try to access to the url of the app
        curl --location --insecure --silent --show-error \
            --header "Host: $check_domain" \
            --resolve $check_domain:443:$LXC_NETWORK.2 \
            --write-out "%{http_code};%{url_effective}\n" \
            --output "./url_output" \
            $check_domain$curl_check_path \
            > "./curl_print"

        # Analyze the result of curl command
        if [ $? -ne 0 ]
        then
            log_error "Connection error..."
            curl_error=1
        fi

        # Print informations about the connection
        local http_code=$(cat "./curl_print" | cut -d ';' -f1)
        test_url_details="
 Test url: $check_domain$curl_check_path
 Real url: $(cat "./curl_print" | cut --delimiter=';' --fields=2)
 HTTP code: $http_code"
        log_debug "$test_url_details"

        # Analyze the http code
        if [ "${http_code:0:1}" = "0" ] || [ "${http_code:0:1}" = "4" ] || [ "${http_code:0:1}" = "5" ] || [ "${http_code:0:1}" = "6" ]
        then
            # If the http code is a 0xx 4xx or 5xx, it's an error code.
            curl_error=1

            # 401 is "Unauthorized", so is a answer of the server. So, it works!
            test "${http_code}" = "401" && curl_error=0

            # 503 is Service Unavailable, it's a temporary error.
            if [ "${http_code}" = "503" ]
            then
                curl_error=0
                log_warning "Service temporarily unavailable"
                # 3 successive error are allowed
                http503=$(( http503 + 1 ))
                if [ $http503 -ge 3 ]; then
                    # Over 3, it's definitively an error
                    curl_error=1
                else
                    # Below 3 times, retry.
                    # Decrease the value of 'i' to retry the same test
                    i=$(( i - 1 ))
                    # Wait 1 second to let's some time to the 503 error
                    sleep 1
                    # And retry immediately
                    continue
                fi
            fi

            if [ $curl_error -eq 1 ]; then
                log_error "The HTTP code shows an error."
            fi
        fi

        # Analyze the output of curl
        if [ -e "./url_output" ]
        then
            # Print the title of the page
            local page_title=$(grep "<title>" "./url_output" | cut --delimiter='>' --fields=2 | cut --delimiter='<' --fields=1)
            log_debug "Title of the page: $page_title"

            # Check if the page title is neither the YunoHost portail or default nginx page
            if [ "$page_title" = "YunoHost Portal" ]
            then
                log_debug "The connection attempt fall on the YunoHost portal."
                yuno_portal=1
            else
                yuno_portal=0
                if [ "$page_title" = "Welcome to nginx on Debian!" ]
                then
                    # Falling on nginx default page is an error.
                    curl_error=1
                    log_error "The connection attempt fall on nginx default page."
                fi

                # Print the first 20 lines of the page
                log_debug "Extract of the page:"
                page_extract=$(lynx -dump -force_html "./url_output" | head --lines 20 | tee -a "$complete_log")

            fi
        fi
    done

    if [[ $curl_error -ne 0 ]]
    then
        log_warning "$test_url_details"
        log_warning "Page title: $page_title"
        log_warning "Page extract: $page_extract"
    fi

    # Detect the issue alias_traversal, https://github.com/yandex/gixy/blob/master/docs/en/plugins/aliastraversal.md

    # Create a file to get for alias_traversal
    echo "<!DOCTYPE html><html><head>
    <title>alias_traversal test</title>
    </head><body><h1>alias_traversal test</h1>
    If you see this page, you have failed the test for alias_traversal issue.</body></html>" \
        | sudo tee $LXC_ROOTFS/var/www/html/alias_traversal.html > /dev/null

    curl --location --insecure --silent $check_domain$check_path../html/alias_traversal.html \
        | grep "title" | grep --quiet "alias_traversal test" \
        && log_error "Issue alias_traversal detected ! Please see here https://github.com/YunoHost/example_ynh/pull/45 to fix that." \
        && SET_RESULT alias_traversal 1
}

#=================================================
# Generic functions for unit tests
#=================================================

check_test_result () {

    # Check the result and print SUCCESS or FAIL

    if [ $yunohost_result -eq 0 ] && [ $curl_error -eq 0 ] && [ $yuno_portal -eq 0 ]
    then
        log_report_test_success
        return 0
    else
        log_report_test_failed
        return 1
    fi
}

validate_that_at_least_one_install_succeeded () {
    # Check if an install have previously work

    # If the test for install in sub dir isn't desactivated
    sub_dir_install=0
    if [ $setup_sub_dir -ne 0 ]
    then
        # If a test succeed or if force_install_ok is set
        # Or if $setup_sub_dir isn't set in the check_process
        if [ $(GET_RESULT check_sub_dir) -eq 1 ] || [ $force_install_ok -eq 1 ] || [ $setup_sub_dir -eq -1 ]
        then
            # Validate installation in sub dir.
            sub_dir_install=1
        fi
    else
        sub_dir_install=0
    fi

    # If the test for install on root isn't desactivated

    root_install=0
    if [ $setup_root -ne 0 ] || [ $setup_nourl -eq 1 ]
    then
        # If a test succeed or if force_install_ok is set
        # Or if $setup_root isn't set in the check_process
        if [ $(GET_RESULT check_root) -eq 1 ] || [ $force_install_ok -eq 1 ] || [ $setup_root -eq -1 ]
        then
            # Validate installation on root.
            root_install=1
        fi
    else
        root_install=0
    fi

    if [ $sub_dir_install -eq 0 ] && [ $root_install -eq 0 ]
    then
        log_error "All installs failed, therefore this test cannot be performed..."
        return 1
    fi
}

#=================================================
# Unit tests
#=================================================

TEST_INSTALL () {
    # Try to install in a sub path, on root or without url access
    # $1 = install type

    local install_type=$1
    if [ "$install_type" = "subdir" ]; then
        start_test "Installation in a sub path"
    elif [ "$install_type" = "root" ]; then
        start_test "Installation on the root"
    else
        start_test "Installation without url access"
    fi

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN
    if [ "$install_type" = "subdir" ]; then
        local check_path=$test_path
    elif [ "$install_type" = "root" ]; then
        local check_path=/
    fi

    # Install the application in a LXC container
    INSTALL_APP "domain=$check_domain" "path=$check_path" "user=$TEST_USER" "is_public=1"

    # Try to access the app by its url
    VALIDATE_THAT_APP_CAN_BE_ACCESSED

    # Check the result and print SUCCESS or FAIL
    if check_test_result
    then    # Success
        SET_RESULT global_setup 1    # Installation succeed
        local check_result_setup=1    # Installation succeed
    else    # Fail
        # The global success for a installation can't be failed if another installation succeed
        SET_RESULT_IF_NONE_YET global_setup -1
        local check_result_setup=-1    # Installation failed
    fi

    # Create a snapshot for this installation, to be able to reuse it instead of a new installation.
    # But only if this installation has worked fine
    if [ $check_result_setup -eq 1 ]; then
        if [ "$check_path" = "/" ]
        then
            # Check if a snapshot already exist for a root install
            if [ -z "$root_snapshot" ]
            then
                log_debug "Create a snapshot for root installation."
                CREATE_LXC_SNAPSHOT 2
                root_snapshot=snap2
            fi
        else
            # Check if a snapshot already exist for a subpath (or no_url) install
            if [ -z "$subpath_snapshot" ]
            then
                # Then create a snapshot
                log_debug "Create a snapshot for sub path installation."
                CREATE_LXC_SNAPSHOT 1
                subpath_snapshot=snap1
            fi
        fi
    fi

    # Remove the application
    if REMOVE_APP
    then
        log_report_test_success
        local check_result_remove=1
        SET_RESULT global_remove 1
    else
        log_report_test_failed
        # The global success for a deletion can't be failed if another remove succeed
        SET_RESULT_IF_NONE_YET global_remove -1
        local check_result_remove=-1
    fi

    # Reinstall the application after the removing
    # Try to resintall only if the first install is a success.
    if [ $check_result_setup -eq 1 ]
    then
        log_small_title "Reinstall the application after a removing."

        INSTALL_APP "domain=$check_domain" "path=$check_path" "user=$TEST_USER" "is_public=1"

        # Try to access the app by its url
        VALIDATE_THAT_APP_CAN_BE_ACCESSED

        # Check the result and print SUCCESS or FAIL
        if check_test_result
        then    # Success
            local check_result_setup=1    # Installation succeed
        else    # Fail
            local check_result_setup=-1    # Installation failed
        fi
    fi

    # Fill the correct variable depend on the type of test
    if [ "$install_type" = "subdir" ]
    then
        SET_RESULT check_sub_dir $check_result_setup
        SET_RESULT check_remove_sub_dir $check_result_remove
    else    # root and no_url
        SET_RESULT check_root $check_result_setup
        SET_RESULT check_remove_root $check_result_remove
    fi

    break_before_continue
}

TEST_UPGRADE () {
    # Try the upgrade script

    commits=""
    for LINE in $(grep "^upgrade=1" "$test_serie_dir/check_process.tests_infos")
    do
        commit=$(echo $LINE | grep -o "from_commit=.*" | awk -F= '{print $2}')
        [ -n "$commit"] || commits+="current " || commits+="$commit "
    done

    # Do an upgrade test for each commit in the upgrade list
    for commit in $commits
    do
        if [ "$commit" == "current" ]
        then
            start_test "Upgrade from the same version"
        else
            # Get the specific section for this upgrade from the check_process
            extract_section "^; commit=$commit" "^;" "$check_process"
            # Get the name for this upgrade.
            upgrade_name=$(grep "^name=" "$check_process_section" | cut -d'=' -f2)
            # Or use the commit if there's no name.
            if [ -z "$upgrade_name" ]; then
                start_test "Upgrade from the commit $commit"
            else
                start_test "Upgrade from $upgrade_name"
            fi
        fi

        # Check if an install have previously work
        # Abort if none install worked
        validate_that_at_least_one_install_succeeded || return

        # Replace manifest key for the test
        check_domain=$SUBDOMAIN
        # Use a path according to previous succeeded installs
        if [ $sub_dir_install -eq 1 ]; then
            local check_path=$test_path
        else
            local check_path=/
        fi

        # Install the application in a LXC container
        log_small_title "Preliminary install..."
        if [ "$commit" == "current" ]
        then
            # If no commit is specified, use the current version.
            LOAD_SNAPSHOT_OR_INSTALL_APP "domain=$check_domain" "path=$check_path" "user=$TEST_USER" "is_public=1"
        else
            # Get the arguments of the manifest for this upgrade.
            specific_upgrade_args="$(grep "^manifest_arg=" "$check_process_section" | cut -d'=' -f2-)"
            if [ -n "$specific_upgrade_args" ]; then
                cp "$test_serie_dir/install_args" "$test_serie_dir/install_args.bkp"
                echo $specific_upgrade_args > "$test_serie_dir/install_args"
            fi

            # Make a backup of the directory
            # and Change to the specified commit
            sudo cp -a "$package_path" "${package_path}_back"
            (cd "$package_path"; git checkout --force --quiet "$commit")
            
            # Install the application
            INSTALL_APP "domain=$check_domain" "path=$check_path" "user=$TEST_USER"

            if [ -n "$specific_upgrade_args" ]; then
                mv "$test_serie_dir/install_args.bkp" "$test_serie_dir/install_args"
            fi

            # Then replace the backup
            sudo rm -r "$package_path"
            sudo mv "${package_path}_back" "$package_path"
        fi

        # Check if the install had work
        if [ $yunohost_result -ne 0 ]
        then
            log_error "Installation failed..."
            log_error "Upgrade test ignored..."
        else
            log_small_title "Upgrade..."

            # Upgrade the application in a LXC container
            RUN_YUNOHOST_CMD "app upgrade $app_id -f '$package_path'"

            # yunohost_result gets the return code of the upgrade
            yunohost_result=$?

            # Print the result of the upgrade command
            if [ $yunohost_result -eq 0 ]; then
                log_debug "Upgrade successful."
            else
                log_error "Upgrade failed. ($yunohost_result)"
            fi

            # Try to access the app by its url
            VALIDATE_THAT_APP_CAN_BE_ACCESSED

            # Check the result and print SUCCESS or FAIL
            if check_test_result
            then    # Success
                # The global success for an upgrade can't be a success if another upgrade failed
                SET_RESULT_IF_NONE_YET check_upgrade 1
            else    # Fail
                SET_RESULT check_upgrade -1
            fi

            # Remove the application
            REMOVE_APP
        fi

        # Uses the default snapshot
        current_snapshot=snap0
        # Stop and restore the LXC container
        LXC_STOP >> $complete_log
    done
}

TEST_PUBLIC_PRIVATE () {
    # Try to install in public or private mode
    # $1 = install type

    local install_type=$1
    if [ "$install_type" = "private" ]; then
        start_test "Installation in private mode"
    else [ "$install_type" = "public" ]
        start_test "Installation in public mode"
    fi


    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN
    # Set public or private according to type of test requested
    if [ "$install_type" = "private" ]; then
        local is_public="0"
    elif [ "$install_type" = "public" ]; then
        local is_public="1"
    fi

    # Initialize the value
    local check_result_public_private=0

    # Try in 2 times, first in root and second in sub path.
    local i=0
    for i in 0 1
    do
        # First, try with a root install
        if [ $i -eq 0 ]
        then
            # Check if root installation worked
            if [ $root_install -eq 1 ]
            then
                # Replace manifest key for path
                local check_path=/
            else
                # Jump to the second path if this check cannot be do
                log_warning "Root install failed, therefore this test cannot be performed..."
                continue
            fi

            # Second, try with a sub path install
        elif [ $i -eq 1 ]
        then
            # Check if sub path installation worked, or if force_install_ok is setted.
            if [ $sub_dir_install -eq 1 ]
            then
                # Replace manifest key for path
                local check_path=$test_path
            else
                # Jump to the second path if this check cannot be do
                log_warning "Sub path install failed, therefore this test cannot be performed..."
                return
            fi
        fi

        # Install the application in a LXC container
        INSTALL_APP "domain=$check_domain" "user=$TEST_USER" "is_public=$is_public" "path=$check_path"

        # Try to access the app by its url
        VALIDATE_THAT_APP_CAN_BE_ACCESSED

        # Change the result according to the results of the curl test
        if [ "$install_type" = "private" ]
        then
            # In private mode, if curl doesn't fell on the ynh portal, it's a fail.
            if [ $yuno_portal -eq 0 ]; then
                log_error "App is not private: it should redirect to the Yunohost portal, but is publicly accessible instead"
                yunohost_result=1
            fi
        elif [ "$install_type" = "public" ]
        then
            # In public mode, if curl fell on the ynh portal, it's a fail.
            if [ $yuno_portal -eq 1 ]; then
                log_error "App page is not public: it should be publicly accessible, but redirects to the Yunohost portal instead"
                yunohost_result=1
            fi
        fi

        # Check the result and print SUCCESS or FAIL
        if [ $yunohost_result -eq 0 ] && [ $curl_error -eq 0 ]
        then
            log_report_test_success
            # The global success for public/private mode can't be a success if another installation failed
            if [ $check_result_public_private -ne -1 ]; then
                check_result_public_private=1    # Installation succeed
            fi
        else
            log_report_test_failed
            check_result_public_private=-1    # Installation failed
        fi

        # Fill the correct variable depend on the type of test
        if [ "$install_type" = "private" ]
        then
            SET_RESULT check_private $check_result_public_private
        else    # public
            SET_RESULT check_public $check_result_public_private
        fi

        break_before_continue

        # Stop and restore the LXC container
        LXC_STOP >> $complete_log
    done
}

TEST_MULTI_INSTANCE () {
    # Try multi-instance installations

    start_test "Multi-instance installations"

    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    if [ $sub_dir_install -eq 1 ]; then
        local check_path=$test_path
    else
        local check_path=/
    fi

    # Install 2 times the same app
    local i=0
    for i in 1 2
    do
        if [ $i -eq 1 ]
        then
            check_domain=$DOMAIN
            log_small_title "First installation: path=$check_domain$check_path"
            # Second installation
        elif [ $i -eq 2 ]
        then
            check_domain=$SUBDOMAIN
            log_small_title "Second installation: path=$check_domain$check_path"
        fi

        # Install the application in a LXC container
        INSTALL_APP "domain=$check_domain" "path=$check_path" "user=$TEST_USER" "is_public=1"

        # Store the result in the correct variable
        # First installation
        if [ $i -eq 1 ]
        then
            local multi_yunohost_result_1=$yunohost_result
            # Second installation
        elif [ $i -eq 2 ]
        then
            local multi_yunohost_result_2=$yunohost_result
        fi
    done

    # Try to access to the 2 apps by theirs url
    for i in 1 2
    do
        # First app
        if [ $i -eq 1 ]
        then
            check_domain=$DOMAIN
            # Second app
        elif [ $i -eq 2 ]
        then
            check_domain=$SUBDOMAIN
            VALIDATE_THAT_APP_CAN_BE_ACCESSED ${app_id}__2
        fi

        # Check the result of curl test
        if [ $curl_error -ne 0 ] || [ $yuno_portal -ne 0 ]
        then
            # The test failed if curl fell on ynh portal or had an error.
            # First app
            if [ $i -eq 1 ]
            then
                multi_yunohost_result_1=1
                # Second app
            elif [ $i -eq 2 ]
            then
                multi_yunohost_result_2=1
            fi
        fi
    done

    # Check the result and print SUCCESS or FAIL
    # Succeed if the 2 installations work;
    if [ $multi_yunohost_result_1 -eq 0 ] && [ $multi_yunohost_result_2 -eq 0 ]
    then    # Success
        log_report_test_success
        SET_RESULT check_multi_instance 1
    else    # Fail
        log_report_test_failed
        SET_RESULT check_multi_instance -1
    fi

    break_before_continue
}

TEST_PORT_ALREADY_USED () {
    # Try to install with specific complications
    # $1 = install type

    local install_type=$1
    start_test "Port already used"

    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN
    # Replace path manifest key for the test
    # Use a path according to previous succeeded installs
    if [ $sub_dir_install -eq 1 ]; then
        local check_path=$test_path
    else
        local check_path=/
    fi

    if grep -q -m1 "port_already_use=1" "$test_serie_dir/check_process.tests_infos"
    then
        local check_port=$(grep -m1 "port_already_use=1" "$test_serie_dir/check_process.tests_infos" | grep -o -E "\([0-9]+\)" | tr -d '()')
    else
        local check_port=6660
    fi

    # Build a service with netcat for use this port before the app.
    echo -e "[Service]\nExecStart=/bin/netcat -l -k -p $check_port\n
    [Install]\nWantedBy=multi-user.target" | \
        sudo tee "$LXC_ROOTFS/etc/systemd/system/netcat.service" \
        > /dev/null

    # Then start this service to block this port.
    LXC_START "sudo systemctl enable netcat & sudo systemctl start netcat"

    # Install the application in a LXC container
    INSTALL_APP "domain=$check_domain" "user=$TEST_USER" "is_public=1" "path=$check_path" "port=$check_port"

    # Try to access the app by its url
    VALIDATE_THAT_APP_CAN_BE_ACCESSED

    # Check the result and print SUCCESS or FAIL
    if check_test_result
    then    # Success
        local check_result_setup=1
    else    # Fail
        local check_result_setup=-1
    fi

    # Fill the correct variable depend on the type of test
    SET_RESULT check_port $check_result_setup

    break_before_continue
}

TEST_BACKUP_RESTORE () {
    # Try to backup then restore the app

    start_test "Backup/Restore"

    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN

    # Try in 2 times, first in root and second in sub path.
    local i=0
    for i in 0 1
    do
        # First, try with a root install
        if [ $i -eq 0 ]
        then
            # Check if root installation worked, or if force_install_ok is setted.
            if [ $root_install -eq 1 ]
            then
                # Replace manifest key for path
                local check_path=/
                log_small_title "Preliminary installation on the root..."
            else
                # Jump to the second path if this check cannot be do
                log_warning "Root install failed, therefore this test cannot be performed..."
                continue
            fi

            # Second, try with a sub path install
        elif [ $i -eq 1 ]
        then
            # Check if sub path installation worked, or if force_install_ok is setted.
            if [ $sub_dir_install -eq 1 ]
            then
                # Replace manifest key for path
                local check_path=$test_path
                log_small_title "Preliminary installation in a sub path..." "white" "bold" clog
            else
                # Jump to the second path if this check cannot be do
                log_warning "Sub path install failed, therefore this test cannot be performed..."
                return
            fi
        fi

        # Install the application in a LXC container
        LOAD_SNAPSHOT_OR_INSTALL_APP "domain=$check_domain" "user=$TEST_USER" "is_public=1" "path=$check_path"

        # Remove the previous residual backups
        sudo rm -rf $LXC_ROOTFS/home/yunohost.backup/archives
        sudo rm -rf $LXC_SNAPSHOTS/$current_snapshot/rootfs/home/yunohost.backup/archives

        # BACKUP
        # Made a backup if the installation succeed
        if [ $yunohost_result -ne 0 ]
        then
            log_error "Installation failed..."
        else
            log_small_title "Backup of the application..."

            # A complete list of backup hooks is available at /usr/share/yunohost/hooks/backup/
            backup_hooks="conf_ssowat data_home conf_ynh_firewall conf_cron"

            # Made a backup of the application
            RUN_YUNOHOST_CMD "backup create -n Backup_test --apps $app_id --system $backup_hooks"

            # yunohost_result gets the return code of the backup
            yunohost_result=$?

            # Print the result of the backup command
            if [ $yunohost_result -eq 0 ]; then
                log_debug "Backup successful"
            else
                log_error "Backup failed. ($yunohost_result)"
            fi
        fi

        # Check the result and print SUCCESS or FAIL
        if [ $yunohost_result -eq 0 ]
        then    # Success
            log_report_test_success
            # The global success for a backup can't be a success if another backup failed
            SET_RESULT_IF_NONE_YET check_backup 1
        else
            log_report_test_failed
            SET_RESULT check_backup -1
        fi

        # Grab the backup archive into the LXC container, and keep a copy
        sudo cp -a $LXC_ROOTFS/home/yunohost.backup/archives ./

        # RESTORE
        # Try the restore process in 2 times, first after removing the app, second after a restore of the container.
        local j=0
        for j in 0 1
        do
            # First, simply remove the application
            if [ $j -eq 0 ]
            then
                # Remove the application
                REMOVE_APP

                log_small_title "Restore after removing the application..."

                # Second, restore the whole container to remove completely the application
            elif [ $j -eq 1 ]
            then
                # Uses the default snapshot
                current_snapshot=snap0

                # Remove the previous residual backups
                sudo rm -rf $LXC_SNAPSHOTS/$current_snapshot/rootfs/home/yunohost.backup/archives

                # Place the copy of the backup archive in the container.
                sudo mv -f ./archives $LXC_SNAPSHOTS/$current_snapshot/rootfs/home/yunohost.backup/

                # Stop and restore the LXC container
                LXC_STOP >> $complete_log

                log_small_title "Restore on a clean YunoHost system..."
            fi

            # Restore the application from the previous backup
            RUN_YUNOHOST_CMD "backup restore Backup_test --force --apps $app_id"

            # yunohost_result gets the return code of the restore
            yunohost_result=$?

            # Print the result of the backup command
            if [ $yunohost_result -eq 0 ]; then
                log_debug "Restore successful."
            else
                log_error "Restore failed. ($yunohost_result)"
            fi

            # Try to access the app by its url
            VALIDATE_THAT_APP_CAN_BE_ACCESSED

            # Check the result and print SUCCESS or FAIL
            if check_test_result
            then
                # The global success for a restore can't be a success if another restore failed
                SET_RESULT_IF_NONE_YET check_restore 1
            else
                SET_RESULT check_restore -1
            fi

            break_before_continue

            # Stop and restore the LXC container
            LXC_STOP >> $complete_log
        done
    done
}

TEST_CHANGE_URL () {
    # Try the change_url script

    start_test "Change URL"

    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN
    # Try in 6 times !
    # Without modify the domain, root to path, path to path and path to root.
    # And then, same with a domain change
    local i=0
    for i in `seq 1 7`
    do
        if [ $i -eq 1 ]; then
            # Same domain, root to path
            check_path=/
            local new_path=$test_path
            local new_domain=$SUBDOMAIN
        elif [ $i -eq 2 ]; then
            # Same domain, path to path
            check_path=$test_path
            local new_path=${test_path}_2
            local new_domain=$SUBDOMAIN
        elif [ $i -eq 3 ]; then
            # Same domain, path to root
            check_path=$test_path
            local new_path=/
            local new_domain=$SUBDOMAIN

        elif [ $i -eq 4 ]; then
            # Other domain, root to path
            check_path=/
            local new_path=$test_path
            local new_domain=$DOMAIN
        elif [ $i -eq 5 ]; then
            # Other domain, path to path
            check_path=$test_path
            local new_path=${test_path}_2
            local new_domain=$DOMAIN
        elif [ $i -eq 6 ]; then
            # Other domain, path to root
            check_path=$test_path
            local new_path=/
            local new_domain=$DOMAIN
        elif [ $i -eq 7 ]; then
            # Other domain, root to root
            check_path=/
            local new_path=/
            local new_domain=$DOMAIN
        fi

        # Ignore the test if it tries to move to the same address
        if [ "$check_path" == "$new_path" ] && [ "$new_domain" == "$SUBDOMAIN" ]; then
            continue
        fi

        # Check if root or subpath installation worked, or if force_install_ok is setted.
        # Try with a sub path install
        if [ "$check_path" = "/" ]
        then
            if [ $root_install -eq 0 ]
            then
                # Skip this test
                log_warning "Root install failed, therefore this test cannot be performed..."
                continue
            elif [ "$new_path" != "/" ] && [ $sub_dir_install -eq 0 ]
            then
                # Skip this test
                log_warning "Sub path install failed, therefore this test cannot be performed..."
                continue
            fi
            # And with a sub path install
        else
            if [ $sub_dir_install -eq 0 ]
            then
                # Skip this test
                log_warning "Sub path install failed, therefore this test cannot be performed..."
                continue
            elif [ "$new_path" = "/" ] && [ $root_install -eq 0 ]
            then
                # Skip this test
                log_warning "Root install failed, therefore this test cannot be performed..."
                continue
            fi
        fi

        # Install the application in a LXC container
        log_small_title "Preliminary install..."
        LOAD_SNAPSHOT_OR_INSTALL_APP "domain=$check_domain" "user=$TEST_USER" "is_public=1" "path=$check_path"

        # Check if the install had work
        if [ $yunohost_result -ne 0 ]
        then
            log_error "Installation failed..."
        else
            log_small_title "Change the url from $SUBDOMAIN$check_path to $new_domain$new_path..."

            # Change the url
            RUN_YUNOHOST_CMD "app change-url $app_id -d '$new_domain' -p '$new_path'"

            # yunohost_result gets the return code of the change-url script
            yunohost_result=$?

            # Print the result of the change_url command
            if [ $yunohost_result -eq 0 ]; then
                log_debug "Change_url script successful"
            else
                log_error "Change_url script failed. ($yunohost_result)"
            fi

            # Try to access the app by its url
            check_path=$new_path
            check_domain=$new_domain
            VALIDATE_THAT_APP_CAN_BE_ACCESSED
        fi

        # Check the result and print SUCCESS or FAIL
        if check_test_result
        then    # Success
            # The global success for a change_url can't be a success if another change_url failed
            SET_RESULT_IF_NONE_YET change_url 1
        else    # Fail
            SET_RESULT change_url -1    # Change_url failed
        fi

        break_before_continue

        # Uses the default snapshot
        current_snapshot=snap0
        # Stop and restore the LXC container
        LXC_STOP >> $complete_log
    done
}

# Define a function to split a file in multiple parts. Used for actions and config-panel toml
splitterAA()
{
    local bound="$1"
    local file="$2"

    # If $2 is a real file
    if [ -e "$file" ]
    then
        # Replace name of the file by its content
        file="$(cat "$file")"
    fi

    local file_lenght=$(echo "$file" | wc --lines | awk '{print $1}')

    bounds=($(echo "$file" | grep --line-number --extended-regexp "$bound" | cut -d':' -f1))

    # Go for each line number (boundary) into the array
    for line_number in $(seq 0 $(( ${#bounds[@]} -1 )))
    do
        # The first bound is the next line number in the array
        # That the low bound on which we cut
        first_bound=$(( ${bounds[$line_number+1]} - 1 ))
        # If there's no next cell in the array, we got -1, in such case, use the lenght of the file.
        # We cut at the end of the file
        test $first_bound -lt 0 && first_bound=$file_lenght
        # The second bound is the current line number in the array minus the next one.
        # The the upper bound in the file.
        second_bound=$(( ${bounds[$line_number]} - $first_bound - 1 ))
        # Cut the file a first time from the beginning to the first bound
        # And a second time from the end, back to the second bound.
        parts[line_number]="$(echo "$file" | head --lines=$first_bound \
            | tail --lines=$second_bound)"
    done
}

ACTIONS_CONFIG_PANEL () {
    # Try the actions and config-panel features

    test_type=$1
    if [ "$test_type" == "actions" ]
    then
        start_test "Actions"

        toml_file="$package_path/actions.toml"
        if [ ! -e "$toml_file" ]
        then
            log_error "No actions.toml found !"
            return 1
        fi

    elif [ "$test_type" == "config_panel" ]
    then
        start_test "Config-panel"

        toml_file="$package_path/config_panel.toml"
        if [ ! -e "$toml_file" ]
        then
            log_error "No config_panel.toml found !"
            return 1
        fi
    fi

    # Check if an install have previously work
    validate_that_at_least_one_install_succeeded || return

    # Replace manifest key for the test
    check_domain=$SUBDOMAIN
    # Use a path according to previous succeeded installs
    if [ $sub_dir_install -eq 1 ]; then
        local check_path=$test_path
    else
        local check_path=/
    fi

    # Install the application in a LXC container
    log_small_title "Preliminary install..."
    LOAD_SNAPSHOT_OR_INSTALL_APP "domain=$check_domain" "user=$TEST_USER" "is_public=1" "path=$check_path"

    validate_action_config_panel()
    {
        # yunohost_result gets the return code of the command
        yunohost_result=$?

        local message="$1"

        # Print the result of the command
        if [ $yunohost_result -eq 0 ]; then
            log_debug "$message succeed."
        else
            log_error "$message failed. ($yunohost_result)"
        fi

        # Check the result and print SUCCESS or FAIL
        if check_test_result
        then    # Success
            # The global success for a actions can't be a success if another iteration failed
            SET_RESULT_IF_NONE_YET action_config_panel 1    # Actions succeed
        else    # Fail
            SET_RESULT action_config_panel -1    # Actions failed
        fi

        break_before_continue
    }

    # List first, then execute
    local i=0
    for i in `seq 1 2`
    do
        # Do a test if the installation succeed
        if [ $yunohost_result -ne 0 ]
        then
            log_error "The previous test has failed..."
            continue
        fi

        if [ $i -eq 1 ]
        then
            if [ "$test_type" == "actions" ]
            then
                log_info "> List the available actions..."

                # List the actions
                RUN_YUNOHOST_CMD "app action list $app_id"

                validate_action_config_panel "yunohost app action list"
            elif [ "$test_type" == "config_panel" ]
            then
                log_info "> Show the config panel..."

                # Show the config-panel
                RUN_YUNOHOST_CMD "app config show-panel $app_id"
                validate_action_config_panel "yunohost app config show-panel"
            fi
        elif [ $i -eq 2 ]
        then
            local parts
            if [ "$test_type" == "actions" ]
            then
                log_info "> Execute the actions..."

                # Split the actions.toml file to separate each actions
                splitterAA "^[[:blank:]]*\[[^.]*\]" "$toml_file"
            elif [ "$test_type" == "config_panel" ]
            then
                log_info "> Apply configurations..."

                # Split the config_panel.toml file to separate each configurations
                splitterAA "^[[:blank:]]*\[.*\]" "$toml_file"
            fi

            # Read each part, each action, one by one
            for part in $(seq 0 $(( ${#parts[@]} -1 )))
            do
                local action_config_argument_name=""
                local action_config_argument_type=""
                local action_config_argument_default=""
                local actions_config_arguments_specifics=""
                local nb_actions_config_arguments_specifics=1

                # Ignore part of the config_panel which are only titles
                if [ "$test_type" == "config_panel" ]
                then
                    # A real config_panel part should have a `ask = ` line. Ignore the part if not.
                    if ! echo "${parts[$part]}" | grep --quiet --extended-regexp "^[[:blank:]]*ask ="
                    then
                        continue
                    fi
                    # Get the name of the config. ask = "Config ?"
                    local action_config_name="$(echo "${parts[$part]}" | grep "ask *= *" | sed 's/^.* = \"\(.*\)\"/\1/')"

                    # Get the config argument name "YNH_CONFIG_part1_part2.part3.partx"
                    local action_config_argument_name="$(echo "${parts[$part]}" | grep "^[[:blank:]]*\[.*\]$")"
                    # Remove []
                    action_config_argument_name="${action_config_argument_name//[\[\]]/}"
                    # And remove spaces
                    action_config_argument_name="${action_config_argument_name// /}"

                elif [ "$test_type" == "actions" ]
                then
                    # Get the name of the action. name = "Name of the action"
                    local action_config_name="$(echo "${parts[$part]}" | grep "name" | sed 's/^.* = \"\(.*\)\"/\1/')"

                    # Get the action. [action]
                    local action_config_action="$(echo "${parts[$part]}" | grep "^\[.*\]$" | sed 's/\[\(.*\)\]/\1/')"
                fi

                # Check if there's any [action.arguments]
                # config_panel always have arguments.
                if echo "${parts[$part]}" | grep --quiet "$action_config_action\.arguments" || [ "$test_type" == "config_panel" ]
                then local action_config_has_arguments=1
                else local action_config_has_arguments=0
                fi

                # If there's arguments for this action.
                if [ $action_config_has_arguments -eq 1 ]
                then
                    if [ "$test_type" == "actions" ]
                    then
                        # Get the argument [action.arguments.name_of_the_argument]
                        action_config_argument_name="$(echo "${parts[$part]}" | grep "$action_config_action\.arguments\." | sed 's/.*\.\(.*\)]/\1/')"
                    fi

                    # Get the type of the argument. type = "type"
                    action_config_argument_type="$(echo "${parts[$part]}" | grep "type" | sed 's/^.* = \"\(.*\)\"/\1/')"
                    # Get the default value of this argument. default = true
                    action_config_argument_default="$(echo "${parts[$part]}" | grep "default" | sed 's/^.* = \(.*\)/\1/')"
                    # Do not use true or false, use 1/0 instead
                    if [ "$action_config_argument_default" == "true" ] && [ "$action_config_argument_type" == "boolean" ]; then
                        action_config_argument_default=1
                    elif [ "$action_config_argument_default" == "false" ] && [ "$action_config_argument_type" == "boolean" ]; then
                        action_config_argument_default=0
                    fi

                    if [ "$test_type" == "config_panel" ]
                    then
                        check_process_arguments=""
                        while read line
                        do
                            # Remove all double quotes
                            add_arg="${line//\"/}"
                            # Then add this argument and follow it by :
                            check_process_arguments="${check_process_arguments}${add_arg}:"
                        done < $test_serie_dir/check_process.configpanel_infos
                    elif [ "$test_type" == "actions" ]
                    then
                        local check_process_arguments=""
                        while read line
                        do
                            # Remove all double quotes
                            add_arg="${line//\"/}"
                            # Then add this argument and follow it by :
                            check_process_arguments="${check_process_arguments}${add_arg}:"
                        done < $test_serie_dir/check_process.actions_infos
                    fi
                    # Look for arguments into the check_process
                    if echo "$check_process_arguments" | grep --quiet "$action_config_argument_name"
                    then
                        # If there's arguments for this actions into the check_process
                        # Isolate the values
                        actions_config_arguments_specifics="$(echo "$check_process_arguments" | sed "s/.*$action_config_argument_name=\(.*\)/\1/")"
                        # And remove values of the following action
                        actions_config_arguments_specifics="${actions_config_arguments_specifics%%\:*}"
                        nb_actions_config_arguments_specifics=$(( $(echo "$actions_config_arguments_specifics" | tr --complement --delete "|" | wc --chars) + 1 ))
                    fi

                    if [ "$test_type" == "config_panel" ]
                    then
                        # Finish to format the name
                        # Remove . by _
                        action_config_argument_name="${action_config_argument_name//./_}"
                        # Move all characters to uppercase
                        action_config_argument_name="${action_config_argument_name^^}"
                        # Add YNH_CONFIG_
                        action_config_argument_name="YNH_CONFIG_$action_config_argument_name"
                    fi
                fi

                # Loop on the number of values into the check_process.
                # Or loop once for the default value
                for j in `seq 1 $nb_actions_config_arguments_specifics`
                do
                    local action_config_argument_built=""
                    if [ $action_config_has_arguments -eq 1 ]
                    then
                        # If there's values into the check_process
                        if [ -n "$actions_config_arguments_specifics" ]
                        then
                            # Build the argument from a value from the check_process
                            local action_config_actual_argument="$(echo "$actions_config_arguments_specifics" | cut -d'|' -f $j)"
                            action_config_argument_built="--args $action_config_argument_name=\"$action_config_actual_argument\""
                        elif [ -n "$action_config_argument_default" ]
                        then
                            # Build the argument from the default value
                            local action_config_actual_argument="$action_config_argument_default"
                            action_config_argument_built="--args $action_config_argument_name=\"$action_config_actual_argument\""
                        else
                            log_warning "> No argument into the check_process to use or default argument for \"$action_config_name\"..."
                            action_config_actual_argument=""
                        fi

                        if [ "$test_type" == "config_panel" ]
                        then
                            log_info "> Apply the configuration for \"$action_config_name\" with the argument \"$action_config_actual_argument\"..."
                        elif [ "$test_type" == "actions" ]
                        then
                            log_info "> Execute the action \"$action_config_name\" with the argument \"$action_config_actual_argument\"..."
                        fi
                    else
                        log_info "> Execute the action \"$action_config_name\"..."
                    fi

                    if [ "$test_type" == "config_panel" ]
                    then
                        # Aply a configuration
                        RUN_YUNOHOST_CMD "app config apply $app_id $action_config_action $action_config_argument_built"
                    elif [ "$test_type" == "actions" ]
                    then
                        # Execute an action
                        RUN_YUNOHOST_CMD "app action run $app_id $action_config_action $action_config_argument_built"
                    fi
                    validate_action_config_panel "yunohost action $action_config_action"
                done
            done
        fi
    done

    # Uses the default snapshot
    current_snapshot=snap0
    # Stop and restore the LXC container
    LXC_STOP >> $complete_log
}

PACKAGE_LINTER () {
    # Package linter

    start_test "Package linter"

    # Execute package linter and linter_result gets the return code of the package linter
    "./package_linter/package_linter.py" "$package_path" > "./temp_linter_result.log"
    "./package_linter/package_linter.py" "$package_path" --json > "./temp_linter_result.json"

    # Print the results of package linter and copy these result in the complete log
    cat "./temp_linter_result.log" | tee --append "$complete_log"
    cat "./temp_linter_result.json" >> "$complete_log"

    SET_RESULT linter_broken  0
    SET_RESULT linter_level_6 0
    SET_RESULT linter_level_7 0
    SET_RESULT linter_level_8 0

    # Check we qualify for level 6, 7, 8
    # Linter will have a warning called "app_in_github_org" if app ain't in the
    # yunohost-apps org...
    if ! cat "./temp_linter_result.json" | jq ".warning" | grep -q "app_in_github_org"
    then
        SET_RESULT linter_level_6 1
    fi
    if cat "./temp_linter_result.json" | jq ".success" | grep -q "qualify_for_level_7"
    then
        SET_RESULT linter_level_7 1
    fi
    if cat "./temp_linter_result.json" | jq ".success" | grep -q "qualify_for_level_8"
    then
        SET_RESULT linter_level_8 1
    fi

    # If there are any critical errors, we'll force level 0
    if [[ -n "$(cat "./temp_linter_result.json" | jq ".critical" | grep -v '\[\]')" ]]
    then
        log_report_test_failed
        SET_RESULT linter_broken 1
        SET_RESULT linter -1
        # If there are any regular errors, we'll cap to 4
    elif [[ -n "$(cat "./temp_linter_result.json" | jq ".error" | grep -v '\[\]')" ]]
    then
        log_report_test_failed
        SET_RESULT linter -1
        # Otherwise, test pass (we'll display a warning depending on if there are
        # any remaning warnings or not)
    else
        if [[ -n "$(cat "./temp_linter_result.json" | jq ".warning" | grep -v '\[\]')" ]]
        then
            log_report_test_warning
        else
            log_report_test_success
        fi
        SET_RESULT linter 1
    fi
}

set_witness_files () {
    # Create files to check if the remove script does not remove them accidentally
    echo "Create witness files..." >> "$complete_log"

    create_witness_file () {
        [ "$2" = "file" ] && local action="touch" || local action="mkdir -p"
        sudo $action "${LXC_ROOTFS}${1}"
    }

    # Nginx conf
    create_witness_file "/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf" file
    create_witness_file "/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf" file

    # /etc
    create_witness_file "/etc/witnessfile" file

    # /opt directory
    create_witness_file "/opt/witnessdir" directory

    # /var/www directory
    create_witness_file "/var/www/witnessdir" directory

    # /home/yunohost.app/
    create_witness_file "/home/yunohost.app/witnessdir" directory

    # /var/log
    create_witness_file "/var/log/witnessfile" file

    # Config fpm
    if [ -d "${LXC_ROOTFS}/etc/php5/fpm" ]; then
        create_witness_file "/etc/php5/fpm/pool.d/witnessfile.conf" file
    fi
    if [ -d "${LXC_ROOTFS}/etc/php/7.0/fpm" ]; then
        create_witness_file "/etc/php/7.0/fpm/pool.d/witnessfile.conf" file
    fi
    if [ -d "${LXC_ROOTFS}/etc/php/7.3/fpm" ]; then
        create_witness_file "/etc/php/7.3/fpm/pool.d/witnessfile.conf" file
    fi

    # Config logrotate
    create_witness_file "/etc/logrotate.d/witnessfile" file

    # Config systemd
    create_witness_file "/etc/systemd/system/witnessfile.service" file

    # Database
    RUN_INSIDE_LXC mysqladmin --user=root --password=$(sudo cat "$LXC_ROOTFS/etc/yunohost/mysql") --wait status > /dev/null 2>&1
    RUN_INSIDE_LXC mysql --user=root --password=$(sudo cat "$LXC_ROOTFS/etc/yunohost/mysql") --wait --execute="CREATE DATABASE witnessdb" > /dev/null 2>&1
}

check_witness_files () {
    # Check all the witness files, to verify if them still here

    check_file_exist () {
        if sudo test ! -e "${LXC_ROOTFS}${1}"
        then
            log_error "The file $1 is missing ! Something gone wrong !"
            SET_RESULT witness 1
        fi
    }

    # Nginx conf
    check_file_exist "/etc/nginx/conf.d/$DOMAIN.d/witnessfile.conf"
    check_file_exist "/etc/nginx/conf.d/$SUBDOMAIN.d/witnessfile.conf"

   # /etc
    check_file_exist "/etc/witnessfile"

    # /opt directory
    check_file_exist "/opt/witnessdir"

    # /var/www directory
    check_file_exist "/var/www/witnessdir"

    # /home/yunohost.app/
    check_file_exist "/home/yunohost.app/witnessdir"

    # /var/log
    check_file_exist "/var/log/witnessfile"

    # Config fpm
    if [ -d "${LXC_ROOTFS}/etc/php5/fpm" ]; then
        check_file_exist "/etc/php5/fpm/pool.d/witnessfile.conf" file
    fi
    if [ -d "${LXC_ROOTFS}/etc/php/7.0/fpm" ]; then
        check_file_exist "/etc/php/7.0/fpm/pool.d/witnessfile.conf" file
    fi
    if [ -d "${LXC_ROOTFS}/etc/php/7.3/fpm" ]; then
        check_file_exist "/etc/php/7.3/fpm/pool.d/witnessfile.conf" file
    fi

    # Config logrotate
    check_file_exist "/etc/logrotate.d/witnessfile"

    # Config systemd
    check_file_exist "/etc/systemd/system/witnessfile.service"

    # Database
    if ! RUN_INSIDE_LXC mysqlshow --user=root --password=$(sudo cat "$LXC_ROOTFS/etc/yunohost/mysql") witnessdb > /dev/null 2>&1
    then
        log_error "The database witnessdb is missing ! Something gone wrong !"
        SET_RESULT witness 1
    fi
    if [ $(GET_RESULT witness) -eq 1 ]
    then
        yunohost_result=1
    fi
}

RUN_TEST_SERIE() {
    # Launch all tests successively
    test_serie_dir=$1

    curl_error=0
    yuno_portal=0

    _path_arg=$(grep -m1 "(PATH)" $test_serie_dir/check_process.manifest_infos | grep -o "\S+=\S+" | awk -F= '{print $2}' | tr -d '"')
    [ -n "$_path_arg" ] && test_path="$_path_arg" || test_path="/"

    log_title "Tests serie: $(cat $test_serie_dir/test_serie_name)"

    # Be sure that the container is running
    LXC_START "true"

    log_small_title "YunoHost versions"

    # Print the version of YunoHost from the LXC container
    LXC_START "sudo yunohost --version"

    source $test_serie_dir/tests_to_perform
    # Init the value for the current test
    current_test_number=1

    # We will chech that the app can be accessed
    # (except if it's a no-url app)
    [ $setup_nourl      -eq 0 ] \
        && enable_validate_that_app_can_be_accessed="true" \
        ||enable_validate_that_app_can_be_accessed="false"

    # Check the package with package linter
    [ $pkg_linter       -eq 1 ] && PACKAGE_LINTER

    # Try to install in a sub path
    [ $setup_sub_dir    -eq 1 ] && TEST_LAUNCHER TEST_INSTALL subdir

    # Try to install on root
    [ $setup_root       -eq 1 ] && TEST_LAUNCHER TEST_INSTALL root

    # Try to install without url access
    [ $setup_nourl      -eq 1 ] && TEST_LAUNCHER TEST_INSTALL no_url

    # Try the upgrade script
    [ $upgrade          -eq 1 ] && TEST_LAUNCHER TEST_UPGRADE

    # Try to install in private mode
    [ $setup_private    -eq 1 ] && TEST_LAUNCHER TEST_PUBLIC_PRIVATE private

    # Try to install in public mode
    [ $setup_public     -eq 1 ] && TEST_LAUNCHER TEST_PUBLIC_PRIVATE public

    # Try multi-instance installations
    [ $multi_instance   -eq 1 ] && TEST_LAUNCHER TEST_MULTI_INSTANCE

    # Try to install with a port already used
    [ $port_already_use -eq 1 ] && TEST_LAUNCHER TEST_PORT_ALREADY_USED

    # Try to backup then restore the app
    [ $backup_restore   -eq 1 ] && TEST_LAUNCHER TEST_BACKUP_RESTORE

    # Try the change_url script
    [ $change_url       -eq 1 ] && TEST_LAUNCHER TEST_CHANGE_URL

    # Try the actions
    [ $actions          -eq 1 ] && TEST_LAUNCHER ACTIONS_CONFIG_PANEL actions

    # Try the config-panel
    [ $config_panel     -eq 1 ] && TEST_LAUNCHER ACTIONS_CONFIG_PANEL config_panel
}

TEST_LAUNCHER () {
    # Abstract for test execution.
    # $1 = Name of the function to execute
    # $2 = Argument for the function

    # Intialize values
    yunohost_result=-1

    # Start the timer for this test
    start_timer
    # And keep this value separately
    local global_start_timer=$starttime

    # Execute the test
    $1 $2

    # Uses the default snapshot
    current_snapshot=snap0

    # Stop and restore the LXC container
    LXC_STOP >> $complete_log

    # Restore the started time for the timer
    starttime=$global_start_timer
    # End the timer for the test
    stop_timer 2

    # Update the lock file with the date of the last finished test.
    # $$ is the PID of package_check itself.
    echo "$1 $2:$(date +%s):$$" > "$lock_file"

}