<commit_before>#!/usr/bin/env bash

# osync test suite 2016111601

# 4 tests:
# quicklocal
# quickremote
# conflocal
# confremote

# for each test:
# files with spaces, subdirs
# largefileset (...large ?)
# exclusions
# conflict resolution initiator with backups / multiple backups
# conflict resolution target with backups / multiple backups
# deletion propagation, failed deletion repropagation
# lock checks
# file attribute tests

#TODO: lock checks missing
#TODO: skip deletion tests
#TODO: daemon mode tests
#TODO: check file contents on attribute updates

#TODO: enable teardown after tests

LARGE_FILESET_URL="http://ftp.drupal.org/files/projects/drupal-8.1.9.tar.gz"

OSYNC_DIR="$(pwd)"
OSYNC_DIR=${OSYNC_DIR%%/dev*}
DEV_DIR="$OSYNC_DIR/dev"
TESTS_DIR="$DEV_DIR/tests"

LOCAL_CONF="local.conf"
REMOTE_CONF="remote.conf"
OLD_CONF="old.conf"
TMP_OLD_CONF="tmp.old.conf"

OSYNC_EXECUTABLE="osync.sh"
OSYNC_DEV_EXECUTABLE="dev/n_osync.sh"
OSYNC_UPGRADE="upgrade-v1.0x-v1.2x.sh"
TMP_FILE="$DEV_DIR/tmp"



if [ "$TRAVIS_RUN" == true ]; then
	echo "Running with travis settings"
	CONF_DIR="$TESTS_DIR/conf-travis"
	SSH_PORT=22
else
	echo "Running with local settings"
	CONF_DIR="$TESTS_DIR/conf-local"
	SSH_PORT=49999
fi

OSYNC_TESTS_DIR="${HOME}/osync-tests"
INITIATOR_DIR="$OSYNC_TESTS_DIR/initiator"
TARGET_DIR="$OSYNC_TESTS_DIR/target"
OSYNC_WORKDIR=".osync_workdir"
OSYNC_STATE_DIR="$OSYNC_WORKDIR/state"
OSYNC_DELETE_DIR="$OSYNC_WORKDIR/deleted"
OSYNC_BACKUP_DIR="$OSYNC_WORKDIR/backup"

# Later populated variables
OSYNC_VERSION=1.x.y
OSYNC_MIN_VERSION=x
OSYNC_IS_STABLE=maybe

# Setup an array with all function modes
declare -Ag osyncParameters

osyncParameters[quicklocal]="--initiator=$INITIATOR_DIR --target=$TARGET_DIR --instance-id=quicklocal"
osyncParameters[quickRemote]="--initiator=$INITIATOR_DIR --target=ssh://localhost:$SSH_PORT/$TARGET_DIR --rsakey=${HOME}/.ssh/id_rsa_local --instance-id=quickremote"
osyncParameters[confLocal]="$CONF_DIR/$LOCAL_CONF"
osyncParameters[confRemote]="$CONF_DIR/$REMOTE_CONF"
#osyncParameters[daemonlocal]="$CONF_DIR/$LOCAL_CONF --on-changes"
#osyncParameters[daemonlocal]="$CONF_DIR/$REMOTE_CONF --on-changes"

function GetConfFileValue () {
	local file="${1}"
	local name="${2}"
	local value

	value=$(grep "^$name=" "$file")
	if [ $? == 0 ]; then
		value="${value##*=}"
		echo "$value"
	else
		assertEquals "$name does not exist in [$file." "1" "0"
	fi
}

function SetConfFileValue () {
	local file="${1}"
	local name="${2}"
	local value="${3}"

	if grep "^$name=" "$file" > /dev/null; then
		sed -i.tmp "s/^$name=.*/$name=$value/" "$file"
		assertEquals "Set $name to [$value]." "0" $?
	else
		assertEquals "$name does not exist in [$file]." "1" "0"
	fi
}

function SetupSSH {
	echo -e  'y\n'| ssh-keygen -t rsa -b 2048 -N "" -f "${HOME}/.ssh/id_rsa_local"
	cat "${HOME}/.ssh/id_rsa_local.pub" >> "${HOME}/.ssh/authorized_keys"
	chmod 600 "${HOME}/.ssh/authorized_keys"

	# Add localhost to known hosts so self connect works
	if [ -z $(ssh-keygen -F localhost) ]; then
		ssh-keyscan -H localhost >> ~/.ssh/known_hosts
	fi
}

