Add Drone CI

- Add drone test environment
- Add drone config
- apt phpunit
- Fix api.php
- Fix item.php
- Fix DBStructure
- Check if caching is possible during tests
This commit is contained in:
Philipp Holzer 2019-09-16 14:47:49 +02:00
parent b51dedd7e7
commit d5dd12b8f8
No known key found for this signature in database
GPG key ID: D8365C3D36B77D90
19 changed files with 321 additions and 34 deletions

34
.drone.yml Normal file
View file

@ -0,0 +1,34 @@
kind: pipeline
name: mysql-php7.1
steps:
- name: mysql-php7.1
image: friendicaci/php7.1:php7.1
commands:
- NOCOVERAGE=true ./autotest.sh
environment:
MYSQL_USERNAME: friendica
MYSQL_PASSWORD: friendica
MYSQL_DATABASE: friendica
MYSQL_HOST: mysql
services:
- name: mysql
image: mysql:8.0
command: [ "--default-authentication-plugin=mysql_native_password" ]
environment:
MYSQL_ROOT_PASSWORD: friendica
MYSQL_USER: friendica
MYSQL_PASSWORD: friendica
MYSQL_DATABASE: friendica
tmpfs:
- /var/lib/mysql
#trigger:
# branch:
# - master
# - develop
# - "*-rc"
# event:
# - pull_request
# - push

View file

