#!/bin/bash

echo -e "Loads functions from testing_process.sh"

#=================================================
# Globals variables
#=================================================

# 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"

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

break_before_continue () {
	# Make a break if auto_remove is set

	if [ $auto_remove -eq 0 ] && [ $bash_mode -ne 1 ]
	then
		LXC_CONNECT_INFO	# Print access information
		read -p "Press a key to delete the application and continue...." < /dev/tty
	fi
}

check_false_positive_error () {
    # Check if FALSE_ERRORS_DETECTION has detected an false positive error.

    # We will use
    # check_false_positive_error || return $?
    # to check the result of the function of propagate the result to parent functions.

    if [ $false_positive_error -eq 1 ] && [ $false_positive_error_loop -lt $max_false_positive_error_loop ]; then
        # Return 75, for EX_TEMPFAIL from sysexits.h
        return 75
    fi
}

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

git_wait_for_pull () {
    local_git_repo="$1"
    min_time_between_pull=$2
    last_pull=$(cat "$local_git_repo/last_pull")
    last_pull=${last_pull:-0}

    if [ $(( $(date +%s) - $last_pull )) -ge $min_time_between_pull ]
    then
        date +%s > "$local_git_repo/last_pull"
        return 0
    else
        return 1
    fi
}

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

# In complement of all checks done by Linter, check if the template is respected.
CHECK_TEMPLATE () {

    template_error=0
    app_scripts_dir="$package_path/scripts"
    # For some check, we don't want to look at the standard scripts.
    app_scripts="backup|change_url|config|install|remove|restore|upgrade"

    grep_command="grep --extended-regexp --dereference-recursive --line-number"
    grep_ignore_scripts="grep --invert-match --extended-regexp $app_scripts"

    show_error () {
        messageL1="$1"
        messageL2="$2"
        messageL3="$3"
        # Keep only the first line found as evidence
        evidence="$(echo "$template_output" | head --lines=1)"
        evidence="${evidence##$package_path/}"
        if [ -n "$template_output" ]
        then
            if [ $template_error -eq 0 ]
            then
                all_test=$((all_test+1))
                unit_test_title "Compare the syntax to the packaging template..."

                ECHO_FORMAT "\nLooks like your package does not respect the template of the example app...\n" "red" "bold"
                ECHO_FORMAT "Please follow the template https://github.com/YunoHost/example_ynh\n\n" "lyellow"
            fi
            template_error=1

            ECHO_FORMAT "\n$messageL1\n" "red" "bold"
            if [ -n "$messageL2" ]; then
                ECHO_FORMAT "$messageL2\n" "red" "bold"
            fi
            if [ -n "$messageL3" ]; then
                ECHO_FORMAT "$messageL3\n" "red" "bold"
            fi
            ECHO_FORMAT "Here the culprit:\n" "red" "bold"
            ECHO_FORMAT "$evidence\n" "lyellow"
        fi
    }

    ### Detect common functions that are not helpers
    template_output="$($grep_command "^[[:blank:]]*[[:alnum:][:punct:]]+[[:blank:]]*\([[:blank:]]*\)" "$app_scripts_dir" | $grep_ignore_scripts)"
    # Put aside ynh_ like helpers
    helper_like="$(echo "$template_output" | sed -n '/:[[:blank:]]*ynh_/p')"
    # Remove ynh_ like helpers
    template_output="$(echo "$template_output" | sed '/:[[:blank:]]*ynh_/d')"
    # Remove find_mails, which is a part of ynh_send_readme_to_admin
    template_output="$(echo "$template_output" | sed '/find_mails/d')"
    # And at_least_one, part of ynh_get_scalable_phpfpm
    template_output="$(echo "$template_output" | sed '/at_least_one/d')"
    show_error "Do not use specific functions aside of ynh helpers." "Keep your code linear directly into the scripts to ease the reading of your scripts"


    ### Detect functions that looks like YunoHost helpers
    if [ -n "$helper_like" ]
    then
        # Download official helpers and YunoHost helpers
        # Get officials helpers
        official_helper_dir="$script_dir/helpers/Officials"
        mkdir -p "$official_helper_dir"
        ( cd "$official_helper_dir"
        # If we don't already have the .git directory
        if [ ! -d .git ]
        then
            # Set the git clone
            git init > /dev/null 2>&1
            git remote add origin -f https://github.com/YunoHost/yunohost > /dev/null 2>&1
            git config core.sparseCheckout true
            # Get only the helper directory
            echo "/data/helpers.d" > .git/info/sparse-checkout
        fi
        # To not pull more than once a hour
        if git_wait_for_pull "$official_helper_dir" 3600; then
            git pull origin HEAD > /dev/null 2>&1
        fi )

        # And get experimentals helpers
        experimental_helper_dir="$script_dir/helpers/Experimental_helpers"
        ( cd "$script_dir/helpers"
        # If we don't already have the repository
        if [ ! -d Experimental_helpers ]
        then
            # Clone the repository
            git clone https://github.com/YunoHost-Apps/Experimental_helpers > /dev/null 2>&1
        fi
        cd Experimental_helpers
        # To not pull more than once a hour
        if git_wait_for_pull "$experimental_helper_dir" 3600; then
            git pull origin HEAD > /dev/null 2>&1
        fi )

        # Check each helper found
        while read helper
        do
            # Keep only the name of the helper
            helper_name=$(echo "$helper" | sed 's/.*:\([[:alnum:][:punct:]]*\).*/\1/' | sed 's/()//')

            # Try to find the helper whether in officials or experimentals
            if ! $grep_command --quiet "^$helper_name" "$script_dir/helpers"
            then
                template_output="$helper"
                show_error "This helper does not look like an existing helper." "Do not use specific functions aside of ynh helpers." "Keep your code linear directly into the scripts to ease the reading of your scripts"
                break
            fi
        done <<< "$(echo "$helper_like")"
    fi


    ### Detect usage of ynh_render_template
    template_output="$($grep_command --word-regexp "ynh_render_template" "$app_scripts_dir")"
    show_error "Do not use ynh_render_template in app scripts." "Use ynh_replace_string instead to keep clear all modifications made on files"


    ### Detect presence of comment separators
    for script in ${app_scripts//|/ }
    do
        if [ -e "$app_scripts_dir/$script" ]
        then
            template_output="$($grep_command --count "#=================" "$app_scripts_dir/$script")"
            if [ $template_output -lt 12 ]
            then
                template_output="$app_scripts_dir/$script"
                show_error "Looks like there isn't many comment separators in your script" "Consider adding more separators to clarify the steps in your script"
            fi
        fi
    done


    ### Detect presence of progression indicators
    for script in ${app_scripts//|/ }
    do
        if [ -e "$app_scripts_dir/$script" ] && [ "$script" != "config" ]
        then
            template_output="$($grep_command --count "ynh_script_progression|ynh_print_info" "$app_scripts_dir/$script")"
           if [ $template_output -lt 5 ] || ( [ "$script" = "change_url" ] && [ $template_output -lt 3 ] )
           then
               template_output="$app_scripts_dir/$script"
               show_error "Looks like there isn't many comment progression indicators in your script" "Consider adding more indicators to help your users know the progression of the scripts"
           fi
        fi
    done


    ### Detect usage of ynh_restore
    template_output="$($grep_command --word-regexp "ynh_restore" "$app_scripts_dir")"
    show_error "Do not use ynh_restore in app scripts." "Use ynh_restore_file instead to keep clear all files that are restored and in which order."


    ### Detect usage of set -u or set -e in remove script
    template_output="$($grep_command --word-regexp "set -u|set -e" "$app_scripts_dir/remove")"
    show_error "Do not use 'set -e' or 'set -u' in a remove script." "If an error happen during the script, the app will be half removed !"

    if [ $template_error -eq 1 ]
    then
        echo ""
        RESULT_template=1
        check_failed
    fi
}

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

PRINT_YUNOHOST_VERSION () {
	ECHO_FORMAT ">> YunoHost versions:\n" "white" "bold" clog
	# Be sure that the container is running
	LXC_START "true"
	# Print the version of YunoHost from the LXC container
	ssh $arg_ssh $lxc_name "sudo yunohost --version"

	# Get the version of YunoHost from the LXC container
	ynh_version=$(ssh -q $lxc_name "sudo yunohost --version --output-as plain | grep -A4 moulinette | tail -n1 | sed 's@\.@@g' | cut -d+ -f1")
}

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

SETUP_APP () {
	# Install an application in a LXC container

	# Uses the default snapshot
	current_snapshot=snap0

	# Exec the pre-install instruction, if there one
	if [ -n "$pre_install" ]
	then
		ECHO_FORMAT "Pre installation request\n" "white" "bold" clog
		# Start the lxc container
		LXC_START "true"
		# Copy all the instructions into a script
		echo "$pre_install" > "$script_dir/preinstall.sh"
		chmod +x "$script_dir/preinstall.sh"
		# Replace variables
		sed -i "s/\$USER/$test_user/" "$script_dir/preinstall.sh"
		sed -i "s/\$DOMAIN/$main_domain/" "$script_dir/preinstall.sh"
		sed -i "s/\$SUBDOMAIN/$sub_domain/" "$script_dir/preinstall.sh"
		sed -i "s/\$PASSWORD/$yuno_pwd/" "$script_dir/preinstall.sh"
		# Copy the pre-install script into the container.
		scp -rq "$script_dir/preinstall.sh" "$lxc_name":
		# Then execute the script to execute the pre-install commands.
		LXC_START "./preinstall.sh >&2"
		# Print the log to print the results
		ECHO_FORMAT "$(cat "$temp_log")\n" clog
	fi

	# Install the application in a LXC container
	LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app install --force \"$package_dir\" -a \"$manifest_args_mod\""

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

	# Print the result of the install command
	if [ $yunohost_result -eq 0 ]; then
		ECHO_FORMAT "Installation successful. ($yunohost_result)\n" "white" clog
	else
		ECHO_FORMAT "Installation failed. ($yunohost_result)\n" "white" clog
	fi

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

	# Retrieve the app id in the log. To manage the app after
	ynh_app_id=$(sudo tac "$yunohost_log" | grep --only-matching --max-count=1 "YNH_APP_INSTANCE_NAME=[^ ]*" | cut --delimiter='=' --fields=2)

	# Analyse the log to extract "warning" and "error" lines
	LOG_EXTRACTOR
	check_false_positive_error || return $?
}

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

	# If it's a root install
	if [ "$check_path" = "/" ]
	then
		# Check if a snapshot already exist for this install
		if [ -z "$root_snapshot" ]
		then
			# Make an installation
			SETUP_APP
            check_false_positive_error || return $?

            # 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 [ $yunohost_result -eq 0 ]; then
                # Check if a snapshot already exist for a root install
                if [ -z "$root_snapshot" ]
                then
                    ECHO_FORMAT "\nCreate a snapshot for root installation.\n" "white" clog
                    create_temp_backup 2
                    root_snapshot=snap2
                fi
            fi
		else
			# Or uses an existing snapshot
			ECHO_FORMAT "Uses an existing snapshot for root installation.\n" "white" clog
			use_temp_snapshot $root_snapshot
		fi

	# In case of sub path install, use another snapshot
	else
		# Check if a snapshot already exist for this install
		if [ -z "$subpath_snapshot" ]
		then
			# Make an installation
			SETUP_APP
            check_false_positive_error || return $?

            # 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 [ $yunohost_result -eq 0 ]; then
                # Check if a snapshot already exist for a subpath (or no_url) install
                if [ -z "$subpath_snapshot" ]
                then
                    ECHO_FORMAT "\nCreate a snapshot for sub path installation.\n" "white" clog
                    create_temp_backup 1
                    root_snapshot=snap1
                fi
            fi
		else
			# Or uses an existing snapshot
			ECHO_FORMAT "Uses an existing snapshot for sub path installation.\n" "white" clog
			use_temp_snapshot $subpath_snapshot
		fi
	fi
}

REMOVE_APP () {
	# Remove an application

	# Make a break if auto_remove is set
	break_before_continue

	ECHO_FORMAT "\nDeleting...\n" "white" "bold" clog

	# Remove the application from the LXC container
	LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app remove \"$ynh_app_id\""

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

	# Print the result of the remove command
	if [ "$yunohost_remove" -eq 0 ]; then
		ECHO_FORMAT "Deleting successful. ($yunohost_remove)\n" "white" clog
	else
		ECHO_FORMAT "Deleting failed. ($yunohost_remove)\n" "white" clog
	fi

	# Check all the witness files, to verify if them still here
	check_witness_files
}

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

CHECK_URL () {
	# Try to access the app by its url

	if [ $use_curl -eq 1 ]
	then
		ECHO_FORMAT "\nTry to access by url...\n" "white" "bold"

		# Force a skipped_uris if public mode is not set
		if [ "$install_type" != "private" ] && [ "$install_type" != "public" ] && [ -z "$public_arg" ]
		then
			# Add a skipped_uris on / for the app
			LXC_START "sudo yunohost app setting \"$ynh_app_id\" skipped_uris -v \"/\""
			# Regen the config of sso
			LXC_START "sudo yunohost app ssowatconf"
			ECHO_FORMAT "Public access forced by a skipped_uris to check.\n" "lyellow" "bold"
		fi

		# Inform /etc/hosts with the IP of LXC to resolve the domain.
		# This is set only here and not before to prevent to help the app's scripts
		echo -e "$ip_range.2 $main_domain #package_check\n$ip_range.2 $sub_domain #package_check" | sudo tee --append /etc/hosts > /dev/null

		# Try to resolv the domain during 10 seconds maximum.
		local i=0
		for i in `seq 1 10`; do
			curl --location --insecure $check_domain > /dev/null 2>&1
			# If curl return 6, it's an error "Could not resolve host"
			if [ $? -ne 6 ]; then
				# If not, curl is ready to work.
				break
			fi
			echo -n .
			sleep 1
		done

		# 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 "$script_dir/url_output"

			# Call curl to try to access to the url of the app
			curl --location --insecure --silent --show-error --write-out "%{http_code};%{url_effective}\n" $check_domain$curl_check_path --output "$script_dir/url_output" > "$script_dir/curl_print"

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

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

			# Analyze the http code
			if [ "${http_code:0:1}" = "0" ] || [ "${http_code:0:1}" = "4" ] || [ "${http_code:0:1}" = "5" ]
			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
					ECHO_FORMAT "Service temporarily unavailable\n" "lyellow" "bold"
					# 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
					ECHO_FORMAT "The HTTP code show an error.\n" "white" "bold" clog
				fi
			fi

			# Analyze the output of curl
			if [ -e "$script_dir/url_output" ]
			then
				# Print the title of the page
				local url_title=$(grep "<title>" "$script_dir/url_output" | cut --delimiter='>' --fields=2 | cut --delimiter='<' --fields=1)
				ECHO_FORMAT "Title of the page: $url_title\n" "white"

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

					# Print the first 20 lines of the page
					ECHO_FORMAT "Extract of the page:\n" "white"
					echo -en "\e[37m"	# Write in 'light grey'
					lynx -dump -force_html "$script_dir/url_output" | head --lines 20 | tee --append "$test_result"
					echo -e "\e[0m"

					if [ $show_resources -eq 1 ]
					then
                        # Get all the resources for the main page of the app.
                        local HTTP_return
                        local moved=0
                        local ignored=0
                        while read HTTP_return
                        do
                            # Ignore robots.txt and ynhpanel.js. They always redirect to the portal.
                            if echo "$HTTP_return" | grep --quiet "$check_domain/robots.txt\|$check_domain/ynhpanel.js"; then
                                ECHO_FORMAT "Ressource ignored:" "white"
                                ECHO_FORMAT " ${HTTP_return##*http*://}\n"
                                ignored=1
                            fi

                            # If it's the line with the resource to get
                            if echo "$HTTP_return" | grep --quiet "^--.*--  http"
                            then
                                # Get only the resource itself.
                                local resource=${HTTP_return##*http*://}

                            # Else, if would be the HTTP return code.
                            else
                                # If the return code is different than 200.
                                if ! echo "$HTTP_return" | grep --quiet "200 OK$"
                                then
                                    # Skipped the check of ignored ressources.
                                    if [ $ignored -eq 1 ]
                                    then
                                        ignored=0
                                        continue
                                    fi
                                    # Isolate the http return code.
                                    http_code="${HTTP_return##*awaiting response... }"
                                    http_code="${http_code:0:3}"
                                    # If the return code is 301 or 302, let's check the redirection.
                                    if echo "$HTTP_return" | grep --quiet "30[12] Moved"
                                    then
                                        ECHO_FORMAT "Ressource moved:" "white"
                                        ECHO_FORMAT " $resource\n"
                                        moved=1
                                    else
                                        ECHO_FORMAT "Resource unreachable (Code $http_code)" "red" "bold"
                                        ECHO_FORMAT " $resource\n"
#    					 				curl_error=1
                                        moved=0
                                    fi
                                else
                                    if [ $moved -eq 1 ]
                                    then
                                        if echo "$resource" | grep --quiet "/yunohost/sso/"
                                        then
                                            ECHO_FORMAT "The previous resource is redirected to the YunoHost portal\n" "red"
#   	 					 				curl_error=1
                                        fi
                                    fi
                                    moved=0
                                fi
                            fi
                        done <<< "$(cd "$package_path"; LC_ALL=C wget --adjust-extension --page-requisites --no-check-certificate $check_domain$curl_check_path 2>&1 | grep "^--.*--  http\|^HTTP request sent")"
                    fi
					echo ""
				fi
			fi
		done

		# 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 /var/lib/lxc/$lxc_name/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" \
			&& ECHO_FORMAT "Issue alias_traversal detected ! Please see here https://github.com/YunoHost/example_ynh/pull/45 to fix that.\n" "red" "bold" && RESULT_alias_traversal=1

		# Remove the entries in /etc/hosts for the test domain
		sudo sed --in-place '/#package_check/d' /etc/hosts
	else
		# If use_curl is set to 0, the url will not tried
		ECHO_FORMAT "Connexion attempt aborted.\n" "white"
		curl_error=0
		yuno_portal=0
	fi
}

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

unit_test_title () {
	# Print a title for the test
	# $1 = Name of the test

	ECHO_FORMAT "\n\n>> $1 [Test $cur_test/$all_test]\n" "white" "bold" clog

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

check_manifest_key () {
	# Check if a manifest key is set
	# $1 = manifest key

	if [ -z "${1}_arg" ]
	then
		ECHO_FORMAT "Unable to find a manifest key for '${1,,}' in the check_process file. Impossible to perform this test\n" "red" clog
		return 1
	fi
}

replace_manifest_key () {
	# Replace a generic manifest key by another
	# $1 = Manifest key
	# $2 = Replacement value

	# Build the variable name by concatenate $1 and _arg
	local manifest_key=$(eval echo \$${1}_arg)

	if [ -n "$manifest_key" ]
	then
		manifest_args_mod=$(echo $manifest_args_mod | sed "s@$manifest_key=[^&]*\&@${manifest_key}=${2}\&@")
	fi
}

check_success () {
	ECHO_FORMAT "--- SUCCESS ---\n" "lgreen" "bold"
}

check_failed () {
	ECHO_FORMAT "--- FAIL ---\n" "red" "bold"
}

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
		check_success
		return 0
	else
		check_failed
		return 1
	fi
}

check_test_result_remove () {
	# Check the result of a remove and print SUCCESS or FAIL

	if [ $yunohost_remove -eq 0 ]
	then
		check_success
		return 0
	else
		check_failed
		return 1
	fi
}

is_install_failed () {
	# 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 [ $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 [ $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
		ECHO_FORMAT "All installs have failed, impossible to perform this test...\n" "red" clog
		return 1
	fi
}

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

CHECK_SETUP () {
	# 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
		unit_test_title "Installation in a sub path..."
	elif [ "$install_type" = "root" ]; then
		unit_test_title "Installation on the root..."
	else
		unit_test_title "Installation without url access..."
		# Disable the curl test
		use_curl=0
	fi

	# Check if the needed manifest key are set or abort the test
	if [ "$install_type" != "no_url" ]; then
		check_manifest_key "domain" || return
		check_manifest_key "path" || return
	fi

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

	# Replace manifest key for the test
	check_domain=$sub_domain
	replace_manifest_key "domain" "$check_domain"
	if [ "$install_type" = "subdir" ]; then
		local check_path=$test_path
	elif [ "$install_type" = "root" ]; then
		local check_path=/
	fi
	replace_manifest_key "path" "$check_path"
	replace_manifest_key "user" "$test_user"
	replace_manifest_key "public" "$public_public_arg"

	# Install the application in a LXC container
	SETUP_APP
	check_false_positive_error || return $?

	# Try to access the app by its url
	CHECK_URL

	# Check the result and print SUCCESS or FAIL
	if check_test_result
	then	# Success
		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
		if [ $RESULT_global_setup -ne 1 ]; then
			RESULT_global_setup=-1	# Installation failed
		fi
		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
				ECHO_FORMAT "Create a snapshot for root installation.\n" "white" clog
				create_temp_backup 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
				ECHO_FORMAT "Create a snapshot for sub path installation.\n" "white" clog
				create_temp_backup 1
				subpath_snapshot=snap1
			fi
		fi
	fi

	# Remove the application
	REMOVE_APP

	# Analyse the log to extract "warning" and "error" lines
	LOG_EXTRACTOR
	check_false_positive_error || return $?

	# Check the result and print SUCCESS or FAIL
	if check_test_result_remove
	then	# Success
		local check_result_remove=1	# Remove in sub path succeed
		RESULT_global_remove=1	# Remove succeed
	else	# Fail
		# The global success for a deletion can't be failed if another remove succeed
		if [ $RESULT_global_remove -ne 1 ]; then
			RESULT_global_remove=-1	# Remove failed
		fi
		local check_result_remove=-1	# Remove in sub path failed
	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
		ECHO_FORMAT "\nReinstall the application after a removing.\n" "white" "bold" clog
		
		SETUP_APP
        check_false_positive_error || return $?

		# Try to access the app by its url
		CHECK_URL

		# 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
		RESULT_check_sub_dir=$check_result_setup
		RESULT_check_remove_sub_dir=$check_result_remove
	else	# root and no_url
		RESULT_check_root=$check_result_setup
		RESULT_check_remove_root=$check_result_remove
	fi

	# Make a break if auto_remove is set
	break_before_continue
}

CHECK_UPGRADE () {
	# Try the upgrade script

	# Do an upgrade test for each commit in the upgrade list
	while read <&4 commit
	do
		if [ "$commit" == "current" ]
		then
			unit_test_title "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=" "$partial_check_process" | cut -d'=' -f2)
			# Or use the commit if there's no name.
			if [ -z "$upgrade_name" ]; then
				unit_test_title "Upgrade from the commit $commit..."
			else
				unit_test_title "Upgrade from $upgrade_name..."
			fi
		fi

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

		# Copy original arguments
		local manifest_args_mod="$manifest_arguments"

		# Replace manifest key for the test
		check_domain=$sub_domain
		replace_manifest_key "domain" "$check_domain"
		# 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
		replace_manifest_key "path" "$check_path"
		replace_manifest_key "user" "$test_user"
		replace_manifest_key "public" "$public_public_arg"

		# Install the application in a LXC container
		ECHO_FORMAT "\nPreliminary install...\n" "white" "bold" clog
		if [ "$commit" == "current" ]
		then
			# If no commit is specified, use the current version.
			STANDARD_SETUP_APP
            check_false_positive_error || return $?
		else
			# Otherwise, use a specific commit
			# Backup the modified arguments
			update_manifest_args="$manifest_args_mod"
			# Get the arguments of the manifest for this upgrade.
			manifest_args_mod="$(grep "^manifest_arg=" "$partial_check_process" | cut -d'=' -f2-)"
			if [ -z "$manifest_args_mod" ]; then
				# If there's no specific arguments, use the previous one.
				manifest_args_mod="$update_manifest_args"
			else
				# Otherwise, keep the new arguments, and replace the variables.
				manifest_args_mod="${manifest_args_mod//DOMAIN/$check_domain}"
				manifest_args_mod="${manifest_args_mod//PATH/$check_path}"
				manifest_args_mod="${manifest_args_mod//USER/$test_user}"
			fi
			# Make a backup of the directory
			sudo cp -a "$package_path" "${package_path}_back"
			# Change to the specified commit
			(cd "$package_path"; git checkout --force --quiet "$commit")
			# Install the application
			SETUP_APP
            check_false_positive_error || return $?
			# Then replace the backup
			sudo rm -r "$package_path"
			sudo mv "${package_path}_back" "$package_path"
			# And restore the arguments for the manifest
			manifest_args_mod="$update_manifest_args"
		fi

		# Check if the install had work
		if [ $yunohost_result -ne 0 ]
		then
			ECHO_FORMAT "\nInstallation failed...\n" "red" "bold"
			ECHO_FORMAT "\nUpgrade test ignored...\n" "red" "bold"
		else
			ECHO_FORMAT "\nUpgrade...\n" "white" "bold" clog

			# Upgrade the application in a LXC container
			LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app upgrade $ynh_app_id -f \"$package_dir\""

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

			# Print the result of the upgrade command
			if [ $yunohost_result -eq 0 ]; then
				ECHO_FORMAT "Upgrade successful. ($yunohost_result)\n" "white" clog
			else
				ECHO_FORMAT "Upgrade failed. ($yunohost_result)\n" "white" clog
			fi

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

			# Analyse the log to extract "warning" and "error" lines
			LOG_EXTRACTOR
			check_false_positive_error || return $?

			# Try to access the app by its url
			CHECK_URL

            # 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
                if [ $RESULT_check_upgrade -ne -1 ]; then
                    RESULT_check_upgrade=1	# Upgrade succeed
                fi
            else	# Fail
                RESULT_check_upgrade=-1	# Upgrade failed
            fi

            # Remove the application
            REMOVE_APP
		fi

		# Uses the default snapshot
		current_snapshot=snap0
		# Stop and restore the LXC container
		LXC_STOP
	done 4< "$script_dir/upgrade_list"
}

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

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

	# Check if the needed manifest key are set or abort the test
	check_manifest_key "public" || return
	check_manifest_key "public_public" || return
	check_manifest_key "public_private" || return

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

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

	# Replace manifest key for the test
	check_domain=$sub_domain
	replace_manifest_key "domain" "$check_domain"
	replace_manifest_key "user" "$test_user"
	# Set public or private according to type of test requested
	if [ "$install_type" = "private" ]; then
		replace_manifest_key "public" "$public_private_arg"
	elif [ "$install_type" = "public" ]; then
		replace_manifest_key "public" "$public_public_arg"
	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=/
				replace_manifest_key "path" "$check_path"
			else
				# Jump to the second path if this check cannot be do
				ECHO_FORMAT "Root install failed, impossible to perform this test...\n" "lyellow" clog
				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
				replace_manifest_key "path" "$check_path"
			else
				# Jump to the second path if this check cannot be do
				ECHO_FORMAT "Sub path install failed, impossible to perform this test...\n" "lyellow" clog
				return
			fi
		fi

		# Install the application in a LXC container
		SETUP_APP
        check_false_positive_error || return $?

		# Try to access the app by its url
		CHECK_URL

		# 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
				ECHO_FORMAT "App is not private: it should redirect to the Yunohost portal, but is publicly accessible instead\n" "lyellow" clog
				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
				ECHO_FORMAT "App page is not public: it should be publicly accessible, but redirects to the Yunohost portal instead\n" "lyellow" clog
				yunohost_result=1
			fi
		fi

		# Check the result and print SUCCESS or FAIL
		if [ $yunohost_result -eq 0 ] && [ $curl_error -eq 0 ]
		then	# Success
			check_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	# Fail
			check_failed
			check_result_public_private=-1	# Installation failed
		fi

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

		# Make a break if auto_remove is set
		break_before_continue

		# Stop and restore the LXC container
		LXC_STOP
	done
}