function DownloadLargeFileSet() {
	local destinationPath="${1}"

	cd "$OSYNC_DIR"
	wget -q "$LARGE_FILESET_URL" > /dev/null
	assertEquals "Download [$LARGE_FILESET_URL]." "0" $?

	tar xvf "$(basename $LARGE_FILESET_URL)" -C "$destinationPath" > /dev/null
	assertEquals "Extract $(basename $LARGE_FILESET_URL)" "0" $?

	rm -f "$(basename $LARGE_FILESET_URL)"
}

function CreateOldFile () {
	local drive
	local filePath="${1}"

	touch "$filePath"
	assertEquals "touch [$filePath]" "0" $?

	# Get current drive
        drive=$(df "$OSYNC_DIR" | tail -1 | awk '{print $1}')

	# modify ctime on ext4 so osync thinks it has to delete the old files
	debugfs -w -R 'set_inode_field "'$filePath'" ctime 201001010101' $drive
	assertEquals "CreateOldFile [$filePath]" "0" $?

	# force update of inodes (ctimes)
	echo 3 > /proc/sys/vm/drop_caches
	assertEquals "Drop caches" "0" $?
}

function PrepareLocalDirs () {
	# Remote dirs are the same as local dirs, so no problem here
	if [ -d "$INITIATOR_DIR" ]; then
		rm -rf "$INITIATOR_DIR"
	fi
	mkdir -p "$INITIATOR_DIR"

	if [ -d "$TARGET_DIR" ]; then
 		rm -rf "$TARGET_DIR"
	fi
	mkdir -p "$TARGET_DIR"
}

function oneTimeSetUp () {
	source "$DEV_DIR/ofunctions.sh"
	SetupSSH

	# Get osync version
	OSYNC_VERSION=$(GetConfFileValue "$OSYNC_DIR/$OSYNC_DEV_EXECUTABLE" "PROGRAM_VERSION")
	OSYNC_VERSION="${OSYNC_VERSION##*=}"
	OSYNC_MIN_VERSION="${OSYNC_VERSION:2:1}"

	OSYNC_IS_STABLE=$(GetConfFileValue "$OSYNC_DIR/$OSYNC_DEV_EXECUTABLE" "IS_STABLE")

	echo "Running with $OSYNC_VERSION ($OSYNC_MIN_VERSION) STABLE=$OSYNC_IS_STABLE"
}

function oneTimeTearDown () {
	# Set osync version stable flag back to origin
	SetConfFileValue "$OSYNC_DIR/$OSYNC_EXECUTABLE" "IS_STABLE" "$OSYNC_IS_STABLE"

	#TODO: uncomment this when dev is done
	#rm -rf "$OSYNC_TESTS_DIR"
}

function setUp () {
        rm -rf "$INITIATOR_DIR"
        rm -rf "$TARGET_DIR"
}

# This test has to be done everytime in order for osync executable to be fresh
function test_Merge () {
	cd "$DEV_DIR"
	./merge.sh
	assertEquals "Merging code" "0" $?

	# Set osync version to stable while testing to avoid warning message
	SetConfFileValue "$OSYNC_DIR/$OSYNC_EXECUTABLE" "IS_STABLE" "yes"
}

function test_LargeFileSet () {
	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		DownloadLargeFileSet "$INITIATOR_DIR"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "LargeFileSet test with parameters [$i]." "0" $?

		[ -d "$INITIATOR_DIR/$OSYNC_STATE_DIR" ]
		assertEquals "Initiator state dir exists" "0" $?

		[ -d "$TARGET_DIR/$OSYNC_STATE_DIR" ]
		assertEquals "Target state dir exists" "0" $?
	done
}

