run.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #!/usr/bin/env bash
  2. # @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
  3. #
  4. # @license GNU AGPL version 3 or any later version
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as
  8. # published by the Free Software Foundation, either version 3 of the
  9. # License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. # Helper script to run the acceptance tests, which test a running Nextcloud
  19. # instance from the point of view of a real user.
  20. #
  21. # The acceptance tests are run in its own Docker container; the grandparent
  22. # directory of the acceptance tests directory (that is, the root directory of
  23. # the Nextcloud server) is copied to the container and the acceptance tests are
  24. # run inside it. Once the tests end the container is stopped. The acceptance
  25. # tests also use the Selenium server to control a web browser, so the Selenium
  26. # server is also launched before the tests start in its own Docker container (it
  27. # will be stopped automatically too once the tests end).
  28. #
  29. # To perform its job, the script requires the "docker" command to be available.
  30. #
  31. # The Docker Command Line Interface (the "docker" command) requires special
  32. # permissions to talk to the Docker daemon, and those permissions are typically
  33. # available only to the root user. Please see the Docker documentation to find
  34. # out how to give access to a regular user to the Docker daemon:
  35. # https://docs.docker.com/engine/installation/linux/linux-postinstall/
  36. #
  37. # Note, however, that being able to communicate with the Docker daemon is the
  38. # same as being able to get root privileges for the system. Therefore, you must
  39. # give access to the Docker daemon (and thus run this script as) ONLY to trusted
  40. # and secure users:
  41. # https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
  42. #
  43. # Finally, take into account that this script will automatically remove the
  44. # Docker containers named "selenium-nextcloud-local-test-acceptance" and
  45. # "nextcloud-local-test-acceptance", even if the script did not create them
  46. # (probably you will not have containers nor images with those names, but just
  47. # in case).
  48. # Sets the variables that abstract the differences in command names and options
  49. # between operating systems.
  50. #
  51. # Switches between timeout on GNU/Linux and gtimeout on macOS (same for mktemp
  52. # and gmktemp).
  53. function setOperatingSystemAbstractionVariables() {
  54. case "$OSTYPE" in
  55. darwin*)
  56. if [ "$(which gtimeout)" == "" ]; then
  57. echo "Please install coreutils (brew install coreutils)"
  58. exit 1
  59. fi
  60. MKTEMP=gmktemp
  61. TIMEOUT=gtimeout
  62. DOCKER_OPTIONS="-e no_proxy=localhost "
  63. ;;
  64. linux*)
  65. MKTEMP=mktemp
  66. TIMEOUT=timeout
  67. DOCKER_OPTIONS=" "
  68. ;;
  69. *)
  70. echo "Operating system ($OSTYPE) not supported"
  71. exit 1
  72. ;;
  73. esac
  74. }
  75. # Launches the Selenium server in a Docker container.
  76. #
  77. # The acceptance tests use Firefox by default but, unfortunately, Firefox >= 48
  78. # does not provide yet the same level of support as earlier versions for certain
  79. # features related to automated testing. Therefore, the Docker image used is not
  80. # the latest one, but an older version known to work.
  81. #
  82. # The acceptance tests expect the Selenium server to be accessible at
  83. # "127.0.0.1:4444"; as the Selenium server container and the container in which
  84. # the acceptance tests are run share the same network nothing else needs to be
  85. # done for the acceptance tests to access the Selenium server and for the
  86. # Selenium server to access the Nextcloud server. However, in order to ensure
  87. # from this script that the Selenium server was started the 4444 port of its
  88. # container is mapped to the 4444 port of the host.
  89. #
  90. # Besides the Selenium server, the Docker image also provides a VNC server, so
  91. # the 5900 port of the container is also mapped to the 5900 port of the host.
  92. #
  93. # The Docker container started here will be automatically stopped when the
  94. # script exits (see cleanUp). If the Selenium server can not be started then the
  95. # script will be exited immediately with an error state; the most common cause
  96. # for the Selenium server to fail to start is that another server is already
  97. # using the mapped ports in the host.
  98. #
  99. # As the web browser is run inside the Docker container it is not visible by
  100. # default. However, it can be viewed using VNC (for example,
  101. # "vncviewer 127.0.0.1:5900"); when asked for the password use "secret".
  102. function prepareSelenium() {
  103. SELENIUM_CONTAINER=selenium-nextcloud-local-test-acceptance
  104. echo "Starting Selenium server"
  105. docker run --detach --name=$SELENIUM_CONTAINER --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-firefox-debug:2.53.1-beryllium
  106. echo "Waiting for Selenium server to be ready"
  107. if ! $TIMEOUT 10s bash -c "while ! curl 127.0.0.1:4444 >/dev/null 2>&1; do sleep 1; done"; then
  108. echo "Could not start Selenium server; running" \
  109. "\"docker run --rm --publish 4444:4444 --publish 5900:5900 $DOCKER_OPTIONS selenium/standalone-firefox-debug:2.53.1-beryllium\"" \
  110. "could give you a hint of the problem"
  111. exit 1
  112. fi
  113. }
  114. # Creates a Docker container to run both the acceptance tests and the Nextcloud
  115. # server used by them.
  116. #
  117. # This function starts a Docker container with a copy the Nextcloud code from
  118. # the grandparent directory, although ignoring any configuration or data that it
  119. # may provide (for example, if that directory was used directly to deploy a
  120. # Nextcloud instance in a web server). As the Nextcloud code is copied to the
  121. # container instead of referenced the original code can be modified while the
  122. # acceptance tests are running without interfering in them.
  123. function prepareDocker() {
  124. NEXTCLOUD_LOCAL_CONTAINER=nextcloud-local-test-acceptance
  125. echo "Starting the Nextcloud container"
  126. # As the Nextcloud server container uses the network of the Selenium server
  127. # container the Nextcloud server can be accessed at "127.0.0.1" from the
  128. # Selenium server.
  129. # The container exits immediately if no command is given, so a Bash session
  130. # is created to prevent that.
  131. docker run --detach --name=$NEXTCLOUD_LOCAL_CONTAINER --network=container:$SELENIUM_CONTAINER --interactive --tty nextcloudci/acceptance-php7.3:acceptance-php7.3-2 bash
  132. # Use the $TMPDIR or, if not set, fall back to /tmp.
  133. NEXTCLOUD_LOCAL_TAR="$($MKTEMP --tmpdir="${TMPDIR:-/tmp}" --suffix=.tar nextcloud-local-XXXXXXXXXX)"
  134. # Setting the user and group of files in the tar would be superfluous, as
  135. # "docker cp" does not take them into account (the extracted files are set
  136. # to root).
  137. echo "Copying local Git working directory of Nextcloud to the container"
  138. tar --create --file="$NEXTCLOUD_LOCAL_TAR" \
  139. --exclude=".git" \
  140. --exclude="./build" \
  141. --exclude="./config/config.php" \
  142. --exclude="./data" \
  143. --exclude="./data-autotest" \
  144. --exclude="./tests" \
  145. --exclude="./apps-extra" \
  146. --exclude="./apps-writable" \
  147. --exclude="node_modules" \
  148. --directory=../../ \
  149. .
  150. tar --append --file="$NEXTCLOUD_LOCAL_TAR" --directory=../../ tests/acceptance/
  151. docker exec $NEXTCLOUD_LOCAL_CONTAINER mkdir /nextcloud
  152. docker cp - $NEXTCLOUD_LOCAL_CONTAINER:/nextcloud/ < "$NEXTCLOUD_LOCAL_TAR"
  153. # run-local.sh expects a Git repository to be available in the root of the
  154. # Nextcloud server, but it was excluded when the Git working directory was
  155. # copied to the container to avoid copying the large and unneeded history of
  156. # the repository.
  157. docker exec $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud && git init"
  158. }
  159. # Removes/stops temporal elements created/started by this script.
  160. function cleanUp() {
  161. # Disable (yes, "+" disables) exiting immediately on errors to ensure that
  162. # all the cleanup commands are executed (well, no errors should occur during
  163. # the cleanup anyway, but just in case).
  164. set +o errexit
  165. echo "Cleaning up"
  166. if [ -f "$NEXTCLOUD_LOCAL_TAR" ]; then
  167. echo "Removing $NEXTCLOUD_LOCAL_TAR"
  168. rm $NEXTCLOUD_LOCAL_TAR
  169. fi
  170. # The name filter must be specified as "^/XXX$" to get an exact match; using
  171. # just "XXX" would match every name that contained "XXX".
  172. if [ -n "$(docker ps --all --quiet --filter name="^/$NEXTCLOUD_LOCAL_CONTAINER$")" ]; then
  173. echo "Removing Docker container $NEXTCLOUD_LOCAL_CONTAINER"
  174. docker rm --volumes --force $NEXTCLOUD_LOCAL_CONTAINER
  175. fi
  176. if [ -n "$(docker ps --all --quiet --filter name="^/$SELENIUM_CONTAINER$")" ]; then
  177. echo "Removing Docker container $SELENIUM_CONTAINER"
  178. docker rm --volumes --force $SELENIUM_CONTAINER
  179. fi
  180. }
  181. # Exit immediately on errors.
  182. set -o errexit
  183. # Execute cleanUp when the script exits, either normally or due to an error.
  184. trap cleanUp EXIT
  185. # Ensure working directory is script directory, as some actions (like copying
  186. # the Git working directory to the container) expect that.
  187. cd "$(dirname $0)"
  188. # "--acceptance-tests-dir XXX" option can be provided to set the directory
  189. # (relative to the root directory of the Nextcloud server) used to look for the
  190. # Behat configuration and the Nextcloud installation script.
  191. # By default it is "tests/acceptance", that is, the acceptance tests for the
  192. # Nextcloud server itself.
  193. ACCEPTANCE_TESTS_DIR_OPTION=""
  194. if [ "$1" = "--acceptance-tests-dir" ]; then
  195. ACCEPTANCE_TESTS_DIR_OPTION="--acceptance-tests-dir $2"
  196. shift 2
  197. fi
  198. # "--timeout-multiplier N" option can be provided before the specific scenario
  199. # to run, if any, to set the timeout multiplier to be used in the acceptance
  200. # tests.
  201. TIMEOUT_MULTIPLIER_OPTION=""
  202. if [ "$1" = "--timeout-multiplier" ]; then
  203. if [[ ! "$2" =~ ^[0-9]+$ ]]; then
  204. echo "--timeout-multiplier must be followed by a positive integer"
  205. exit 1
  206. fi
  207. TIMEOUT_MULTIPLIER_OPTION="--timeout-multiplier $2"
  208. shift 2
  209. fi
  210. # If no parameter is provided to this script all the acceptance tests are run.
  211. SCENARIO_TO_RUN=$1
  212. setOperatingSystemAbstractionVariables
  213. prepareSelenium
  214. prepareDocker
  215. echo "Running tests"
  216. docker exec $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud && tests/acceptance/run-local.sh $ACCEPTANCE_TESTS_DIR_OPTION $TIMEOUT_MULTIPLIER_OPTION allow-git-repository-modifications $SCENARIO_TO_RUN"