CHECK_MULTI_INSTANCE () {
	# Try multi-instance installations

	unit_test_title "Multi-instance installations..."

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

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

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

	replace_manifest_key "user" "$test_user"
	replace_manifest_key "public" "$public_public_arg"

	# Install 2 times the same app
	local i=0
	for i in 1 2
	do
		# First installation
		if [ $i -eq 1 ]
		then
			check_domain=$main_domain
			ECHO_FORMAT "First installation: path=$check_domain$check_path\n" clog
		# Second installation
		elif [ $i -eq 2 ]
		then
			check_domain=$sub_domain
			ECHO_FORMAT "Second installation: path=$check_domain$check_path\n" clog
		fi

		# Replace path and domain manifest keys for the test
		replace_manifest_key "domain" "$check_domain"

		# Install the application in a LXC container
		SETUP_APP
        check_false_positive_error || return $?

		# Store the result in the correct variable
		# First installation
		if [ $i -eq 1 ]
		then
			local multi_yunohost_result_1=$yunohost_result
			local ynh_app_id_1=$ynh_app_id
		# Second installation
		elif [ $i -eq 2 ]
		then
			local multi_yunohost_result_2=$yunohost_result
			local ynh_app_id_2=$ynh_app_id
		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=$main_domain
			ynh_app_id=$ynh_app_id_1
		# Second app
		elif [ $i -eq 2 ]
		then
			check_domain=$sub_domain
			ynh_app_id=$ynh_app_id_2
		fi

		# Try to access the app by its url
		CHECK_URL

		# 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
		check_success
		RESULT_check_multi_instance=1
	else	# Fail
		check_failed
		RESULT_check_multi_instance=-1
	fi

	# Make a break if auto_remove is set
	break_before_continue
}

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

	local install_type=$1
	if [ "$install_type" = "incorrect_path" ]; then
		unit_test_title "Malformed path..."
		# Check if the needed manifest key are set or abort the test
		check_manifest_key "path" || return
	else [ "$install_type" = "port_already_use" ]
		unit_test_title "Port already used..."
		# Check if the needed manifest key are set or abort the test
		check_manifest_key "port" || return
	fi

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

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

	# Replace manifest key for the test
	check_domain=$sub_domain
	replace_manifest_key "domain" "$check_domain"
	replace_manifest_key "user" "$test_user"
	replace_manifest_key "public" "$public_public_arg"

	# Replace path manifest key for the test
	if [ "$install_type" = "incorrect_path" ]; then
		# Change the path from /path to path/
		local wrong_path=${test_path#/}/
		# Use this wrong path only for the arguments that will give to yunohost for installation.
		replace_manifest_key "path" "$wrong_path"
		local check_path=$test_path
	else [ "$install_type" = "port_already_use" ]
		# 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
		replace_manifest_key "path" "$check_path"
	fi

	# Open the specified port to force the script to find another
	if [ "$install_type" = "port_already_use" ]
	then

		# If the first character is a #, that means it this port number is not in the manifest
		if [ "${port_arg:0:1}" = "#" ]
		then
			# Retrieve the port number
			local check_port="${port_arg:1}"

		# Else, the port number is in the manifest. So the port number is set at a fixed value.
		else
			local check_port=6660
			# Replace port manifest key for the test
			replace_manifest_key "port" "$check_port"
		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 "/var/lib/lxc/$lxc_name/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"
	fi

	# Install the application in a LXC container
	SETUP_APP
	check_false_positive_error || return $?

	# Try to access the app by its url
	CHECK_URL

	# 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
	if [ "$install_type" = "incorrect_path" ]
	then
		RESULT_check_path=$check_result_setup
	elif [ "$install_type" = "port_already_use" ]; then
		RESULT_check_port=$check_result_setup
	fi

	# Make a break if auto_remove is set
	break_before_continue
}

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

	unit_test_title "Backup/Restore..."

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

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

	# Replace manifest key for the test
	check_domain=$sub_domain
	replace_manifest_key "domain" "$check_domain"
	replace_manifest_key "user" "$test_user"
	replace_manifest_key "public" "$public_public_arg"

	# 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=/
				replace_manifest_key "path" "$check_path"
				ECHO_FORMAT "\nPreliminary installation on the root...\n" "white" "bold" clog
			else
				# Jump to the second path if this check cannot be do
				ECHO_FORMAT "Root install failed, impossible to perform this test...\n" "lyellow" clog
				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
				replace_manifest_key "path" "$check_path"
				ECHO_FORMAT "\nPreliminary installation in a sub path...\n" "white" "bold" clog
			else
				# Jump to the second path if this check cannot be do
				ECHO_FORMAT "Sub path install failed, impossible to perform this test...\n" "lyellow" clog
				return
			fi
		fi

		# Install the application in a LXC container
		STANDARD_SETUP_APP
        check_false_positive_error || return $?

		# Remove the previous residual backups
		sudo rm -rf /var/lib/lxc/$lxc_name/rootfs/home/yunohost.backup/archives
		sudo rm -rf /var/lib/lxcsnaps/$lxc_name/$current_snapshot/rootfs/home/yunohost.backup/archives

		# BACKUP
		# Made a backup if the installation succeed
		if [ $yunohost_result -ne 0 ]
		then
			ECHO_FORMAT "\nInstallation failed...\n" "red" "bold"
		else
			ECHO_FORMAT "\nBackup of the application...\n" "white" "bold" clog

			# Made a backup of the application
			LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug backup create -n Backup_test --apps $ynh_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
				ECHO_FORMAT "Backup successful. ($yunohost_result)\n" "white" clog
			else
				ECHO_FORMAT "Backup failed. ($yunohost_result)\n" "white" clog
			fi

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

			# Analyse the log to extract "warning" and "error" lines
			LOG_EXTRACTOR
			check_false_positive_error || return $?
		fi

		# Check the result and print SUCCESS or FAIL
		if [ $yunohost_result -eq 0 ]
		then	# Success
			check_success
			# The global success for a backup can't be a success if another backup failed
			if [ $RESULT_check_backup -ne -1 ]; then
			    RESULT_check_backup=1	# Backup succeed
			fi
		else	# Fail
			check_failed
			RESULT_check_backup=-1	# Backup failed
		fi

		# Grab the backup archive into the LXC container, and keep a copy
		sudo cp -a /var/lib/lxc/$lxc_name/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

				ECHO_FORMAT "\nRestore after removing the application...\n" "white" "bold" clog

			# 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 /var/lib/lxcsnaps/$lxc_name/$current_snapshot/rootfs/home/yunohost.backup/archives

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

				# Stop and restore the LXC container
				LXC_STOP

				ECHO_FORMAT "\nRestore on a clean YunoHost system...\n" "white" "bold" clog
			fi

			# Restore the application from the previous backup
			LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug backup restore Backup_test --force --apps $ynh_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
				ECHO_FORMAT "Restore successful. ($yunohost_result)\n" "white" clog
			else
				ECHO_FORMAT "Restore failed. ($yunohost_result)\n" "white" clog
			fi

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

			# Analyse the log to extract "warning" and "error" lines
			LOG_EXTRACTOR
			check_false_positive_error || return $?

			# Try to access the app by its url
			CHECK_URL

			# Check the result and print SUCCESS or FAIL
			if check_test_result
			then	# Success
				# The global success for a restore can't be a success if another restore failed
				if [ $RESULT_check_restore -ne -1 ]; then
					RESULT_check_restore=1	# Restore succeed
				fi
			else	# Fail
				RESULT_check_restore=-1	# Restore failed
			fi

			# Make a break if auto_remove is set
			break_before_continue

			# Stop and restore the LXC container
			LXC_STOP
		done
	done
}

