nextcloud/build/acceptance/features/core/NextcloudTestServerContext.php

157 lines
5.9 KiB
PHP

<?php
/**
*
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
/**
* Behat context to run each scenario against a clean Nextcloud server.
*
* Before each scenario is run, this context sets up a fresh Nextcloud server
* with predefined data and configuration. Thanks to this every scenario is
* independent from the others and they all know the initial state of the
* server.
*
* This context is expected to be used along with RawMinkContext contexts (or
* subclasses). As the server address can be different for each scenario, this
* context automatically sets the "base_url" parameter of all its sibling
* RawMinkContexts; just add NextcloudTestServerContext to the context list of a
* suite in "behat.yml".
*
* The Nextcloud server is set up by running a new Docker container; the Docker
* image used by the container must provide a Nextcloud server ready to be used
* by the tests. By default, the image "nextcloud-local-test-acceptance" is
* used, although that can be customized using the "dockerImageName" parameter
* in "behat.yml". In the same way, the range of ports in which the Nextcloud
* server will be published in the local host (by default, "15000-16000") can be
* customized using the "hostPortRangeForContainer" parameter.
*
* Note that using Docker containers as a regular user requires giving access to
* the Docker daemon to that user. Unfortunately, that makes possible for that
* user to get root privileges for the system. Please see the
* NextcloudTestServerDockerHelper documentation for further information on this
* issue.
*/
class NextcloudTestServerContext implements Context {
/**
* @var NextcloudTestServerDockerHelper
*/
private $dockerHelper;
/**
* Creates a new NextcloudTestServerContext.
*
* @param string $dockerImageName the name of the Docker image that provides
* the Nextcloud test server.
* @param string $hostPortRangeForContainer the range of local ports in the
* host in which the port 80 of the container can be published.
*/
public function __construct($dockerImageName = "nextcloud-local-test-acceptance", $hostPortRangeForContainer = "15000-16000") {
$this->dockerHelper = new NextcloudTestServerDockerHelper($dockerImageName, $hostPortRangeForContainer);
}
/**
* @BeforeScenario
*
* Sets up the Nextcloud test server before each scenario.
*
* It starts the Docker container and, once ready, it sets the "base_url"
* parameter of the sibling RawMinkContexts to "http://" followed by the IP
* address and port of the container; if the Docker container can not be
* started after some time an exception is thrown (as it is just a warning
* for the test runner and nothing to be explicitly catched a plain base
* Exception is used).
*
* @param \Behat\Behat\Hook\Scope\BeforeScenarioScope $scope the
* BeforeScenario hook scope.
* @throws \Exception if the Docker container can not be started.
*/
public function startNextcloudTestServer(BeforeScenarioScope $scope) {
$this->dockerHelper->createAndStartContainer();
$serverAddress = $this->dockerHelper->getNextcloudTestServerAddress();
$isServerReadyCallback = function() use ($serverAddress) {
return $this->isServerReady($serverAddress);
};
$timeout = 10;
$timeoutStep = 0.5;
if (!Utils::waitFor($isServerReadyCallback, $timeout, $timeoutStep)) {
throw new Exception("Docker container for Nextcloud could not be started");
}
$this->setBaseUrlInSiblingRawMinkContexts($scope, "http://" . $serverAddress . "/index.php");
}
/**
* @AfterScenario
*
* Cleans up the Nextcloud test server after each scenario.
*
* It stops and removes the Docker container; if the Docker container can
* not be removed after some time an exception is thrown (as it is just a
* warning for the test runner and nothing to be explicitly catched a plain
* base Exception is used).
*
* @throws \Exception if the Docker container can not be removed.
*/
public function stopNextcloudTestServer() {
$this->dockerHelper->stopAndRemoveContainer();
$wasContainerRemovedCallback = function() {
return !$this->dockerHelper->isContainerRegistered();
};
$timeout = 10;
$timeoutStep = 0.5;
if (!Utils::waitFor($wasContainerRemovedCallback, $timeout, $timeoutStep)) {
throw new Exception("Docker container for Nextcloud (" . $this->dockerHelper->getContainerName() . ") could not be removed");
}
}
private function isServerReady($serverAddress) {
$curlHandle = curl_init("http://" . $serverAddress);
// Returning the transfer as the result of curl_exec prevents the
// transfer from being written to the output.
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
$transfer = curl_exec($curlHandle);
curl_close($curlHandle);
return $transfer !== false;
}
private function setBaseUrlInSiblingRawMinkContexts(BeforeScenarioScope $scope, $baseUrl) {
$environment = $scope->getEnvironment();
foreach ($environment->getContexts() as $context) {
if ($context instanceof Behat\MinkExtension\Context\RawMinkContext) {
$context->setMinkParameter("base_url", $baseUrl);
}
}
}
}