@ -26,4 +26,6 @@ before_script:
- phpenv config-add .travis/redis.ini - phpenv config-add .travis/redis.ini
- phpenv config-add .travis/memcached.ini - phpenv config-add .travis/memcached.ini
script: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
after_success: bash <(curl -s https://codecov.io/bash) after_success: bash <(curl -s https://codecov.io/bash)

161
autotest.sh Executable file
View file

@ -0,0 +1,161 @@
#!/usr/bin/env bash
DATABASENAME=${MYSQL_DATABASE:-test}
DATABASEUSER=${MYSQL_USERNAME:-friendica}
DATABASEHOST=${MYSQL_HOST:-localhost}
BASEDIR=$PWD
export MYSQL_DATABASE="$DATABASENAME"
export MYSQL_USERNAME="$DATABASEUSER"
export MYSQL_PASSWORD="friendica"
if [ -z "$PHP_EXE" ]; then
PHP_EXE=php
fi
PHP=$(which "$PHP_EXE")
# Use the Friendica internal composer
COMPOSER="$BASEDIR/bin/composer.phar"
set -e
_XDEBUG_CONFIG=$XDEBUG_CONFIG
unset XDEBUG_CONFIG
if [ -x "$PHP" ]; then
echo "Using PHP executable $PHP"
else
echo "Could not find PHP executable $PHP_EXE" >&2
exit 3
fi
echo "Installing depdendencies"
$PHP "$COMPOSER" install
PHPUNIT="$BASEDIR/vendor/bin/phpunit"
if [ -x "$PHPUNIT" ]; then
echo "Using PHPUnit executable $PHPUNIT"
else
echo "Could not find PHPUnit executable after composer $PHPUNIT" >&2
exit 3
fi
if ! [ \( -w config -a ! -f config/local.config.php \) -o \( -f config/local.config.php -a -w config/local.config.php \) ]; then
echo "Please enable write permissions on config and config/config.php" >&2
exit 1
fi
# Back up existing (dev) config if one exists and backup not already there
if [ -f config/local.config.php ] && [ ! -f config/local.config-autotest-backup.php ]; then
mv config/local.config.php config/local.config-autotest-backup.php
fi
function cleanup_config {
if [ -n "$DOCKER_CONTAINER_ID" ]; then
echo "Kill the docker $DOCKER_CONTAINER_ID"
docker stop "$DOCKER_CONTAINER_ID"
docker rm -f "$DOCKER_CONTAINER_ID"
fi
cd "$BASEDIR"
# Restore existing config
if [ -f config/local.config-autotest-backup.php ]; then
mv config/local.config-autotest-backup.php config/local.config.php
fi
}
# restore config on exit
trap cleanup_config EXIT
function execute_tests {
echo "Setup environment for MariaDB testing ..."
# back to root folder
cd "$BASEDIR"
# backup current config
if [ -f config/local.config.php ]; then
mv config/local.config.php config/local.config-autotest-backup.php
fi
if [ -n "$USEDOCKER" ]; then
echo "Fire up the mysql docker"
DOCKER_CONTAINER_ID=$(docker run \
-e MYSQL_ROOT_PASSWORD=friendica \
-e MYSQL_USER="$DATABASEUSER" \
-e MYSQL_PASSWORD=friendica \
-e MYSQL_DATABASE="$DATABASENAME" \
-d mysql)
DATABASEHOST=$(docker inspect --format="{{.NetworkSettings.IPAddress}}" "$DOCKER_CONTAINER_ID")
else
if [ -z "$DRONE" ]; then # no need to drop the DB when we are on CI
if [ "mysql" != "$(mysql --version | grep -o mysql)" ]; then
echo "Your mysql binary is not provided by mysql"
echo "To use the docker container set the USEDOCKER environment variable"
exit 3
fi
mysql -u "$DATABASEUSER" -pfriendica -e "DROP DATABASE IF EXISTS $DATABASENAME"
mysql -u "$DATABASEUSER" -pfriendica -e "CREATE DATABASE $DATABASENAME DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"
else
DATABASEHOST=mysql
fi
fi
echo "Waiting for MySQL $DATABASEHOST initialization..."
if ! bin/wait-for-connection $DATABASEHOST 3306 300; then
echo "[ERROR] Waited 300 seconds, no response" >&2
exit 1
fi
if [ -n "$USEDOCKER" ]; then
echo "Initialize database..."
docker exec $DOCKER_CONTAINER_ID mysql -u root -pfriendica -e 'CREATE DATABASE IF NOT EXISTS $DATABASENAME;'
fi
export MYSQL_HOST="$DATABASEHOST"
#call installer
echo "Installing Friendica..."
"$PHP" ./bin/console.php autoinstall --dbuser="$DATABASEUSER" --dbpass=friendica --dbdata="$DATABASENAME" --dbhost="$DATABASEHOST" --url=https://friendica.local --admin=admin@friendica.local
#test execution
echo "Testing..."
rm -fr "coverage-html"
mkdir "coverage-html"
if [[ "$_XDEBUG_CONFIG" ]]; then
export XDEBUG_CONFIG=$_XDEBUG_CONFIG
fi
COVER=''
if [ -z "$NOCOVERAGE" ]; then
COVER="--coverage-clover autotest-clover.xml --coverage-html coverage-html"
else
echo "No coverage"
fi
INPUT="$BASEDIR/tests"
if [ -n "$1" ]; then
INPUT="$INPUT/$1"
fi
echo "${PHPUNIT[@]}" --configuration tests/phpunit.xml $COVER --log-junit "autotest-results.xml" "$INPUT" "$2"
"${PHPUNIT[@]}" --configuration tests/phpunit.xml $COVER --log-junit "autotest-results.xml" "$INPUT" "$2"
RESULT=$?
if [ -n "$DOCKER_CONTAINER_ID" ]; then
echo "Kill the docker $DOCKER_CONTAINER_ID"
docker stop $DOCKER_CONTAINER_ID
docker rm -f $DOCKER_CONTAINER_ID
unset $DOCKER_CONTAINER_ID
fi
}
#
# Start the test execution
#
if [ -n "$1" ] && [ ! -f "tests/$FILENAME" ] && [ "${FILENAME:0:2}" != "--" ]; then
execute_tests "$FILENAME" "$2"
else
execute_tests
fi

40
bin/wait-for-connection Executable file
View file

@ -0,0 +1,40 @@
#!/usr/bin/php
<?php
$timeout = 60;
switch ($argc) {
case 4:
$timeout = (float)$argv[3];
case 3:
$host = $argv[1];
$port = (int)$argv[2];
break;
default:
fwrite(STDERR, 'Usage: '.$argv[0].' host port [timeout]'."\n");
exit(2);
}
if ($timeout < 0) {
fwrite(STDERR, 'Timeout must be greater than zero'."\n");
exit(2);
}
if ($port < 1) {
fwrite(STDERR, 'Port must be an integer greater than zero'."\n");
exit(2);
}
$socketTimeout = (float)ini_get('default_socket_timeout');
if ($socketTimeout > $timeout) {
$socketTimeout = $timeout;
}
$stopTime = time() + $timeout;
do {
$sock = @fsockopen($host, $port, $errno, $errstr, $socketTimeout);
if ($sock !== false) {
fclose($sock);
fwrite(STDOUT, "\n");
exit(0);
}
sleep(1);
fwrite(STDOUT, '.');
} while (time() < $stopTime);
fwrite(STDOUT, "\n");
exit(1);

View file

@ -48,9 +48,9 @@ use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\XML; use Friendica\Util\XML;
require_once 'mod/share.php'; require_once __DIR__ . '/../mod/share.php';
require_once 'mod/item.php'; require_once __DIR__ . '/../mod/item.php';
require_once 'mod/wall_upload.php'; require_once __DIR__ . '/../mod/wall_upload.php';
define('API_METHOD_ANY', '*'); define('API_METHOD_ANY', '*');
define('API_METHOD_GET', 'GET'); define('API_METHOD_GET', 'GET');

View file

@ -2,6 +2,7 @@
/** /**
* @file mod/api.php * @file mod/api.php
*/ */
use Friendica\App; use Friendica\App;
use Friendica\Core\Config; use Friendica\Core\Config;
use Friendica\Core\L10n; use Friendica\Core\L10n;
@ -9,7 +10,7 @@ use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\Module\Login; use Friendica\Module\Login;
require_once 'include/api.php'; require_once __DIR__ . '/../include/api.php';
function oauth_get_client(OAuthRequest $request) function oauth_get_client(OAuthRequest $request)
{ {

View file

@ -42,7 +42,7 @@ use Friendica\Util\Security;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Worker\Delivery; use Friendica\Worker\Delivery;
require_once 'include/items.php'; require_once __DIR__ . '/../include/items.php';
function item_post(App $a) { function item_post(App $a) {
if (!local_user() && !remote_user()) { if (!local_user() && !remote_user()) {

View file

@ -37,7 +37,7 @@ class MemcacheCache extends Cache implements IMemoryCache
$memcache_host = $config->get('system', 'memcache_host'); $memcache_host = $config->get('system', 'memcache_host');
$memcache_port = $config->get('system', 'memcache_port'); $memcache_port = $config->get('system', 'memcache_port');
if (!$this->memcache->connect($memcache_host, $memcache_port)) { if (!@$this->memcache->connect($memcache_host, $memcache_port)) {
throw new Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available'); throw new Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available');
} }
} }

View file

@ -37,9 +37,9 @@ class RedisCache extends Cache implements IMemoryCache
$redis_pw = $config->get('system', 'redis_password'); $redis_pw = $config->get('system', 'redis_password');
$redis_db = $config->get('system', 'redis_db', 0); $redis_db = $config->get('system', 'redis_db', 0);
if (isset($redis_port) && !$this->redis->connect($redis_host, $redis_port)) { if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available'); throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
} elseif (!$this->redis->connect($redis_host)) { } elseif (!@$this->redis->connect($redis_host)) {
throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available'); throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available');
} }

View file

@ -12,7 +12,7 @@ use Friendica\Core\L10n;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
require_once 'include/dba.php'; require_once __DIR__ . '/../../include/dba.php';
/** /**
* @brief This class contain functions for the database management * @brief This class contain functions for the database management

View file

@ -67,7 +67,7 @@ class Database
{ {
// Use environment variables for mysql if they are set beforehand // Use environment variables for mysql if they are set beforehand
if (!empty($server['MYSQL_HOST']) if (!empty($server['MYSQL_HOST'])
&& !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER'])) && (!empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER'])))
&& $server['MYSQL_PASSWORD'] !== false && $server['MYSQL_PASSWORD'] !== false
&& !empty($server['MYSQL_DATABASE'])) && !empty($server['MYSQL_DATABASE']))
{ {

View file

@ -80,7 +80,7 @@ class StaticDatabase extends Database
{ {
// Use environment variables for mysql if they are set beforehand // Use environment variables for mysql if they are set beforehand
if (!empty($server['MYSQL_HOST']) if (!empty($server['MYSQL_HOST'])
&& !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER'])) && (!empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER'])))
&& $server['MYSQL_PASSWORD'] !== false && $server['MYSQL_PASSWORD'] !== false
&& !empty($server['MYSQL_DATABASE'])) && !empty($server['MYSQL_DATABASE']))
{ {

View file

@ -1,16 +1,17 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="utf-8" ?>
<phpunit <phpunit
bootstrap="tests/bootstrap.php" bootstrap="bootstrap.php"
verbose="true"> verbose="true">
<testsuites> <testsuite name='friendica'>
<testsuite> <directory suffix='.php'>functional/</directory>
<directory>tests/</directory> <directory suffix='.php'>include/</directory>
<directory suffix='.php'>src/</directory>
<directory suffix='.php'>./</directory>
</testsuite> </testsuite>
</testsuites>
<!-- Filters for Code Coverage --> <!-- Filters for Code Coverage -->
<filter> <filter>
<whitelist> <whitelist>
<directory suffix=".php">.</directory> <directory suffix=".php">..</directory>
<exclude> <exclude>
<directory suffix=".php">config/</directory> <directory suffix=".php">config/</directory>
<directory suffix=".php">doc/</directory> <directory suffix=".php">doc/</directory>
@ -22,9 +23,6 @@
</exclude> </exclude>
</whitelist> </whitelist>
</filter> </filter>
<logging>
<log type="coverage-clover" target="clover.xml" />
</logging>
<listeners> <listeners>
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" /> <listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" />
</listeners> </listeners>

View file

@ -14,16 +14,22 @@ class MemcacheCacheTest extends MemoryCacheTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['MEMCACHE_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcache_host') ->with('system', 'memcache_host')
->andReturn('localhost'); ->andReturn($host);
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcache_port') ->with('system', 'memcache_port')
->andReturn(11211); ->andReturn(11211);
$this->cache = new MemcacheCache('localhost', $configMock); try {
$this->cache = new MemcacheCache($host, $configMock);
} catch (\Exception $e) {
$this->markTestSkipped('Memcache is not available');
}
return $this->cache; return $this->cache;
} }

View file

@ -16,14 +16,20 @@ class MemcachedCacheTest extends MemoryCacheTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['MEMCACHED_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcached_hosts') ->with('system', 'memcached_hosts')
->andReturn([0 => 'localhost, 11211']); ->andReturn([0 => $host . ', 11211']);
$logger = new NullLogger(); $logger = new NullLogger();
$this->cache = new MemcachedCache('localhost', $configMock, $logger); try {
$this->cache = new MemcachedCache($host, $configMock, $logger);
} catch (\Exception $exception) {
$this->markTestSkipped('Memcached is not available');
}
return $this->cache; return $this->cache;
} }

View file

@ -15,10 +15,12 @@ class RedisCacheTest extends MemoryCacheTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['REDIS_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'redis_host') ->with('system', 'redis_host')
->andReturn('localhost'); ->andReturn($host);
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'redis_port') ->with('system', 'redis_port')
@ -33,7 +35,11 @@ class RedisCacheTest extends MemoryCacheTest
->with('system', 'redis_password') ->with('system', 'redis_password')
->andReturn(null); ->andReturn(null);
$this->cache = new RedisCache('localhost', $configMock); try {
$this->cache = new RedisCache($host, $configMock);
} catch (\Exception $e) {
$this->markTestSkipped('Redis is not available.');
}
return $this->cache; return $this->cache;
} }

View file

@ -16,15 +16,26 @@ class MemcacheCacheLockTest extends LockTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['MEMCACHE_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcache_host') ->with('system', 'memcache_host')
->andReturn('localhost'); ->andReturn($host);
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcache_port') ->with('system', 'memcache_port')
->andReturn(11211); ->andReturn(11211);
return new CacheLock(new MemcacheCache('localhost', $configMock)); $lock = null;
try {
$cache = new MemcacheCache($host, $configMock);
$lock = new CacheLock($cache);
} catch (\Exception $e) {
$this->markTestSkipped('Memcache is not available');
}
return $lock;
} }
} }

View file

@ -17,13 +17,24 @@ class MemcachedCacheLockTest extends LockTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['MEMCACHED_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'memcached_hosts') ->with('system', 'memcached_hosts')
->andReturn([0 => 'localhost, 11211']); ->andReturn([0 => $host . ', 11211']);
$logger = new NullLogger(); $logger = new NullLogger();
return new CacheLock(new MemcachedCache('localhost', $configMock, $logger)); $lock = null;
try {
$cache = new MemcachedCache($host, $configMock, $logger);
$lock = new CacheLock($cache);
} catch (\Exception $e) {
$this->markTestSkipped('Memcached is not available');
}
return $lock;
} }
} }

View file

@ -16,10 +16,12 @@ class RedisCacheLockTest extends LockTest
{ {
$configMock = \Mockery::mock(Configuration::class); $configMock = \Mockery::mock(Configuration::class);
$host = $_SERVER['REDIS_HOST'] ?? 'localhost';
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'redis_host') ->with('system', 'redis_host')
->andReturn('localhost'); ->andReturn($host);
$configMock $configMock
->shouldReceive('get') ->shouldReceive('get')
->with('system', 'redis_port') ->with('system', 'redis_port')
@ -34,6 +36,15 @@ class RedisCacheLockTest extends LockTest
->with('system', 'redis_password') ->with('system', 'redis_password')
->andReturn(null); ->andReturn(null);
return new CacheLock(new RedisCache('localhost', $configMock)); $lock = null;
try {
$cache = new RedisCache($host, $configMock);
$lock = new CacheLock($cache);
} catch (\Exception $e) {
$this->markTestSkipped('Redis is not available');
}
return $lock;
} }
} }