CHECK_CHANGE_URL () {
	# Try the change_url script

	unit_test_title "Change URL..."

	# Check if the needed manifest key are set or abort the test
	check_manifest_key "domain" || return

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

	# Copy original arguments
	local manifest_args_mod="$manifest_arguments"

	# Replace manifest key for the test
	check_domain=$sub_domain
	replace_manifest_key "domain" "$check_domain"
	replace_manifest_key "user" "$test_user"
	replace_manifest_key "public" "$public_public_arg"

	# 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=$sub_domain
		elif [ $i -eq 2 ]; then
			# Same domain, path to path
			check_path=$test_path
			local new_path=${test_path}_2
			local new_domain=$sub_domain
		elif [ $i -eq 3 ]; then
			# Same domain, path to root
			check_path=$test_path
			local new_path=/
			local new_domain=$sub_domain

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

        # Ignore the test if it tries to move to the same address
        if [ "$check_path" == "$new_path" ] && [ "$new_domain" == "$sub_domain" ]; 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
                # Jump this test
                ECHO_FORMAT "Root install failed, impossible to perform this test...\n" "lyellow" clog
                continue
            elif [ "$new_path" != "/" ] && [ $sub_dir_install -eq 0 ]
            then
                # Jump this test
                ECHO_FORMAT "Sub path install failed, impossible to perform this test...\n" "lyellow" clog
                continue
            fi
        # And with a sub path install
        else
            if [ $sub_dir_install -eq 0 ]
            then
                # Jump this test
                ECHO_FORMAT "Sub path install failed, impossible to perform this test...\n" "lyellow" clog
                continue
            elif [ "$new_path" = "/" ] && [ $root_install -eq 0 ]
            then
                # Jump this test
                ECHO_FORMAT "Root install failed, impossible to perform this test...\n" "lyellow" clog
                continue
            fi
        fi

		# Install the application in a LXC container
		ECHO_FORMAT "\nPreliminary install...\n" "white" "bold" clog
		STANDARD_SETUP_APP
        check_false_positive_error || return $?

		# Check if the install had work
		if [ $yunohost_result -ne 0 ]
		then
			ECHO_FORMAT "Installation failed...\n" "red" "bold"
		else
			ECHO_FORMAT "Change the url from $sub_domain$check_path to $new_domain$new_path...\n" "white" "bold" clog

			# Change the url
			LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app change-url $ynh_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
				ECHO_FORMAT "Change_url script successful. ($yunohost_result)\n" "white" clog
			else
				ECHO_FORMAT "Change_url script failed. ($yunohost_result)\n" "white" clog
			fi

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

			# Analyse the log to extract "warning" and "error" lines
			LOG_EXTRACTOR
			check_false_positive_error || return $?

			# Try to access the app by its url
			check_path=$new_path
			check_domain=$new_domain
			CHECK_URL
		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
			if [ $RESULT_change_url -ne -1 ]; then
				RESULT_change_url=1	# Change_url succeed
			fi
		else	# Fail
			RESULT_change_url=-1	# Change_url failed
		fi

		# Make a break if auto_remove is set
		break_before_continue

		# Uses the default snapshot
		current_snapshot=snap0
		# Stop and restore the LXC container
		LXC_STOP
	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
        unit_test_title "Actions..."

        toml_file="$package_path/actions.toml"
        if [ ! -e "$toml_file" ]
        then
            ECHO_FORMAT "\nNo actions.toml found !\n" "red" "bold"
            return 1
        fi

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

        toml_file="$package_path/config_panel.toml"
        if [ ! -e "$toml_file" ]
        then
            ECHO_FORMAT "\nNo config_panel.toml found !\n" "red" "bold"
            return 1
        fi
    fi

    # Check if the needed manifest key are set or abort the test
    check_manifest_key "domain" || return

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

    # Copy original arguments
    local manifest_args_mod="$manifest_arguments"

    # Replace manifest key for the test
    check_domain=$sub_domain
    replace_manifest_key "domain" "$check_domain"
    replace_manifest_key "user" "$test_user"
    replace_manifest_key "public" "$public_public_arg"
    # 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
    replace_manifest_key "path" "$check_path"

    # Install the application in a LXC container
    ECHO_FORMAT "\nPreliminary install...\n" "white" "bold" clog
    STANDARD_SETUP_APP
    check_false_positive_error || return $?

    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
            ECHO_FORMAT "$message succeed. ($yunohost_result)\n" "white" clog
        else
            ECHO_FORMAT "$message failed. ($yunohost_result)\n" "white" clog
        fi

        # Check all the witness files, to verify if they're still there
        check_witness_files

        # Analyse the log to extract "warning" and "error" lines
        LOG_EXTRACTOR
        check_false_positive_error || return $?

        # 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
            if [ $RESULT_action_config_panel -ne -1 ]; then
                RESULT_action_config_panel=1	# Actions succeed
            fi
        else	# Fail
            RESULT_action_config_panel=-1	# Actions failed
        fi

        # Make a break if auto_remove is set
        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
            ECHO_FORMAT "\nThe previous test has failed...\n" "red" "bold"
        else
            if [ $i -eq 1 ]
            then
                if [ "$test_type" == "actions" ]
                then
                    ECHO_FORMAT "\n> List the available actions...\n" "white" "bold" clog

                    # List the actions
                    LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app action list $ynh_app_id"

                    validate_action_config_panel "yunohost app action list"
                elif [ "$test_type" == "config_panel" ]
                then
                    ECHO_FORMAT "\n> Show the config panel...\n" "white" "bold" clog

                    # Show the config-panel
                    LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app config show-panel $ynh_app_id"
                    validate_action_config_panel "yunohost app config show-panel"
                fi
            elif [ $i -eq 2 ]
            then
                local parts
                if [ "$test_type" == "actions" ]
                then
                    ECHO_FORMAT "\n> Execute the actions...\n" "white" "bold" clog

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

                    # 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
                            local check_process_arguments="$config_panel_arguments"
                        elif [ "$test_type" == "actions" ]
                        then
                            local check_process_arguments="$actions_arguments"
                        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
                                ECHO_FORMAT "\n> No argument into the check_process to use or default argument for \"$action_config_name\"..." "lyellow" "bold" clog
                                action_config_actual_argument=""
                            fi
                            if [ "$test_type" == "config_panel" ]
                            then
                                ECHO_FORMAT "\n> Apply the configuration for \"$action_config_name\" with the argument \"$action_config_actual_argument\"...\n" "white" "bold" clog
                            elif [ "$test_type" == "actions" ]
                            then
                                ECHO_FORMAT "\n> Execute the action \"$action_config_name\" with the argument \"$action_config_actual_argument\"...\n" "white" "bold" clog
                            fi
                        else
                            ECHO_FORMAT "\n> Execute the action \"$action_config_name\"...\n" "white" "bold" clog
                        fi

                        if [ "$test_type" == "config_panel" ]
                        then
                            # Aply a configuration
                            LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app config apply $ynh_app_id $action_config_action $action_config_argument_built"
                        elif [ "$test_type" == "actions" ]
                        then
                            # Execute an action
                            LXC_START "sudo PACKAGE_CHECK_EXEC=1 yunohost --debug app action run $ynh_app_id $action_config_action $action_config_argument_built"
                        fi
                        validate_action_config_panel "yunohost action $action_config_action"
                    done
                done
            fi
        fi
    done

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

PACKAGE_LINTER () {
	# Package linter

	unit_test_title "Package linter..."

	# Execute package linter and linter_result gets the return code of the package linter
	"$script_dir/package_linter/package_linter.py" "$package_path" > "$temp_result"

	# linter_result gets the return code of the package linter
	local linter_result=$?

	# Print the results of package linter and copy these result in the complete log
	cat "$temp_result" | tee --append "$complete_log"

	# Check the result and print SUCCESS or FAIL
	if [ $linter_result -eq 0 ]
	then	# Success
		check_success
		RESULT_linter=1
	else	# Fail
		check_failed
		RESULT_linter=-1

        # If return code is 2, this is critical failure, app should be considered as broken (level 0)
        if [ $linter_result -eq 2 ]
        then
            RESULT_linter=-2
        fi
	fi
}

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

	# Intialize values
	yunohost_result=-1
	yunohost_remove=-1
    false_positive_error=0
    max_false_positive_error_loop=3

    for false_positive_error_loop in $( seq 1 $max_false_positive_error_loop )
    do
        # Start the timer for this test
        start_timer
        # And keep this value separately
        local global_start_timer=$starttime

        # Execute the test
        $1 $2

        if [ $false_positive_error -eq 1 ]
        then
            ECHO_FORMAT "This test was aborted because of a $false_positive_error_cond error.\n" "red" "bold" clog
            if [ $false_positive_error_loop -lt $max_false_positive_error_loop ]
            then
                ECHO_FORMAT "The test will restart.\n" "lyellow" "bold" clog
                cur_test=$((cur_test-1))
            fi
        fi

        # Uses the default snapshot
        current_snapshot=snap0

        # Stop and restore the LXC container
        LXC_STOP

        # 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"

        # Exit the loop if there's no temporary errors detected.
        if [ $false_positive_error -eq 0 ]
        then
            break
        fi
    done
}

