1#!/usr/bin/env bash
2set -euo pipefail
3
4SMACK_VERSION="4.4.4"
5GRADLE_VERSION="6.3"
6FORCE_CUSTOM_GRADLE=true
7CURL_ARGS="--location --silent"
8DEBUG=false
9LOCAL_RUN=false
10
11HOST='example.org'
12ADMINUSER='admin'
13ADMINPASS='admin'
14
15usage()
16{
17	echo "Usage: $0 [-d] [-l] [-g] [-i IPADDRESS] [-s SMACK_VERSION] [-h HOST] [-u USERNAME] [-p PASSWORD]"
18	echo "    -d: Enable debug mode. Prints commands, and preserves temp directories if used (default: off)"
19	echo "    -l: Launch a local Openfire. (default: off)"
20	echo "    -i: Set a hosts file for the given IP and host (or for example.com if running locally). Reverted at exit."
21	echo "    -g: Use system Gradle, rather than use the known-good (default: off)"
22	echo "    -s: Set Smack to the given version (default: ${SMACK_VERSION})"
23	echo "    -h: The hostname for the Openfire under test (default: example.org)"
24	echo "    -u: Admin username for Openfire (default: admin)"
25	echo "    -p: Admin password for Openfire (default: admin)"
26	exit 2
27}
28
29while getopts dlgs:h:i:u:p: OPTION "$@"; do
30	case $OPTION in
31		d)
32			DEBUG=true
33			set -x
34			;;
35		l)
36			LOCAL_RUN=true
37			;;
38		g)
39			FORCE_CUSTOM_GRADLE=false
40			;;
41		s)
42			SMACK_VERSION="${OPTARG}"
43			;;
44		h)
45			HOST="${OPTARG}"
46			;;
47		i)
48			IPADDRESS="${OPTARG}"
49			;;
50		u)
51			ADMINUSER="${OPTARG}"
52			;;
53		p)
54			ADMINPASS="${OPTARG}"
55			;;
56		\? ) usage;;
57        :  ) usage;;
58        *  ) usage;;
59	esac
60done
61
62if [[ $LOCAL_RUN == true ]] && [[ $HOST != "example.org" ]]; then
63	echo "Host is fixed if launching a local instance. If you have an already-running instance to test against, omit the -l flag (and provide -i 127.0.0.1 if necessary)."
64	exit 1
65fi
66
67function setBaseDirectory {
68	# Pretty fancy method to get reliable the absolute path of a shell
69	# script, *even if it is sourced*. Credits go to GreenFox on
70	# stackoverflow: http://stackoverflow.com/a/12197518/194894
71	pushd . > /dev/null
72	SCRIPTDIR="${BASH_SOURCE[0]}";
73	while [ -h "${SCRIPTDIR}" ]; do
74		cd "$(dirname "${SCRIPTDIR}")"
75		SCRIPTDIR="$(readlink "$(basename "${SCRIPTDIR}")")";
76	done
77	cd "$(dirname "${SCRIPTDIR}")" > /dev/null
78	SCRIPTDIR="$(pwd)";
79	popd  > /dev/null
80	BASEDIR="${SCRIPTDIR}"
81	cd "${BASEDIR}"
82}
83
84function createTempDirectory {
85	OFTEMPDIR=$(mktemp -d)
86}
87
88function cleanup {
89	if [[ $DEBUG == false && -n "${OFTEMPDIR-}" ]]; then
90		echo "Removing temp directories"
91		rm -rf "${OFTEMPDIR}"
92	fi
93	if [[ -n "${IPADDRESS-}" ]]; then
94		echo "Resetting hosts file after local running. This may prompt for sudo."
95		sudo sed -i'.original' "/$IPADDRESS $HOST/d" /etc/hosts
96	fi
97	if [[ $LOCAL_RUN == true ]]; then
98		echo "Stopping Openfire"
99		pkill -f openfire.lib #TODO: Can this be made more future-proof against changes in the start script?
100	fi
101}
102
103function setUpGradleEnvironment {
104
105	if command -v gradle &> /dev/null; then
106		GRADLE_IN_PATH=true
107	else
108		GRADLE_IN_PATH=false
109	fi
110
111	if [[ $GRADLE_IN_PATH == false ]] || $FORCE_CUSTOM_GRADLE; then
112		createTempDirectory
113		GRADLEDIR="${OFTEMPDIR}/gradle"
114		mkdir "${GRADLEDIR}"
115		pushd "${GRADLEDIR}"
116		declare -r GRADLE_ZIP="gradle-${GRADLE_VERSION}-bin.zip"
117		curl ${CURL_ARGS} --output ${GRADLE_ZIP} "https://services.gradle.org/distributions/${GRADLE_ZIP}"
118		unzip "${GRADLE_ZIP}"
119		GRADLE="${GRADLEDIR}/gradle-${GRADLE_VERSION}/bin/gradle"
120		popd
121	else
122		GRADLE="gradle"
123	fi
124}
125
126function setHostsFile {
127	if [[ -n "${IPADDRESS-}" ]]; then
128		echo "Setting hosts file for local running. This may prompt for sudo."
129		sudo /bin/sh -c "echo \"$IPADDRESS $HOST\" >> /etc/hosts"
130	fi
131}
132
133function launchOpenfire {
134	declare -r OPENFIRE_SHELL_SCRIPT="${BASEDIR}/distribution/target/distribution-base/bin/openfire.sh"
135
136	if [[ ! -f "${OPENFIRE_SHELL_SCRIPT}" ]]; then
137		mvn verify -P ci
138	fi
139
140	rm -f distribution/target/distribution-base/conf/openfire.xml
141	cp distribution/target/distribution-base/conf/openfire-demoboot.xml \
142		distribution/target/distribution-base/conf/openfire.xml
143
144	echo "Starting Openfire…"
145	"${OPENFIRE_SHELL_SCRIPT}" &
146
147	# Wait 120 seconds for Openfire to open up the web interface and
148	# assume Openfire is fully operational once that happens (not sure if
149	# this assumption is correct).
150	echo "Waiting for Openfire…"
151	timeout 120 bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/$0/$1; do sleep 0.3; done' localhost 7070
152}
153
154function runTestsInGradle {
155	echo "Starting Integration Tests (using Smack ${SMACK_VERSION})…"
156
157	DISABLED_INTEGRATION_TESTS=()
158
159	# The three tests below suffer from a race condition, making them flap. See https://github.com/igniterealtime/Smack/pull/440
160    DISABLED_INTEGRATION_TESTS+=(MoodIntegrationTest)
161    DISABLED_INTEGRATION_TESTS+=(UserTuneIntegrationTest)
162    DISABLED_INTEGRATION_TESTS+=(GeolocationIntegrationTest)
163
164    # Occasionally fail because of unstable AbstractSmackIntegrationTest#performActionAndWaitForPresence being used.
165    # See https://github.com/igniterealtime/Smack/commit/5bfe789e08ebb251b3c4302cb653c715eee363ea for unstable solution.
166    # Disable until AbstractSmackIntegrationTest#performActionAndWaitForPresence is improved/replaced.
167	DISABLED_INTEGRATION_TESTS+=(EntityCapsTest)
168	DISABLED_INTEGRATION_TESTS+=(SoftwareInfoIntegrationTest)
169
170    # Occasionally fails, disabled until cause can be found.
171	DISABLED_INTEGRATION_TESTS+=(XmppConnectionIntegrationTest)
172	DISABLED_INTEGRATION_TESTS+=(StreamManagementTest)
173	DISABLED_INTEGRATION_TESTS+=(WaitForClosingStreamElementTest)
174	DISABLED_INTEGRATION_TESTS+=(IoTControlIntegrationTest)
175	DISABLED_INTEGRATION_TESTS+=(ModularXmppClientToServerConnectionLowLevelIntegrationTest)
176
177	SINTTEST_DISABLED_TESTS_ARGUMENT="-Dsinttest.disabledTests="
178	for disabledTest in "${DISABLED_INTEGRATION_TESTS[@]}"; do
179		SINTTEST_DISABLED_TESTS_ARGUMENT+="${disabledTest},"
180	done
181	# Remove last ',' from the argument. Can't use ${SINTTEST_DISABLED_TESTS_ARGUMENT::-1} because bash on MacOS is infuriatingly incompatible.
182	SINTTEST_DISABLED_TESTS_ARGUMENT="${SINTTEST_DISABLED_TESTS_ARGUMENT:0:$((${#SINTTEST_DISABLED_TESTS_ARGUMENT}-1))}"
183
184	$GRADLE --console=plain \
185			--build-file test.gradle \
186			-PsmackVersion="${SMACK_VERSION}" \
187			-q dependencies
188	$GRADLE --console=plain \
189			--stacktrace \
190			run \
191			-b test.gradle \
192			-PsmackVersion="${SMACK_VERSION}" \
193			-Dsinttest.service="${HOST}" \
194			-Dsinttest.securityMode=disabled \
195			-Dsinttest.replyTimeout=60000 \
196			-Dsinttest.adminAccountUsername="${ADMINUSER}" \
197			-Dsinttest.adminAccountPassword="${ADMINPASS}" \
198			-Dsinttest.enabledConnections=tcp \
199			-Dsinttest.dnsResolver=javax \
200			${SINTTEST_DISABLED_TESTS_ARGUMENT}
201}
202
203
204setBaseDirectory
205trap cleanup EXIT
206setUpGradleEnvironment
207if [[ -n "${IPADDRESS-}" ]]; then
208	setHostsFile
209fi
210if [[ $LOCAL_RUN == true ]]; then
211	launchOpenfire
212fi
213runTestsInGradle
214