function test_Exclusions () {
	# Will sync except php files
	# RSYNC_EXCLUDE_PATTERN="*.php" is set at runtime for quicksync and in config files for other runs

	local numberOfPHPFiles
	local numberOfExcludedFiles
	local numberOfInitiatorFiles
	local numberOfTargetFiles

	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		DownloadLargeFileSet "$INITIATOR_DIR"

		numberOfPHPFiles=$(find "$INITIATOR_DIR" ! -wholename "$INITIATOR_DIR/$OSYNC_WORKDIR*" -name "*.php" | wc -l)

		REMOTE_HOST_PING=no RSYNC_EXCLUDE_PATTERN="*.php" ./$OSYNC_EXECUTABLE $i
		assertEquals "Exclusions with parameters [$i]." "0" $?

		#WIP Add exclusion from file tests here
		numberOfInitiatorFiles=$(find "$INITIATOR_DIR" ! -wholename "$INITIATOR_DIR/$OSYNC_WORKDIR*" | wc -l)
		numberOfTargetFiles=$(find "$TARGET_DIR" ! -wholename "$TARGET_DIR/$OSYNC_WORKDIR*" | wc -l)
		numberOfExcludedFiles=$((numberOfInitiatorFiles-numberOfTargetFiles))

		assertEquals "Number of php files: $numberOfPHPFiles - Number of excluded files: $numberOfExcludedFiles" $numberOfPHPFiles $numberOfExcludedFiles
	done
}