set_witness_files () {
	# Create files to check if the remove script does not remove them accidentally
	echo "Create witness files..." | tee --append "$test_result"

	lxc_dir="/var/lib/lxc/$lxc_name/rootfs"

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

	# Nginx conf
	create_witness_file "/etc/nginx/conf.d/$main_domain.d/witnessfile.conf" file
	create_witness_file "/etc/nginx/conf.d/$sub_domain.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_dir}/etc/php5/fpm" ]; then
		create_witness_file "/etc/php5/fpm/pool.d/witnessfile.conf" file
	fi
	if [ -d "${lxc_dir}/etc/php/7.0/fpm" ]; then
		create_witness_file "/etc/php/7.0/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
	sudo lxc-attach --name=$lxc_name -- mysqladmin --user=root --password=$(sudo cat "$lxc_dir/etc/yunohost/mysql") --wait status > /dev/null 2>&1
	sudo lxc-attach --name=$lxc_name -- mysql --user=root --password=$(sudo cat "$lxc_dir/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

	lxc_dir="/var/lib/lxc/$lxc_name/rootfs"

	check_file_exist () {
		if sudo test ! -e "${lxc_dir}${1}"
		then
			ECHO_FORMAT "The file $1 is missing ! Something gone wrong !\n" "red" "bold"
			RESULT_witness=1
		fi
	}

	# Nginx conf
	check_file_exist "/etc/nginx/conf.d/$main_domain.d/witnessfile.conf"
	check_file_exist "/etc/nginx/conf.d/$sub_domain.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_dir}/etc/php5/fpm" ]; then
		check_file_exist "/etc/php5/fpm/pool.d/witnessfile.conf" file
	fi
	if [ -d "${lxc_dir}/etc/php/7.0/fpm" ]; then
		check_file_exist "/etc/php/7.0/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 ! sudo lxc-attach --name=$lxc_name -- mysqlshow --user=root --password=$(sudo cat "$lxc_dir/etc/yunohost/mysql") | grep --quiet '^| witnessdb' > /dev/null 2>&1
	then
		ECHO_FORMAT "The database witnessdb is missing ! Something gone wrong !\n" "red" "bold"
		RESULT_witness=1
	fi
	if [ $RESULT_witness -eq 1 ]
	then
		yunohost_result=1
		yunohost_remove=1
	fi
}