function test_Deletetion () {
	local iFile1="$INITIATOR_DIR/ific"
	local iFile2="$INITIATOR_DIR/ifoc"
	local tFile1="$TARGET_DIR/tfic"
	local tFile2="$TARGET_DIR/tfoc"


	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		touch "$iFile1"
		touch "$iFile2"
		touch "$tFile1"
		touch "$tFile2"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "First deletion run with parameters [$i]." "0" $?

		rm -f "$iFile1"
		rm -f "$tFile1"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Second deletion run with parameters [$i]." "0" $?

		[ -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$(basename $iFile1)" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$(basename $iFile1)] has been soft deleted on target" "0" $?
		[ -f "$iFile1" ]
		assertEquals "File [$iFile1] is still in initiator" "1" $?

		# The variable substitution is not the best comprehensible code
		[ -f "${iFile1/$INITIATOR_DIR/TARGET_DIR}" ]
		assertEquals "File [${iFile1/$INITIATOR_DIR/TARGET_DIR}] is still in target" "1" $?

		[ -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$(basename $tFile1)" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$(basename $tFile1)] has been soft deleted on initiator" "0" $?
		[ -f "$tFile1" ]
		assertEquals "File [$tFile1] is still in target" "1" $?

		[ -f "${tFile1/$TARGET_DIR/INITIATOR_DIR}" ]
		assertEquals "File [${tFile1/$TARGET_DIR/INITIATOR_DIR}] is still in initiator" "1" $?
	done
}

function test_deletion_failure () {

	if [ "$TRAVIS_RUN" == true ]; then
		echo "Skipping deletionFailure tests as travis does not support chattr."
		return 0
	fi

	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs

		DirA="some directory with spaces"
		DirB="another directoy/and sub directory"

		mkdir -p "$INITIATOR_DIR/$DirA"
		mkdir -p "$TARGET_DIR/$DirB"

		FileA="$DirA/File A"
		FileB="$DirB/File B"

		touch "$INITIATOR_DIR/$FileA"
		touch "$TARGET_DIR/$FileB"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "First deletion run with parameters [$i]." "0" $?

		rm -f "$INITIATOR_DIR/$FileA"
		rm -f "$TARGET_DIR/$FileB"

		# Prevent files from being deleted
		chattr +i "$TARGET_DIR/$FileA"
		chattr +i "$INITIATOR_DIR/$FileB"

		# This shuold fail with exitcode 1
		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Second deletion run with parameters [$i]." "1" $?

		[ -f "$TARGET_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$FileA] is still present in deletion dir." "0" $?
		[ ! -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA] is not present in deletion dir." "0" $?

		[ -f "$INITIATOR_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$FileB] is still present in deletion dir." "0" $?
		[ ! -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB] is not present in deletion dir." "0" $?

		# Allow files from being deleted
		chattr -i "$TARGET_DIR/$FileA"
		chattr -i "$INITIATOR_DIR/$FileB"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Third deletion run with parameters [$i]." "0" $?

		[ ! -f "$TARGET_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$FileA] is still present in deletion dir." "0" $?
		[ -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA] is not present in deletion dir." "0" $?

		[ ! -f "$INITIATOR_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$FileB] is still present in deletion dir." "0" $?
		[ -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB] is not present in deletion dir." "0" $?
	done
}

function test_skip_deletion () {
	echo "Not implemented yet"
}<commit_msg>Added deletion skip tests<commit_after>#!/usr/bin/env bash

# osync test suite 2016111602

# 4 tests:
# quicklocal
# quickremote
# conflocal
# confremote

# for each test:
# files with spaces, subdirs
# largefileset (...large ?)
# exclusions
# conflict resolution initiator with backups / multiple backups
# conflict resolution target with backups / multiple backups
# deletion propagation, failed deletion repropagation
# lock checks
# file attribute tests

#TODO: lock checks missing
#TODO: daemon mode tests

#TODO: enable teardown after tests

LARGE_FILESET_URL="http://ftp.drupal.org/files/projects/drupal-8.1.9.tar.gz"

OSYNC_DIR="$(pwd)"
OSYNC_DIR=${OSYNC_DIR%%/dev*}
DEV_DIR="$OSYNC_DIR/dev"
TESTS_DIR="$DEV_DIR/tests"

LOCAL_CONF="local.conf"
REMOTE_CONF="remote.conf"
OLD_CONF="old.conf"
TMP_OLD_CONF="tmp.old.conf"

OSYNC_EXECUTABLE="osync.sh"
OSYNC_DEV_EXECUTABLE="dev/n_osync.sh"
OSYNC_UPGRADE="upgrade-v1.0x-v1.2x.sh"
TMP_FILE="$DEV_DIR/tmp"



if [ "$TRAVIS_RUN" == true ]; then
	echo "Running with travis settings"
	CONF_DIR="$TESTS_DIR/conf-travis"
	SSH_PORT=22
else
	echo "Running with local settings"
	CONF_DIR="$TESTS_DIR/conf-local"
	SSH_PORT=49999
fi

OSYNC_TESTS_DIR="${HOME}/osync-tests"
INITIATOR_DIR="$OSYNC_TESTS_DIR/initiator"
TARGET_DIR="$OSYNC_TESTS_DIR/target"
OSYNC_WORKDIR=".osync_workdir"
OSYNC_STATE_DIR="$OSYNC_WORKDIR/state"
OSYNC_DELETE_DIR="$OSYNC_WORKDIR/deleted"
OSYNC_BACKUP_DIR="$OSYNC_WORKDIR/backup"

# Later populated variables
OSYNC_VERSION=1.x.y
OSYNC_MIN_VERSION=x
OSYNC_IS_STABLE=maybe

# Setup an array with all function modes
declare -Ag osyncParameters

osyncParameters[quicklocal]="--initiator=$INITIATOR_DIR --target=$TARGET_DIR --instance-id=quicklocal"
osyncParameters[quickRemote]="--initiator=$INITIATOR_DIR --target=ssh://localhost:$SSH_PORT/$TARGET_DIR --rsakey=${HOME}/.ssh/id_rsa_local --instance-id=quickremote"
osyncParameters[confLocal]="$CONF_DIR/$LOCAL_CONF"
osyncParameters[confRemote]="$CONF_DIR/$REMOTE_CONF"
#osyncParameters[daemonlocal]="$CONF_DIR/$LOCAL_CONF --on-changes"
#osyncParameters[daemonlocal]="$CONF_DIR/$REMOTE_CONF --on-changes"

function GetConfFileValue () {
	local file="${1}"
	local name="${2}"
	local value

	value=$(grep "^$name=" "$file")
	if [ $? == 0 ]; then
		value="${value##*=}"
		echo "$value"
	else
		assertEquals "$name does not exist in [$file." "1" "0"
	fi
}

function SetConfFileValue () {
	local file="${1}"
	local name="${2}"
	local value="${3}"

	if grep "^$name=" "$file" > /dev/null; then
		sed -i.tmp "s/^$name=.*/$name=$value/" "$file"
		assertEquals "Set $name to [$value]." "0" $?
	else
		assertEquals "$name does not exist in [$file]." "1" "0"
	fi
}

function SetupSSH {
	echo -e  'y\n'| ssh-keygen -t rsa -b 2048 -N "" -f "${HOME}/.ssh/id_rsa_local"
	cat "${HOME}/.ssh/id_rsa_local.pub" >> "${HOME}/.ssh/authorized_keys"
	chmod 600 "${HOME}/.ssh/authorized_keys"

	# Add localhost to known hosts so self connect works
	if [ -z $(ssh-keygen -F localhost) ]; then
		ssh-keyscan -H localhost >> ~/.ssh/known_hosts
	fi
}

function DownloadLargeFileSet() {
	local destinationPath="${1}"

	cd "$OSYNC_DIR"
	wget -q "$LARGE_FILESET_URL" > /dev/null
	assertEquals "Download [$LARGE_FILESET_URL]." "0" $?

	tar xvf "$(basename $LARGE_FILESET_URL)" -C "$destinationPath" > /dev/null
	assertEquals "Extract $(basename $LARGE_FILESET_URL)" "0" $?

	rm -f "$(basename $LARGE_FILESET_URL)"
}

function CreateOldFile () {
	local drive
	local filePath="${1}"

	touch "$filePath"
	assertEquals "touch [$filePath]" "0" $?

	# Get current drive
        drive=$(df "$OSYNC_DIR" | tail -1 | awk '{print $1}')

	# modify ctime on ext4 so osync thinks it has to delete the old files
	debugfs -w -R 'set_inode_field "'$filePath'" ctime 201001010101' $drive
	assertEquals "CreateOldFile [$filePath]" "0" $?

	# force update of inodes (ctimes)
	echo 3 > /proc/sys/vm/drop_caches
	assertEquals "Drop caches" "0" $?
}

function PrepareLocalDirs () {
	# Remote dirs are the same as local dirs, so no problem here
	if [ -d "$INITIATOR_DIR" ]; then
		rm -rf "$INITIATOR_DIR"
	fi
	mkdir -p "$INITIATOR_DIR"

	if [ -d "$TARGET_DIR" ]; then
 		rm -rf "$TARGET_DIR"
	fi
	mkdir -p "$TARGET_DIR"
}

function oneTimeSetUp () {
	source "$DEV_DIR/ofunctions.sh"
	SetupSSH

	# Get osync version
	OSYNC_VERSION=$(GetConfFileValue "$OSYNC_DIR/$OSYNC_DEV_EXECUTABLE" "PROGRAM_VERSION")
	OSYNC_VERSION="${OSYNC_VERSION##*=}"
	OSYNC_MIN_VERSION="${OSYNC_VERSION:2:1}"

	OSYNC_IS_STABLE=$(GetConfFileValue "$OSYNC_DIR/$OSYNC_DEV_EXECUTABLE" "IS_STABLE")

	echo "Running with $OSYNC_VERSION ($OSYNC_MIN_VERSION) STABLE=$OSYNC_IS_STABLE"
}

function oneTimeTearDown () {
	# Set osync version stable flag back to origin
	SetConfFileValue "$OSYNC_DIR/$OSYNC_EXECUTABLE" "IS_STABLE" "$OSYNC_IS_STABLE"

	#TODO: uncomment this when dev is done
	#rm -rf "$OSYNC_TESTS_DIR"
}

function setUp () {
        rm -rf "$INITIATOR_DIR"
        rm -rf "$TARGET_DIR"
}

# This test has to be done everytime in order for osync executable to be fresh
function test_Merge () {
	cd "$DEV_DIR"
	./merge.sh
	assertEquals "Merging code" "0" $?

	# Set osync version to stable while testing to avoid warning message
	SetConfFileValue "$OSYNC_DIR/$OSYNC_EXECUTABLE" "IS_STABLE" "yes"
}

function test_LargeFileSet () {
	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		DownloadLargeFileSet "$INITIATOR_DIR"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "LargeFileSet test with parameters [$i]." "0" $?

		[ -d "$INITIATOR_DIR/$OSYNC_STATE_DIR" ]
		assertEquals "Initiator state dir exists" "0" $?

		[ -d "$TARGET_DIR/$OSYNC_STATE_DIR" ]
		assertEquals "Target state dir exists" "0" $?
	done
}

function test_Exclusions () {
	# Will sync except php files
	# RSYNC_EXCLUDE_PATTERN="*.php" is set at runtime for quicksync and in config files for other runs

	local numberOfPHPFiles
	local numberOfExcludedFiles
	local numberOfInitiatorFiles
	local numberOfTargetFiles

	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		DownloadLargeFileSet "$INITIATOR_DIR"

		numberOfPHPFiles=$(find "$INITIATOR_DIR" ! -wholename "$INITIATOR_DIR/$OSYNC_WORKDIR*" -name "*.php" | wc -l)

		REMOTE_HOST_PING=no RSYNC_EXCLUDE_PATTERN="*.php" ./$OSYNC_EXECUTABLE $i
		assertEquals "Exclusions with parameters [$i]." "0" $?

		#WIP Add exclusion from file tests here
		numberOfInitiatorFiles=$(find "$INITIATOR_DIR" ! -wholename "$INITIATOR_DIR/$OSYNC_WORKDIR*" | wc -l)
		numberOfTargetFiles=$(find "$TARGET_DIR" ! -wholename "$TARGET_DIR/$OSYNC_WORKDIR*" | wc -l)
		numberOfExcludedFiles=$((numberOfInitiatorFiles-numberOfTargetFiles))

		assertEquals "Number of php files: $numberOfPHPFiles - Number of excluded files: $numberOfExcludedFiles" $numberOfPHPFiles $numberOfExcludedFiles
	done
}

function test_Deletetion () {
	local iFile1="$INITIATOR_DIR/ific"
	local iFile2="$INITIATOR_DIR/ifoc"
	local tFile1="$TARGET_DIR/tfic"
	local tFile2="$TARGET_DIR/tfoc"


	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs
		touch "$iFile1"
		touch "$iFile2"
		touch "$tFile1"
		touch "$tFile2"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "First deletion run with parameters [$i]." "0" $?

		rm -f "$iFile1"
		rm -f "$tFile1"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Second deletion run with parameters [$i]." "0" $?

		[ -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$(basename $iFile1)" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$(basename $iFile1)] has been soft deleted on target" "0" $?
		[ -f "$iFile1" ]
		assertEquals "File [$iFile1] is still in initiator" "1" $?

		# The variable substitution is not the best comprehensible code
		[ -f "${iFile1/$INITIATOR_DIR/TARGET_DIR}" ]
		assertEquals "File [${iFile1/$INITIATOR_DIR/TARGET_DIR}] is still in target" "1" $?

		[ -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$(basename $tFile1)" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$(basename $tFile1)] has been soft deleted on initiator" "0" $?
		[ -f "$tFile1" ]
		assertEquals "File [$tFile1] is still in target" "1" $?

		[ -f "${tFile1/$TARGET_DIR/INITIATOR_DIR}" ]
		assertEquals "File [${tFile1/$TARGET_DIR/INITIATOR_DIR}] is still in initiator" "1" $?
	done
}

function test_deletion_failure () {

	if [ "$TRAVIS_RUN" == true ]; then
		echo "Skipping deletionFailure tests as travis does not support chattr."
		return 0
	fi

	for i in "${osyncParameters[@]}"; do
		cd "$OSYNC_DIR"

		PrepareLocalDirs

		DirA="some directory with spaces"
		DirB="another directoy/and sub directory"

		mkdir -p "$INITIATOR_DIR/$DirA"
		mkdir -p "$TARGET_DIR/$DirB"

		FileA="$DirA/File A"
		FileB="$DirB/File B"

		touch "$INITIATOR_DIR/$FileA"
		touch "$TARGET_DIR/$FileB"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "First deletion run with parameters [$i]." "0" $?

		rm -f "$INITIATOR_DIR/$FileA"
		rm -f "$TARGET_DIR/$FileB"

		# Prevent files from being deleted
		chattr +i "$TARGET_DIR/$FileA"
		chattr +i "$INITIATOR_DIR/$FileB"

		# This shuold fail with exitcode 1
		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Second deletion run with parameters [$i]." "1" $?

		[ -f "$TARGET_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$FileA] is still present in deletion dir." "0" $?
		[ ! -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA] is not present in deletion dir." "0" $?

		[ -f "$INITIATOR_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$FileB] is still present in deletion dir." "0" $?
		[ ! -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB] is not present in deletion dir." "0" $?

		# Allow files from being deleted
		chattr -i "$TARGET_DIR/$FileA"
		chattr -i "$INITIATOR_DIR/$FileB"

		REMOTE_HOST_PING=no ./$OSYNC_EXECUTABLE $i
		assertEquals "Third deletion run with parameters [$i]." "0" $?

		[ ! -f "$TARGET_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$FileA] is still present in deletion dir." "0" $?
		[ -f "$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA" ]
		assertEquals "File [$TARGET_DIR/$OSYNC_DELETE_DIR/$FileA] is not present in deletion dir." "0" $?

		[ ! -f "$INITIATOR_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$FileB] is still present in deletion dir." "0" $?
		[ -f "$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB" ]
		assertEquals "File [$INITIATOR_DIR/$OSYNC_DELETE_DIR/$FileB] is not present in deletion dir." "0" $?
	done
}

function test_skip_deletion () {
	local skipDeletionLocal
	local skipDeletionRemote
	local modes

	if [ "$OSYNC_MIN_VERSION" == "1" ]; then
		echo "Skipping SkipDeletion test because it wasn't implemented in osync v1.1."
		return 0
	fi

	# Keep original values
	skipDeletionLocal=$(GetConfFileValue "$CONF_DIR/$LOCAL_CONF" "SKIP_DELETION")
	skipDeletionRemote=$(GetConfFileValue "$CONF_DIR/$REMOTE_CONF" "SKIP_DELETION")

	modes=('initiator' 'target' 'initiator,target')

	for mode in "${modes[@]}"; do

		SetConfFileValue "$CONF_DIR/$LOCAL_CONF" "SKIP_DELETION" "$mode"
		SetConfFileValue "$CONF_DIR/$REMOTE_CONF" "SKIP_DELETION" "$mode"

		for i in "${osyncParameters[@]}"; do
			cd "$OSYNC_DIR"
			PrepareLocalDirs

			DirA="another/one/bites/ de_dust"
			DirB="phantom of /the opera"

			mkdir -p "$INITIATOR_DIR/$DirA"
			mkdir -p "$TARGET_DIR/$DirB"

			FileA="$DirA/<NAME>"
			FileB="$DirB/<NAME>"

			touch "$INITIATOR_DIR/$FileA"
			touch "$TARGET_DIR/$FileB"

			# First run
			REMOTE_HOST_PING=no SKIP_DELETION="$mode" ./$OSYNC_EXECUTABLE $i
			assertEquals "First deletion run with parameters [$i]." "0" $?

			rm -f "$INITIATOR_DIR/$FileA"
			rm -f "$TARGET_DIR/$FileB"

			# Second run
			REMOTE_HOST_PING=no SKIP_DELETION="$mode" ./$OSYNC_EXECUTABLE $i
			assertEquals "First deletion run with parameters [$i]." "0" $?

			if [ "$mode" == "initiator" ]; then
				[ -f "$TARGET_DIR/$FileA" ]
				assertEquals "File [$TARGET_DIR/$FileA] still exists in mode $mode." "1" $?
				[ -f "$INITIATOR_DIR/$FileB" ]
				assertEquals "File [$INITIATOR_DIR/$FileB still exists in mode $mode." "0" $?

			elif [ "$mode" == "target" ]; then
				[ -f "$TARGET_DIR/$FileA" ]
				assertEquals "File [$TARGET_DIR/$FileA] still exists in mode $mode." "0" $?
				[ -f "$INITIATOR_DIR/$FileB" ]
				assertEquals "File [$INITIATOR_DIR/$FileB still exists in mode $mode." "1" $?


			elif [ "$mode" == "initiator,target" ]; then
				[ -f "$TARGET_DIR/$FileA" ]
				assertEquals "File [$TARGET_DIR/$FileA] still exists in mode $mode." "0" $?
				[ -f "$INITIATOR_DIR/$FileB" ]
				assertEquals "File [$INITIATOR_DIR/$FileB still exists in mode $mode." "0" $?
			else
				assertEquals "Bogus skip deletion mode" "0" "1"
			fi
		done
	done

	# Set original values back
	SetConfFileValue "$CONF_DIR/$LOCAL_CONF" "SKIP_DELETION" "$skipDeletionLocal"
	SetConfFileValue "$CONF_DIR/$REMOTE_CONF" "SKIP_DELETION" "$skipDeletionRemote"
}
{'fraction_non_alphanumeric': 0.14430617115581315, 'fraction_numerical': 0.010569382884418685, 'mean_word_length': 4.809606338202525, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '475850', 'n_tokens_mistral': 9430, 'n_tokens_neox': 8650, 'n_words': 2080}