TESTING_PROCESS () {
	# Launch all tests successively

	ECHO_FORMAT "\nTests serie: ${tests_serie#;; }\n\n" "white" "underlined" clog

	PRINT_YUNOHOST_VERSION

	# Init the value for the current test
	cur_test=1

	# By default, all tests will try to access the app with curl
	use_curl=1

	# Check the package with package linter
	if [ $pkg_linter -eq 1 ]; then
		PACKAGE_LINTER
	fi

	CHECK_TEMPLATE

	# Try to install in a sub path
	if [ $setup_sub_dir -eq 1 ]; then
		TEST_LAUNCHER CHECK_SETUP subdir
	fi

	# Try to install on root
	if [ $setup_root -eq 1 ]; then
		TEST_LAUNCHER CHECK_SETUP root
	fi

	# Try to install without url access
	if [ $setup_nourl -eq 1 ]; then
		TEST_LAUNCHER CHECK_SETUP no_url
	fi

	# Try the upgrade script
	if [ $upgrade -eq 1 ]; then
		TEST_LAUNCHER CHECK_UPGRADE
	fi

	# Try to install in private mode
	if [ $setup_private -eq 1 ]; then
		TEST_LAUNCHER CHECK_PUBLIC_PRIVATE private
	fi

	# Try to install in public mode
	if [ $setup_public -eq 1 ]; then
		TEST_LAUNCHER CHECK_PUBLIC_PRIVATE public
	fi

	# Try multi-instance installations
	if [ $multi_instance -eq 1 ]; then
		TEST_LAUNCHER CHECK_MULTI_INSTANCE
	fi

	# Try to install with an malformed path
	if [ $incorrect_path -eq 1 ]; then
		TEST_LAUNCHER CHECK_COMMON_ERROR incorrect_path
	fi

	# Try to install with a port already used
	if [ $port_already_use -eq 1 ]; then
		TEST_LAUNCHER CHECK_COMMON_ERROR port_already_use
	fi

	# Try to backup then restore the app
	if [ $backup_restore -eq 1 ]; then
		TEST_LAUNCHER CHECK_BACKUP_RESTORE
	fi

	# Try the change_url script
	if [ $change_url -eq 1 ]; then
		TEST_LAUNCHER CHECK_CHANGE_URL
	fi

	# Try the actions
	if [ $actions -eq 1 ]; then
		TEST_LAUNCHER ACTIONS_CONFIG_PANEL actions
	fi

	# Try the config-panel
	if [ $config_panel -eq 1 ]; then
		TEST_LAUNCHER ACTIONS_CONFIG_PANEL config_panel
	fi
}