From 6418fed24dbd58b579a9e9d00b3713f0f9ac9648 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 4 Jan 2021 15:02:30 +0100 Subject: [PATCH 01/39] Adds parallel testing --- composer.json | 1 + src/Illuminate/Database/Schema/Builder.php | 22 +++++ .../Database/Schema/Grammars/Grammar.php | 24 +++++ .../Database/Schema/Grammars/MySqlGrammar.php | 31 +++++++ .../Database/Schema/MySqlBuilder.php | 26 ++++++ .../Foundation/Testing/RefreshDatabase.php | 75 ++++++++++++++- .../Testing/RefreshDatabaseState.php | 7 ++ src/Illuminate/Support/Facades/Schema.php | 2 + src/Illuminate/Support/Facades/Storage.php | 2 + src/Illuminate/Support/Facades/Testing.php | 22 +++++ src/Illuminate/Testing/Testing.php | 93 +++++++++++++++++++ .../DatabaseAbstractSchemaGrammarTest.php | 37 ++++++++ .../DatabaseMySqlSchemaGrammarTest.php | 42 +++++++++ tests/Database/DatabaseMysqlBuilderTest.php | 48 ++++++++++ tests/Database/DatabaseSchemaBuilderTest.php | 27 ++++++ tests/Testing/TestingTest.php | 84 +++++++++++++++++ 16 files changed, 540 insertions(+), 3 deletions(-) create mode 100644 src/Illuminate/Support/Facades/Testing.php create mode 100644 src/Illuminate/Testing/Testing.php create mode 100755 tests/Database/DatabaseAbstractSchemaGrammarTest.php create mode 100644 tests/Database/DatabaseMysqlBuilderTest.php create mode 100644 tests/Testing/TestingTest.php diff --git a/composer.json b/composer.json index 2f5533c97fe0..79bea59f041d 100644 --- a/composer.json +++ b/composer.json @@ -131,6 +131,7 @@ "ext-posix": "Required to use all features of the queue worker.", "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.155).", + "brianium/paratest": "Required to run tests in parallel (^6.0).", "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6|^3.0).", "filp/whoops": "Required for friendly error pages in development (^2.8).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 80611bfc92ed..32da92049bd0 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -94,6 +94,28 @@ public static function morphUsingUuids() return static::defaultMorphKeyType('uuid'); } + /** + * Create a database in the schema if the database not exists. + * + * @param string $name + * @return bool + */ + public function createDatabaseIfNotExists($name) + { + throw new LogicException('This database driver does not support create databases.'); + } + + /** + * Drop a database from the schema if the database exists. + * + * @param string $name + * @return bool + */ + public function dropDatabaseIfExists($name) + { + throw new LogicException('This database driver does not support drop databases.'); + } + /** * Determine if the given table exists. * diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index b60dfe817b62..46d3271a36da 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -9,6 +9,7 @@ use Illuminate\Database\Query\Expression; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Fluent; +use LogicException; use RuntimeException; abstract class Grammar extends BaseGrammar @@ -27,6 +28,29 @@ abstract class Grammar extends BaseGrammar */ protected $fluentCommands = []; + /** + * Compile a create database if not exists command. + * + * @param string $name + * @param \Illuminate\Database\Connection $connection + * @return string + */ + public function compileCreateDatabaseIfNotExists($name, $connection) + { + throw new LogicException('This database driver does not support create databases.'); + } + + /** + * Compile a drop database if exists command. + * + * @param string $name + * @return string + */ + public function compileDropDatabaseIfExists($name) + { + throw new LogicException('This database driver does not support drop databases.'); + } + /** * Compile a rename column command. * diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index c2952e47926c..02c985d4df81 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -26,6 +26,37 @@ class MySqlGrammar extends Grammar */ protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger']; + /** + * Compile a create database if not exists command. + * + * @param string $name + * @param \Illuminate\Database\Connection $connection + * @return string + */ + public function compileCreateDatabaseIfNotExists($name, $connection) + { + return sprintf( + 'CREATE DATABASE IF NOT EXISTS %s CHARACTER SET %s COLLATE %s;', + $this->wrapValue($name), + $this->wrapValue($connection->getConfig('charset')), + $this->wrapValue($connection->getConfig('collation')), + ); + } + + /** + * Compile a drop database if exists command. + * + * @param string $name + * @return string + */ + public function compileDropDatabaseIfExists($name) + { + return sprintf( + 'DROP DATABASE IF EXISTS %s;', + $this->wrapValue($name) + ); + } + /** * Compile the query to determine the list of tables. * diff --git a/src/Illuminate/Database/Schema/MySqlBuilder.php b/src/Illuminate/Database/Schema/MySqlBuilder.php index f07946c85e23..7c298c01e18d 100755 --- a/src/Illuminate/Database/Schema/MySqlBuilder.php +++ b/src/Illuminate/Database/Schema/MySqlBuilder.php @@ -4,6 +4,32 @@ class MySqlBuilder extends Builder { + /** + * Create a database in the schema if the database not exists. + * + * @param string $name + * @return bool + */ + public function createDatabaseIfNotExists($name) + { + return $this->connection->statement( + $this->grammar->compileCreateDatabaseIfNotExists($name, $this->connection) + ); + } + + /** + * Drop a database from the schema if the database exists. + * + * @param string $name + * @return bool + */ + public function dropDatabaseIfExists($name) + { + return $this->connection->statement( + $this->grammar->compileDropDatabaseIfExists($name) + ); + } + /** * Determine if the given table exists. * diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabase.php b/src/Illuminate/Foundation/Testing/RefreshDatabase.php index f62fad83c559..65ce1c5abeb7 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabase.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabase.php @@ -3,6 +3,8 @@ namespace Illuminate\Foundation\Testing; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\Testing; trait RefreshDatabase { @@ -13,9 +15,15 @@ trait RefreshDatabase */ public function refreshDatabase() { - $this->usingInMemoryDatabase() - ? $this->refreshInMemoryDatabase() - : $this->refreshTestDatabase(); + if ($this->usingInMemoryDatabase()) { + return $this->refreshInMemoryDatabase(); + } + + Testing::whenRunningInParallel(function () { + $this->switchToTemporaryDatabase(); + }); + + $this->refreshTestDatabase(); } /** @@ -30,6 +38,67 @@ protected function usingInMemoryDatabase() return config("database.connections.$default.database") === ':memory:'; } + /** + * Switch to the temporary test database. + * + * @return void + */ + protected function switchToTemporaryDatabase() + { + $default = config('database.default'); + + config()->set( + "database.connections.{$default}.database", + RefreshDatabaseState::$temporaryDatabase, + ); + } + + /** + * Creates a temporary database, if needed. + * + * @beforeClass + * + * @return void + */ + public static function setUpTemporaryDatabase() + { + tap(new static(), function ($testCase) { + $testCase->refreshApplication(); + + if ($testCase->usingInMemoryDatabase()) { + return; + } + + Testing::whenRunningInParallel(function () use ($testCase) { + $name = $testCase->getConnection()->getConfig('database'); + + Schema::createDatabaseIfNotExists( + RefreshDatabaseState::$temporaryDatabase = Testing::addTokenIfNeeded($name) + ); + }); + })->app->flush(); + } + + /** + * Drop the temporary database, if any. + * + * @afterClass + * + * @return void + */ + public static function tearDownTemporaryDatabase() + { + if (RefreshDatabaseState::$temporaryDatabase) { + tap(new static(), function ($testCase) { + $testCase->refreshApplication(); + + Schema::dropDatabaseIfExists( + RefreshDatabaseState::$temporaryDatabase, + ); + })->app->flush(); + } + } + /** * Refresh the in-memory database. * diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php b/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php index 1f33087396f6..fab4f0cff52b 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php @@ -10,4 +10,11 @@ class RefreshDatabaseState * @var bool */ public static $migrated = false; + + /** + * The temporary database name, if any. + * + * @var string|null + */ + public static $temporaryDatabase; } diff --git a/src/Illuminate/Support/Facades/Schema.php b/src/Illuminate/Support/Facades/Schema.php index f11c58bc069f..3159b3f42565 100755 --- a/src/Illuminate/Support/Facades/Schema.php +++ b/src/Illuminate/Support/Facades/Schema.php @@ -4,8 +4,10 @@ /** * @method static \Illuminate\Database\Schema\Builder create(string $table, \Closure $callback) + * @method static \Illuminate\Database\Schema\Builder createDatabaseIfNotExists(string $name) * @method static \Illuminate\Database\Schema\Builder disableForeignKeyConstraints() * @method static \Illuminate\Database\Schema\Builder drop(string $table) + * @method static \Illuminate\Database\Schema\Builder dropDatabaseIfExists(string $name) * @method static \Illuminate\Database\Schema\Builder dropIfExists(string $table) * @method static \Illuminate\Database\Schema\Builder enableForeignKeyConstraints() * @method static \Illuminate\Database\Schema\Builder rename(string $from, string $to) diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index 3c7154a63c82..a1f4e2ec32cb 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -54,6 +54,8 @@ public static function fake($disk = null, array $config = []) $root = storage_path('framework/testing/disks/'.$disk) ); + $root = Testing::addTokenIfNeeded($root); + static::set($disk, $fake = static::createLocalDriver(array_merge($config, [ 'root' => $root, ]))); diff --git a/src/Illuminate/Support/Facades/Testing.php b/src/Illuminate/Support/Facades/Testing.php new file mode 100644 index 000000000000..614104fa5771 --- /dev/null +++ b/src/Illuminate/Support/Facades/Testing.php @@ -0,0 +1,22 @@ +app = $app; + } + + /** + * Adds an unique test token to the given string, if needed. + * + * @return string + */ + public function addTokenIfNeeded($string) + { + if (! $this->inParallel()) { + return $string; + } + + return "{$string}_test_{$this->token()}"; + } + + /** + * Apply the callback if tests are running in parallel. + * + * @param callable $callback + * @return void + */ + public function whenRunningInParallel($callback) + { + if ($this->inParallel()) { + $callback(); + } + } + + /** + * Indicates if the current tests are been run in Parallel. + * + * @return bool + */ + protected function inParallel() + { + return $this->app->runningUnitTests() && getenv('LARAVEL_PARALLEL_TESTING') && $this->token(); + } + + /** + * Gets an unique test token. + * + * @return int|false + */ + protected function token() + { + return static::$tokenResolver + ? call_user_func(static::$tokenResolver) + : getenv('TEST_TOKEN'); + } + + /** + * Set with token resolver callback. + * + * @param \Closure|null $resolver + * @return void + */ + public static function tokenResolver($resolver) + { + static::$tokenResolver = $resolver; + } +} diff --git a/tests/Database/DatabaseAbstractSchemaGrammarTest.php b/tests/Database/DatabaseAbstractSchemaGrammarTest.php new file mode 100755 index 000000000000..21aef6525de4 --- /dev/null +++ b/tests/Database/DatabaseAbstractSchemaGrammarTest.php @@ -0,0 +1,37 @@ +expectException(LogicException::class); + $this->expectExceptionMessage('This database driver does not support create databases.'); + + $grammar->compileCreateDatabaseIfNotExists('foo', m::mock(Connection::class)); + } + + public function testDropDatabaseIfExists() + { + $grammar = new class extends Grammar {}; + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('This database driver does not support drop databases.'); + + $grammar->compileDropDatabaseIfExists('foo'); + } +} diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 3e81cdca0950..419e2980b349 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -1149,6 +1149,48 @@ public function testAddingComment() $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); } + public function testCreateDatabaseIfNotExists() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_foo'); + $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_foo'); + + $statement = $this->getGrammar()->compileCreateDatabaseIfNotExists('my_database_a', $connection); + + $this->assertSame( + 'CREATE DATABASE IF NOT EXISTS `my_database_a` CHARACTER SET `utf8mb4_foo` COLLATE `utf8mb4_unicode_ci_foo`;', + $statement + ); + + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_bar'); + $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_bar'); + + $statement = $this->getGrammar()->compileCreateDatabaseIfNotExists('my_database_b', $connection); + + $this->assertSame( + 'CREATE DATABASE IF NOT EXISTS `my_database_b` CHARACTER SET `utf8mb4_bar` COLLATE `utf8mb4_unicode_ci_bar`;', + $statement + ); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'DROP DATABASE IF EXISTS `my_database_a`;', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'DROP DATABASE IF EXISTS `my_database_b`;', + $statement + ); + } + public function testDropAllTables() { $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); diff --git a/tests/Database/DatabaseMysqlBuilderTest.php b/tests/Database/DatabaseMysqlBuilderTest.php new file mode 100644 index 000000000000..9c905220b22c --- /dev/null +++ b/tests/Database/DatabaseMysqlBuilderTest.php @@ -0,0 +1,48 @@ +shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4'); + $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci'); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'CREATE DATABASE IF NOT EXISTS `my_temporary_database` CHARACTER SET `utf8mb4` COLLATE `utf8mb4_unicode_ci`;' + )->andReturn(true); + + $builder = new MysqlBuilder($connection); + $builder->createDatabaseIfNotExists('my_temporary_database'); + } + + public function testDropDatabaseIfExists() + { + $grammar = new MysqlGrammar(); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'DROP DATABASE IF EXISTS `my_database_a`;' + )->andReturn(true); + + $builder = new MysqlBuilder($connection); + + $builder->dropDatabaseIfExists('my_database_a'); + } +} diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php index 53a13c79128c..2a356060c152 100755 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Database/DatabaseSchemaBuilderTest.php @@ -4,6 +4,7 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Builder; +use LogicException; use Mockery as m; use PHPUnit\Framework\TestCase; use stdClass; @@ -15,6 +16,32 @@ protected function tearDown(): void m::close(); } + public function testCreateDatabaseIfNotExists() + { + $connection = m::mock(Connection::class); + $grammar = m::mock(stdClass::class); + $connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); + $builder = new Builder($connection); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('This database driver does not support create databases.'); + + $builder->createDatabaseIfNotExists('foo'); + } + + public function testDropDatabaseIfExists() + { + $connection = m::mock(Connection::class); + $grammar = m::mock(stdClass::class); + $connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); + $builder = new Builder($connection); + + $this->expectException(LogicException::class); + $this->expectExceptionMessage('This database driver does not support drop databases.'); + + $builder->dropDatabaseIfExists('foo'); + } + public function testHasTableCorrectlyCallsGrammar() { $connection = m::mock(Connection::class); diff --git a/tests/Testing/TestingTest.php b/tests/Testing/TestingTest.php new file mode 100644 index 000000000000..26629f788b34 --- /dev/null +++ b/tests/Testing/TestingTest.php @@ -0,0 +1,84 @@ +shouldReceive('runningUnitTests') + ->once() + ->andReturn(false); + + $testing = new Testing($app); + $run = false; + + $testing->whenRunningInParallel(function () use (&$run) { + $run = true; + }); + $this->assertFalse($run); + + $app->shouldReceive('runningUnitTests') + ->once() + ->andReturn(true); + + Testing::tokenResolver(function () { + return 1; + }); + + $testing->whenRunningInParallel(function () use (&$run) { + $run = true; + }); + $this->assertTrue($run); + } + + public function testAddTokenIfNeeded() + { + $app = m::mock(Application::class); + + $app->shouldReceive('runningUnitTests') + ->once() + ->andReturn(false); + + $this->assertSame( + 'my_local_storage', + (new Testing($app))->addTokenIfNeeded('my_local_storage') + ); + + $app->shouldReceive('runningUnitTests') + ->once() + ->andReturn(true); + + Testing::tokenResolver(function () { + return 1; + }); + + $this->assertSame( + 'my_local_storage_test_1', + (new Testing($app))->addTokenIfNeeded('my_local_storage') + ); + } + + public function tearDown(): void + { + parent::tearDown(); + + m::close(); + Testing::tokenResolver(null); + putenv('LARAVEL_PARALLEL_TESTING=0'); + } +} From dcc511214cafbdfd9e51e2251a2bdb42e913c22d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 4 Jan 2021 18:21:41 +0100 Subject: [PATCH 02/39] Fixes "Class MysqlBuilder" not found --- tests/Database/DatabaseMysqlBuilderTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Database/DatabaseMysqlBuilderTest.php b/tests/Database/DatabaseMysqlBuilderTest.php index 9c905220b22c..df0a6703baa9 100644 --- a/tests/Database/DatabaseMysqlBuilderTest.php +++ b/tests/Database/DatabaseMysqlBuilderTest.php @@ -4,11 +4,11 @@ use Illuminate\Database\Connection; use Illuminate\Database\Schema\Grammars\MysqlGrammar; -use Illuminate\Database\Schema\MysqlBuilder; +use Illuminate\Database\Schema\MySqlBuilder; use Mockery as m; use PHPUnit\Framework\TestCase; -class DatabaseMysqlBuilderTest extends TestCase +class DatabaseMySqlBuilderTest extends TestCase { protected function tearDown(): void { @@ -27,7 +27,7 @@ public function testCreateDatabaseIfNotExists() 'CREATE DATABASE IF NOT EXISTS `my_temporary_database` CHARACTER SET `utf8mb4` COLLATE `utf8mb4_unicode_ci`;' )->andReturn(true); - $builder = new MysqlBuilder($connection); + $builder = new MySqlBuilder($connection); $builder->createDatabaseIfNotExists('my_temporary_database'); } @@ -41,7 +41,7 @@ public function testDropDatabaseIfExists() 'DROP DATABASE IF EXISTS `my_database_a`;' )->andReturn(true); - $builder = new MysqlBuilder($connection); + $builder = new MySqlBuilder($connection); $builder->dropDatabaseIfExists('my_database_a'); } From bb004e5d1ccdf3ee526bed4bcd5ea4786992b415 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 4 Jan 2021 21:37:42 +0100 Subject: [PATCH 03/39] Updates wording when drivers do not support create/drop databases --- src/Illuminate/Database/Schema/Builder.php | 4 ++-- src/Illuminate/Database/Schema/Grammars/Grammar.php | 4 ++-- tests/Database/DatabaseAbstractSchemaGrammarTest.php | 4 ++-- tests/Database/DatabaseSchemaBuilderTest.php | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 32da92049bd0..1d4f76d7b9fd 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -102,7 +102,7 @@ public static function morphUsingUuids() */ public function createDatabaseIfNotExists($name) { - throw new LogicException('This database driver does not support create databases.'); + throw new LogicException('This database driver does not support creating databases.'); } /** @@ -113,7 +113,7 @@ public function createDatabaseIfNotExists($name) */ public function dropDatabaseIfExists($name) { - throw new LogicException('This database driver does not support drop databases.'); + throw new LogicException('This database driver does not support dropping databases.'); } /** diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index 46d3271a36da..85610788242f 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -37,7 +37,7 @@ abstract class Grammar extends BaseGrammar */ public function compileCreateDatabaseIfNotExists($name, $connection) { - throw new LogicException('This database driver does not support create databases.'); + throw new LogicException('This database driver does not support creating databases.'); } /** @@ -48,7 +48,7 @@ public function compileCreateDatabaseIfNotExists($name, $connection) */ public function compileDropDatabaseIfExists($name) { - throw new LogicException('This database driver does not support drop databases.'); + throw new LogicException('This database driver does not support dropping databases.'); } /** diff --git a/tests/Database/DatabaseAbstractSchemaGrammarTest.php b/tests/Database/DatabaseAbstractSchemaGrammarTest.php index 21aef6525de4..ea6f6c6e6394 100755 --- a/tests/Database/DatabaseAbstractSchemaGrammarTest.php +++ b/tests/Database/DatabaseAbstractSchemaGrammarTest.php @@ -20,7 +20,7 @@ public function testCreateDatabaseIfNotExists() $grammar = new class extends Grammar {}; $this->expectException(LogicException::class); - $this->expectExceptionMessage('This database driver does not support create databases.'); + $this->expectExceptionMessage('This database driver does not support creating databases.'); $grammar->compileCreateDatabaseIfNotExists('foo', m::mock(Connection::class)); } @@ -30,7 +30,7 @@ public function testDropDatabaseIfExists() $grammar = new class extends Grammar {}; $this->expectException(LogicException::class); - $this->expectExceptionMessage('This database driver does not support drop databases.'); + $this->expectExceptionMessage('This database driver does not support dropping databases.'); $grammar->compileDropDatabaseIfExists('foo'); } diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php index 2a356060c152..d82d9d69ae5c 100755 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Database/DatabaseSchemaBuilderTest.php @@ -24,7 +24,7 @@ public function testCreateDatabaseIfNotExists() $builder = new Builder($connection); $this->expectException(LogicException::class); - $this->expectExceptionMessage('This database driver does not support create databases.'); + $this->expectExceptionMessage('This database driver does not support creating databases.'); $builder->createDatabaseIfNotExists('foo'); } @@ -37,7 +37,7 @@ public function testDropDatabaseIfExists() $builder = new Builder($connection); $this->expectException(LogicException::class); - $this->expectExceptionMessage('This database driver does not support drop databases.'); + $this->expectExceptionMessage('This database driver does not support dropping databases.'); $builder->dropDatabaseIfExists('foo'); } From 8e30c932f6b6deccaf73f3870338b285dfc7f8f1 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Jan 2021 01:31:36 +0100 Subject: [PATCH 04/39] Avoids "getenv" and "putenv" in favour of $_SERVER --- src/Illuminate/Testing/Testing.php | 4 ++-- tests/Testing/TestingTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Testing/Testing.php b/src/Illuminate/Testing/Testing.php index 8851e4ff758c..c19839e985e1 100644 --- a/src/Illuminate/Testing/Testing.php +++ b/src/Illuminate/Testing/Testing.php @@ -65,7 +65,7 @@ public function whenRunningInParallel($callback) */ protected function inParallel() { - return $this->app->runningUnitTests() && getenv('LARAVEL_PARALLEL_TESTING') && $this->token(); + return $this->app->runningUnitTests() && ! empty($_SERVER['LARAVEL_PARALLEL_TESTING']) && $this->token(); } /** @@ -77,7 +77,7 @@ protected function token() { return static::$tokenResolver ? call_user_func(static::$tokenResolver) - : getenv('TEST_TOKEN'); + : ($_SERVER['TEST_TOKEN'] ?? false); } /** diff --git a/tests/Testing/TestingTest.php b/tests/Testing/TestingTest.php index 26629f788b34..76668b3e54e5 100644 --- a/tests/Testing/TestingTest.php +++ b/tests/Testing/TestingTest.php @@ -13,7 +13,7 @@ protected function setUp(): void { parent::setUp(); - putenv('LARAVEL_PARALLEL_TESTING=1'); + $_SERVER['LARAVEL_PARALLEL_TESTING'] = 1; } public function testWhenRunningInParallel() @@ -79,6 +79,6 @@ public function tearDown(): void m::close(); Testing::tokenResolver(null); - putenv('LARAVEL_PARALLEL_TESTING=0'); + unset($_SERVER['LARAVEL_PARALLEL_TESTING']); } } From 36d5e7a25016255828fef4ab3204defa116bbf75 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Jan 2021 14:53:51 +0100 Subject: [PATCH 05/39] Moves "createDatabaseIfNotExists" to "createDatabase" --- src/Illuminate/Database/Schema/Builder.php | 4 ++-- .../Database/Schema/Grammars/Grammar.php | 4 ++-- .../Database/Schema/Grammars/MySqlGrammar.php | 8 ++++---- .../Database/Schema/MySqlBuilder.php | 6 +++--- .../Foundation/Testing/RefreshDatabase.php | 7 ++++--- src/Illuminate/Support/Facades/Schema.php | 2 +- .../DatabaseAbstractSchemaGrammarTest.php | 4 ++-- .../DatabaseMySqlSchemaGrammarTest.php | 14 +++++++------- tests/Database/DatabaseMysqlBuilderTest.php | 18 +++++++++--------- tests/Database/DatabaseSchemaBuilderTest.php | 4 ++-- 10 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 1d4f76d7b9fd..04f96e43308a 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -95,12 +95,12 @@ public static function morphUsingUuids() } /** - * Create a database in the schema if the database not exists. + * Create a database in the schema. * * @param string $name * @return bool */ - public function createDatabaseIfNotExists($name) + public function createDatabase($name) { throw new LogicException('This database driver does not support creating databases.'); } diff --git a/src/Illuminate/Database/Schema/Grammars/Grammar.php b/src/Illuminate/Database/Schema/Grammars/Grammar.php index 85610788242f..18071b2fbb12 100755 --- a/src/Illuminate/Database/Schema/Grammars/Grammar.php +++ b/src/Illuminate/Database/Schema/Grammars/Grammar.php @@ -29,13 +29,13 @@ abstract class Grammar extends BaseGrammar protected $fluentCommands = []; /** - * Compile a create database if not exists command. + * Compile a create database command. * * @param string $name * @param \Illuminate\Database\Connection $connection * @return string */ - public function compileCreateDatabaseIfNotExists($name, $connection) + public function compileCreateDatabase($name, $connection) { throw new LogicException('This database driver does not support creating databases.'); } diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 02c985d4df81..77909c336675 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -27,16 +27,16 @@ class MySqlGrammar extends Grammar protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger']; /** - * Compile a create database if not exists command. + * Compile a create database command. * * @param string $name * @param \Illuminate\Database\Connection $connection * @return string */ - public function compileCreateDatabaseIfNotExists($name, $connection) + public function compileCreateDatabase($name, $connection) { return sprintf( - 'CREATE DATABASE IF NOT EXISTS %s CHARACTER SET %s COLLATE %s;', + 'CREATE DATABASE %s DEFAULT CHARACTER SET %s DEFAULT COLLATE %s', $this->wrapValue($name), $this->wrapValue($connection->getConfig('charset')), $this->wrapValue($connection->getConfig('collation')), @@ -52,7 +52,7 @@ public function compileCreateDatabaseIfNotExists($name, $connection) public function compileDropDatabaseIfExists($name) { return sprintf( - 'DROP DATABASE IF EXISTS %s;', + 'DROP DATABASE IF EXISTS %s', $this->wrapValue($name) ); } diff --git a/src/Illuminate/Database/Schema/MySqlBuilder.php b/src/Illuminate/Database/Schema/MySqlBuilder.php index 7c298c01e18d..b7cff5568d1b 100755 --- a/src/Illuminate/Database/Schema/MySqlBuilder.php +++ b/src/Illuminate/Database/Schema/MySqlBuilder.php @@ -5,15 +5,15 @@ class MySqlBuilder extends Builder { /** - * Create a database in the schema if the database not exists. + * Create a database in the schema. * * @param string $name * @return bool */ - public function createDatabaseIfNotExists($name) + public function createDatabase($name) { return $this->connection->statement( - $this->grammar->compileCreateDatabaseIfNotExists($name, $this->connection) + $this->grammar->compileCreateDatabase($name, $this->connection) ); } diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabase.php b/src/Illuminate/Foundation/Testing/RefreshDatabase.php index 65ce1c5abeb7..501eec7c193c 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabase.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabase.php @@ -72,9 +72,10 @@ public static function setUpTemporaryDatabase() Testing::whenRunningInParallel(function () use ($testCase) { $name = $testCase->getConnection()->getConfig('database'); - Schema::createDatabaseIfNotExists( - RefreshDatabaseState::$temporaryDatabase = Testing::addTokenIfNeeded($name) - ); + RefreshDatabaseState::$temporaryDatabase = Testing::addTokenIfNeeded($name); + + Schema::dropDatabaseIfExists(RefreshDatabaseState::$temporaryDatabase); + Schema::createDatabase(RefreshDatabaseState::$temporaryDatabase); }); })->app->flush(); } diff --git a/src/Illuminate/Support/Facades/Schema.php b/src/Illuminate/Support/Facades/Schema.php index 3159b3f42565..8c5b32c21ead 100755 --- a/src/Illuminate/Support/Facades/Schema.php +++ b/src/Illuminate/Support/Facades/Schema.php @@ -4,7 +4,7 @@ /** * @method static \Illuminate\Database\Schema\Builder create(string $table, \Closure $callback) - * @method static \Illuminate\Database\Schema\Builder createDatabaseIfNotExists(string $name) + * @method static \Illuminate\Database\Schema\Builder createDatabase(string $name) * @method static \Illuminate\Database\Schema\Builder disableForeignKeyConstraints() * @method static \Illuminate\Database\Schema\Builder drop(string $table) * @method static \Illuminate\Database\Schema\Builder dropDatabaseIfExists(string $name) diff --git a/tests/Database/DatabaseAbstractSchemaGrammarTest.php b/tests/Database/DatabaseAbstractSchemaGrammarTest.php index ea6f6c6e6394..04e23eb264be 100755 --- a/tests/Database/DatabaseAbstractSchemaGrammarTest.php +++ b/tests/Database/DatabaseAbstractSchemaGrammarTest.php @@ -15,14 +15,14 @@ protected function tearDown(): void m::close(); } - public function testCreateDatabaseIfNotExists() + public function testCreateDatabase() { $grammar = new class extends Grammar {}; $this->expectException(LogicException::class); $this->expectExceptionMessage('This database driver does not support creating databases.'); - $grammar->compileCreateDatabaseIfNotExists('foo', m::mock(Connection::class)); + $grammar->compileCreateDatabase('foo', m::mock(Connection::class)); } public function testDropDatabaseIfExists() diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 419e2980b349..1eb17e0cc56a 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -1149,16 +1149,16 @@ public function testAddingComment() $this->assertSame("alter table `users` add `foo` varchar(255) not null comment 'Escape \\' when using words like it\\'s'", $statements[0]); } - public function testCreateDatabaseIfNotExists() + public function testCreateDatabase() { $connection = $this->getConnection(); $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_foo'); $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_foo'); - $statement = $this->getGrammar()->compileCreateDatabaseIfNotExists('my_database_a', $connection); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); $this->assertSame( - 'CREATE DATABASE IF NOT EXISTS `my_database_a` CHARACTER SET `utf8mb4_foo` COLLATE `utf8mb4_unicode_ci_foo`;', + 'CREATE DATABASE `my_database_a` DEFAULT CHARACTER SET `utf8mb4_foo` DEFAULT COLLATE `utf8mb4_unicode_ci_foo`', $statement ); @@ -1166,10 +1166,10 @@ public function testCreateDatabaseIfNotExists() $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4_bar'); $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci_bar'); - $statement = $this->getGrammar()->compileCreateDatabaseIfNotExists('my_database_b', $connection); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); $this->assertSame( - 'CREATE DATABASE IF NOT EXISTS `my_database_b` CHARACTER SET `utf8mb4_bar` COLLATE `utf8mb4_unicode_ci_bar`;', + 'CREATE DATABASE `my_database_b` DEFAULT CHARACTER SET `utf8mb4_bar` DEFAULT COLLATE `utf8mb4_unicode_ci_bar`', $statement ); } @@ -1179,14 +1179,14 @@ public function testDropDatabaseIfExists() $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); $this->assertSame( - 'DROP DATABASE IF EXISTS `my_database_a`;', + 'DROP DATABASE IF EXISTS `my_database_a`', $statement ); $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); $this->assertSame( - 'DROP DATABASE IF EXISTS `my_database_b`;', + 'DROP DATABASE IF EXISTS `my_database_b`', $statement ); } diff --git a/tests/Database/DatabaseMysqlBuilderTest.php b/tests/Database/DatabaseMysqlBuilderTest.php index df0a6703baa9..88d1b568b1f1 100644 --- a/tests/Database/DatabaseMysqlBuilderTest.php +++ b/tests/Database/DatabaseMysqlBuilderTest.php @@ -3,7 +3,7 @@ namespace Illuminate\Tests\Database; use Illuminate\Database\Connection; -use Illuminate\Database\Schema\Grammars\MysqlGrammar; +use Illuminate\Database\Schema\Grammars\MySqlGrammar; use Illuminate\Database\Schema\MySqlBuilder; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -15,30 +15,30 @@ protected function tearDown(): void m::close(); } - public function testCreateDatabaseIfNotExists() + public function testCreateDatabase() { - $grammar = new MysqlGrammar(); + $grammar = new MySqlGrammar(); $connection = m::mock(Connection::class); - $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8mb4'); - $connection->shouldReceive('getConfig')->once()->once()->with('collation')->andReturn('utf8mb4_unicode_ci'); + $connection->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8mb4'); + $connection->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8mb4_unicode_ci'); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'CREATE DATABASE IF NOT EXISTS `my_temporary_database` CHARACTER SET `utf8mb4` COLLATE `utf8mb4_unicode_ci`;' + 'CREATE DATABASE `my_temporary_database` DEFAULT CHARACTER SET `utf8mb4` DEFAULT COLLATE `utf8mb4_unicode_ci`' )->andReturn(true); $builder = new MySqlBuilder($connection); - $builder->createDatabaseIfNotExists('my_temporary_database'); + $builder->createDatabase('my_temporary_database'); } public function testDropDatabaseIfExists() { - $grammar = new MysqlGrammar(); + $grammar = new MySqlGrammar(); $connection = m::mock(Connection::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'DROP DATABASE IF EXISTS `my_database_a`;' + 'DROP DATABASE IF EXISTS `my_database_a`' )->andReturn(true); $builder = new MySqlBuilder($connection); diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php index d82d9d69ae5c..b22bfd7dc70e 100755 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ b/tests/Database/DatabaseSchemaBuilderTest.php @@ -16,7 +16,7 @@ protected function tearDown(): void m::close(); } - public function testCreateDatabaseIfNotExists() + public function testCreateDatabase() { $connection = m::mock(Connection::class); $grammar = m::mock(stdClass::class); @@ -26,7 +26,7 @@ public function testCreateDatabaseIfNotExists() $this->expectException(LogicException::class); $this->expectExceptionMessage('This database driver does not support creating databases.'); - $builder->createDatabaseIfNotExists('foo'); + $builder->createDatabase('foo'); } public function testDropDatabaseIfExists() From a546cf27264bf12aba5ab3719445fe6cc13647e5 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Jan 2021 14:56:08 +0100 Subject: [PATCH 06/39] Adds support to "Postgres" --- .../Schema/Grammars/PostgresGrammar.php | 31 +++++++++++++++ .../Database/Schema/PostgresBuilder.php | 26 +++++++++++++ .../Database/DatabasePostgresBuilderTest.php | 30 +++++++++++++++ .../DatabasePostgresSchemaGrammarTest.php | 38 +++++++++++++++++++ 4 files changed, 125 insertions(+) diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 8486e499d313..010d27ad35d4 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -35,6 +35,37 @@ class PostgresGrammar extends Grammar */ protected $fluentCommands = ['Comment']; + /** + * Compile a create database command. + * + * @param string $name + * @param \Illuminate\Database\Connection $connection + * @return string + */ + public function compileCreateDatabase($name, $connection) + { + return sprintf( + 'CREATE DATABASE %s ENCODING %s', + $this->wrapValue($name), + $this->wrapValue($connection->getConfig('charset')), + ); + } + + /** + * Compile a drop database if exists command. + * + * @param string $name + * @return string + */ + public function compileDropDatabaseIfExists($name) + { + return sprintf( + 'DROP DATABASE IF EXISTS %s', + $this->wrapValue($name) + ); + } + + /** * Compile the query to determine if a table exists. * diff --git a/src/Illuminate/Database/Schema/PostgresBuilder.php b/src/Illuminate/Database/Schema/PostgresBuilder.php index c249ccf02ebe..4e5e4aecda2e 100755 --- a/src/Illuminate/Database/Schema/PostgresBuilder.php +++ b/src/Illuminate/Database/Schema/PostgresBuilder.php @@ -4,6 +4,32 @@ class PostgresBuilder extends Builder { + /** + * Create a database in the schema. + * + * @param string $name + * @return bool + */ + public function createDatabase($name) + { + return $this->connection->statement( + $this->grammar->compileCreateDatabase($name, $this->connection) + ); + } + + /** + * Drop a database from the schema if the database exists. + * + * @param string $name + * @return bool + */ + public function dropDatabaseIfExists($name) + { + return $this->connection->statement( + $this->grammar->compileDropDatabaseIfExists($name) + ); + } + /** * Determine if the given table exists. * diff --git a/tests/Database/DatabasePostgresBuilderTest.php b/tests/Database/DatabasePostgresBuilderTest.php index 54e64e2e54e5..8f6bdfcc124b 100644 --- a/tests/Database/DatabasePostgresBuilderTest.php +++ b/tests/Database/DatabasePostgresBuilderTest.php @@ -16,6 +16,36 @@ protected function tearDown(): void m::close(); } + public function testCreateDatabase() + { + $grammar = new PostgresGrammar(); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'CREATE DATABASE "my_temporary_database" ENCODING "utf8"' + )->andReturn(true); + + $builder = $this->getBuilder($connection); + $builder->createDatabase('my_temporary_database'); + } + + public function testDropDatabaseIfExists() + { + $grammar = new PostgresGrammar(); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'DROP DATABASE IF EXISTS "my_database_a"' + )->andReturn(true); + + $builder = $this->getBuilder($connection); + + $builder->dropDatabaseIfExists('my_database_a'); + } + /** * Ensure that when the reference is unqualified (i.e., does not contain a * database name or a schema), and the search_path is empty, the database diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php index f044eb9b6667..f7d08453918b 100755 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ b/tests/Database/DatabasePostgresSchemaGrammarTest.php @@ -997,6 +997,44 @@ public function testAddingMultiPolygon() $this->assertSame('alter table "geo" add column "coordinates" geography(multipolygon, 4326) not null', $statements[0]); } + public function testCreateDatabase() + { + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_foo'); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); + + $this->assertSame( + 'CREATE DATABASE "my_database_a" ENCODING "utf8_foo"', + $statement + ); + + $connection = $this->getConnection(); + $connection->shouldReceive('getConfig')->once()->once()->with('charset')->andReturn('utf8_bar'); + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); + + $this->assertSame( + 'CREATE DATABASE "my_database_b" ENCODING "utf8_bar"', + $statement + ); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'DROP DATABASE IF EXISTS "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'DROP DATABASE IF EXISTS "my_database_b"', + $statement + ); + } + public function testDropAllTablesEscapesTableNames() { $statement = $this->getGrammar()->compileDropAllTables(['alpha', 'beta', 'gamma']); From b0b6243e1d70fcf5b7c6e8cc15d1726dd8e59169 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 5 Jan 2021 14:57:40 +0100 Subject: [PATCH 07/39] Fixes CS --- src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 010d27ad35d4..0817998ec4d8 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -65,7 +65,6 @@ public function compileDropDatabaseIfExists($name) ); } - /** * Compile the query to determine if a table exists. * From 5697ee4f50c290e33b6fafac7ec09ec61ba115f3 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 18:26:37 +0100 Subject: [PATCH 08/39] Uses a database per process regardless of the used database testing trait --- .../Foundation/Testing/RefreshDatabase.php | 76 +------- .../Testing/RefreshDatabaseState.php | 7 - .../Foundation/Testing/TestCase.php | 3 + src/Illuminate/Support/Facades/Storage.php | 3 +- src/Illuminate/Support/Facades/Testing.php | 22 --- .../Testing/ParallelConsoleOutput.php | 61 ++++++ src/Illuminate/Testing/ParallelRunner.php | 136 +++++++++++++ src/Illuminate/Testing/ParallelTesting.php | 181 ++++++++++++++++++ src/Illuminate/Testing/Testing.php | 93 --------- tests/Testing/ParallelTestingTest.php | 44 +++++ tests/Testing/TestingTest.php | 84 -------- 11 files changed, 430 insertions(+), 280 deletions(-) delete mode 100644 src/Illuminate/Support/Facades/Testing.php create mode 100644 src/Illuminate/Testing/ParallelConsoleOutput.php create mode 100644 src/Illuminate/Testing/ParallelRunner.php create mode 100644 src/Illuminate/Testing/ParallelTesting.php delete mode 100644 src/Illuminate/Testing/Testing.php create mode 100644 tests/Testing/ParallelTestingTest.php delete mode 100644 tests/Testing/TestingTest.php diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabase.php b/src/Illuminate/Foundation/Testing/RefreshDatabase.php index 501eec7c193c..f62fad83c559 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabase.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabase.php @@ -3,8 +3,6 @@ namespace Illuminate\Foundation\Testing; use Illuminate\Contracts\Console\Kernel; -use Illuminate\Support\Facades\Schema; -use Illuminate\Support\Facades\Testing; trait RefreshDatabase { @@ -15,15 +13,9 @@ trait RefreshDatabase */ public function refreshDatabase() { - if ($this->usingInMemoryDatabase()) { - return $this->refreshInMemoryDatabase(); - } - - Testing::whenRunningInParallel(function () { - $this->switchToTemporaryDatabase(); - }); - - $this->refreshTestDatabase(); + $this->usingInMemoryDatabase() + ? $this->refreshInMemoryDatabase() + : $this->refreshTestDatabase(); } /** @@ -38,68 +30,6 @@ protected function usingInMemoryDatabase() return config("database.connections.$default.database") === ':memory:'; } - /** - * Switch to the temporary test database. - * - * @return void - */ - protected function switchToTemporaryDatabase() - { - $default = config('database.default'); - - config()->set( - "database.connections.{$default}.database", - RefreshDatabaseState::$temporaryDatabase, - ); - } - - /** - * Creates a temporary database, if needed. - * - * @beforeClass - * - * @return void - */ - public static function setUpTemporaryDatabase() - { - tap(new static(), function ($testCase) { - $testCase->refreshApplication(); - - if ($testCase->usingInMemoryDatabase()) { - return; - } - - Testing::whenRunningInParallel(function () use ($testCase) { - $name = $testCase->getConnection()->getConfig('database'); - - RefreshDatabaseState::$temporaryDatabase = Testing::addTokenIfNeeded($name); - - Schema::dropDatabaseIfExists(RefreshDatabaseState::$temporaryDatabase); - Schema::createDatabase(RefreshDatabaseState::$temporaryDatabase); - }); - })->app->flush(); - } - - /** - * Drop the temporary database, if any. - * - * @afterClass - * - * @return void - */ - public static function tearDownTemporaryDatabase() - { - if (RefreshDatabaseState::$temporaryDatabase) { - tap(new static(), function ($testCase) { - $testCase->refreshApplication(); - - Schema::dropDatabaseIfExists( - RefreshDatabaseState::$temporaryDatabase, - ); - })->app->flush(); - } - } - /** * Refresh the in-memory database. * diff --git a/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php b/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php index fab4f0cff52b..1f33087396f6 100644 --- a/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php +++ b/src/Illuminate/Foundation/Testing/RefreshDatabaseState.php @@ -10,11 +10,4 @@ class RefreshDatabaseState * @var bool */ public static $migrated = false; - - /** - * The temporary database name, if any. - * - * @var string|null - */ - public static $temporaryDatabase; } diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index b32202517ceb..6beebcd56a63 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -7,6 +7,7 @@ use Illuminate\Console\Application as Artisan; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Facade; +use Illuminate\Testing\ParallelTesting; use Illuminate\Support\Str; use Mockery; use Mockery\Exception\InvalidCountException; @@ -83,6 +84,8 @@ protected function setUp(): void $this->refreshApplication(); } + $this->app[ParallelTesting::class]->setUpIfNeeded($this); + $this->setUpTraits(); foreach ($this->afterApplicationCreatedCallbacks as $callback) { diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index a1f4e2ec32cb..9d0f55daeb10 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -3,6 +3,7 @@ namespace Illuminate\Support\Facades; use Illuminate\Filesystem\Filesystem; +use Illuminate\Testing\ParallelTesting; /** * @method static \Illuminate\Contracts\Filesystem\Filesystem assertExists(string|array $path) @@ -54,7 +55,7 @@ public static function fake($disk = null, array $config = []) $root = storage_path('framework/testing/disks/'.$disk) ); - $root = Testing::addTokenIfNeeded($root); + $root = static::$app[ParallelTesting::class]->addTokenIfNeeded($root); static::set($disk, $fake = static::createLocalDriver(array_merge($config, [ 'root' => $root, diff --git a/src/Illuminate/Support/Facades/Testing.php b/src/Illuminate/Support/Facades/Testing.php deleted file mode 100644 index 614104fa5771..000000000000 --- a/src/Illuminate/Support/Facades/Testing.php +++ /dev/null @@ -1,22 +0,0 @@ -getVerbosity(), + $output->isDecorated(), + $output->getFormatter(), + ); + + $this->output = $output; + } + + /** + * Writes a message to the output. + * + * @param string|iterable $messages + * @param bool $newline + * @param int $options + * @return void + */ + public function write($messages, bool $newline = false, int $options = 0) + { + $messages = collect($messages)->filter(function ($message) { + return ! Str::contains($message, $this->ignore); + }); + + $this->output->write($messages->toArray(), $newline, $options); + } +} diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php new file mode 100644 index 000000000000..a8d953363081 --- /dev/null +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -0,0 +1,136 @@ +options = $options; + + if ($output instanceof ConsoleOutput) { + $output = new ParallelConsoleOutput($output); + } + + $this->runner = new WrapperRunner($options, $output); + } + + /** + * Set the application resolver callback. + * + * @param \Closure|null $resolver + * @return void + */ + public static function resolveApplicationUsing($resolver) + { + static::$applicationResolver = $resolver; + } + + /** + * Runs the test suite. + * + * @return void + */ + public function run(): void + { + try { + $this->runner->run(); + } catch (Throwable $e) { + throw $e; + } finally { + $this->forEachProcess(function ($app) { + $app[ParallelTesting::class]->beforeProcessDestroyed(); + }); + } + } + + /** + * Returns the highest exit code encountered throughout the course of test execution. + * + * @return int + */ + public function getExitCode(): int + { + return $this->runner->getExitCode(); + } + + /** + * Apply the given callback for each process. + * + * @param callable $callback + * @return void + */ + protected function forEachProcess($callback) + { + collect(range(1, $this->options->processes()))->each(function ($token) use ($callback) { + tap($this->createApplication(), function ($app) use ($callback, $token) { + ParallelTesting::resolveTokenUsing(function () use ($token) { + return $token; + }); + + $callback($app); + })->flush(); + }); + } + + /** + * Creates the application. + * + * @return \Illuminate\Contracts\Foundation\Application + */ + protected function createApplication() + { + $applicationResolver = static::$applicationResolver ?: function () { + $applicationCreator = new class { + use \Tests\CreatesApplication; + }; + + return $applicationCreator->createApplication(); + }; + + return call_user_func($applicationResolver); + } +} diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php new file mode 100644 index 000000000000..102ebc588e64 --- /dev/null +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -0,0 +1,181 @@ +whenNotUsingInMemoryDatabase(function ($database) { + Schema::dropDatabaseIfExists( + $this->addTokenIfNeeded($database), + ); + }); + } + + /** + * SetUp the given test case for parallel testing, if needed. + * + * @param \Illuminate\Foundation\Testing\TestCase $testCase + * + * @return void + */ + public function setUpIfNeeded($testCase) + { + $this->whenRunningInParallel(function () use ($testCase) { + $uses = array_flip(class_uses_recursive(get_class($testCase))); + + if (Arr::hasAny($uses, [ + Testing\DatabaseMigrations::class, + Testing\DatabaseTransactions::class, + Testing\RefreshDatabase::class + ])) { + $this->switchToTemporaryDatabase(); + } + }); + } + + /** + * Adds an unique test token to the given string, if needed. + * + * @return string + */ + public function addTokenIfNeeded($string) + { + if (! $this->inParallel()) { + return $string; + } + + return "{$string}_test_{$this->token()}"; + } + + /** + * Apply the callback when tests are not using in memory database. + * + * @param callable $callback + * @return void + */ + protected function whenNotUsingInMemoryDatabase($callback) + { + $database = DB::getConfig('database'); + + if ($database != ':memory:') { + $callback($database); + } + } + + /** + * Apply the callback if tests are running in parallel. + * + * @param callable $callback + * @return void + */ + protected function whenRunningInParallel($callback) + { + if ($this->inParallel()) { + $callback(); + } + } + + /** + * Switch to the temporary database. + * + * @return void + */ + protected function switchToTemporaryDatabase() + { + $this->whenNotUsingInMemoryDatabase(function ($database) { + $database = $this->ensureTemporaryDatabaseExists($database); + + DB::purge(); + + $default = config('database.default'); + + config()->set( + "database.connections.{$default}.database", + $database, + ); + }); + } + + /** + * Ensure a temporary database exists + * + * @param string $database + * + * @return string + */ + protected function ensureTemporaryDatabaseExists($database) + { + if (! static::$temporaryDatabase) { + static::$temporaryDatabase = $this->addTokenIfNeeded($database); + + Schema::dropDatabaseIfExists(static::$temporaryDatabase); + Schema::createDatabase(static::$temporaryDatabase); + + $this->switchToTemporaryDatabase(); + + Artisan::call('migrate:fresh'); + } + + return static::$temporaryDatabase; + } + + /** + * Indicates if the current tests are been run in Parallel. + * + * @return bool + */ + protected function inParallel() + { + return ! empty($_SERVER['LARAVEL_PARALLEL_TESTING']) && $this->token(); + } + + /** + * Gets an unique test token. + * + * @return int|false + */ + protected function token() + { + return static::$tokenResolver + ? call_user_func(static::$tokenResolver) + : ($_SERVER['TEST_TOKEN'] ?? false); + } +} diff --git a/src/Illuminate/Testing/Testing.php b/src/Illuminate/Testing/Testing.php deleted file mode 100644 index c19839e985e1..000000000000 --- a/src/Illuminate/Testing/Testing.php +++ /dev/null @@ -1,93 +0,0 @@ -app = $app; - } - - /** - * Adds an unique test token to the given string, if needed. - * - * @return string - */ - public function addTokenIfNeeded($string) - { - if (! $this->inParallel()) { - return $string; - } - - return "{$string}_test_{$this->token()}"; - } - - /** - * Apply the callback if tests are running in parallel. - * - * @param callable $callback - * @return void - */ - public function whenRunningInParallel($callback) - { - if ($this->inParallel()) { - $callback(); - } - } - - /** - * Indicates if the current tests are been run in Parallel. - * - * @return bool - */ - protected function inParallel() - { - return $this->app->runningUnitTests() && ! empty($_SERVER['LARAVEL_PARALLEL_TESTING']) && $this->token(); - } - - /** - * Gets an unique test token. - * - * @return int|false - */ - protected function token() - { - return static::$tokenResolver - ? call_user_func(static::$tokenResolver) - : ($_SERVER['TEST_TOKEN'] ?? false); - } - - /** - * Set with token resolver callback. - * - * @param \Closure|null $resolver - * @return void - */ - public static function tokenResolver($resolver) - { - static::$tokenResolver = $resolver; - } -} diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php new file mode 100644 index 000000000000..5a8562076c06 --- /dev/null +++ b/tests/Testing/ParallelTestingTest.php @@ -0,0 +1,44 @@ +assertSame( + 'my_local_storage', + (new ParallelTesting())->addTokenIfNeeded('my_local_storage') + ); + + ParallelTesting::resolveTokenUsing(function () { + return 1; + }); + + $this->assertSame( + 'my_local_storage_test_1', + (new ParallelTesting())->addTokenIfNeeded('my_local_storage') + ); + } + + public function tearDown(): void + { + parent::tearDown(); + + m::close(); + ParallelTesting::resolveTokenUsing(null); + unset($_SERVER['LARAVEL_PARALLEL_TESTING']); + } +} diff --git a/tests/Testing/TestingTest.php b/tests/Testing/TestingTest.php deleted file mode 100644 index 76668b3e54e5..000000000000 --- a/tests/Testing/TestingTest.php +++ /dev/null @@ -1,84 +0,0 @@ -shouldReceive('runningUnitTests') - ->once() - ->andReturn(false); - - $testing = new Testing($app); - $run = false; - - $testing->whenRunningInParallel(function () use (&$run) { - $run = true; - }); - $this->assertFalse($run); - - $app->shouldReceive('runningUnitTests') - ->once() - ->andReturn(true); - - Testing::tokenResolver(function () { - return 1; - }); - - $testing->whenRunningInParallel(function () use (&$run) { - $run = true; - }); - $this->assertTrue($run); - } - - public function testAddTokenIfNeeded() - { - $app = m::mock(Application::class); - - $app->shouldReceive('runningUnitTests') - ->once() - ->andReturn(false); - - $this->assertSame( - 'my_local_storage', - (new Testing($app))->addTokenIfNeeded('my_local_storage') - ); - - $app->shouldReceive('runningUnitTests') - ->once() - ->andReturn(true); - - Testing::tokenResolver(function () { - return 1; - }); - - $this->assertSame( - 'my_local_storage_test_1', - (new Testing($app))->addTokenIfNeeded('my_local_storage') - ); - } - - public function tearDown(): void - { - parent::tearDown(); - - m::close(); - Testing::tokenResolver(null); - unset($_SERVER['LARAVEL_PARALLEL_TESTING']); - } -} From 07d9cb3a3ce1de25b4e4f2b45d1142105d3bcf02 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 17:29:05 +0000 Subject: [PATCH 09/39] Apply fixes from StyleCI --- src/Illuminate/Foundation/Testing/TestCase.php | 2 +- src/Illuminate/Testing/ParallelConsoleOutput.php | 1 - src/Illuminate/Testing/ParallelRunner.php | 2 -- src/Illuminate/Testing/ParallelTesting.php | 6 +++--- tests/Testing/ParallelTestingTest.php | 1 - 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 6beebcd56a63..fd74d7b6fe3b 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -7,8 +7,8 @@ use Illuminate\Console\Application as Artisan; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Facade; -use Illuminate\Testing\ParallelTesting; use Illuminate\Support\Str; +use Illuminate\Testing\ParallelTesting; use Mockery; use Mockery\Exception\InvalidCountException; use PHPUnit\Framework\TestCase as BaseTestCase; diff --git a/src/Illuminate/Testing/ParallelConsoleOutput.php b/src/Illuminate/Testing/ParallelConsoleOutput.php index 41cc8e5567fd..7444be3b92d7 100644 --- a/src/Illuminate/Testing/ParallelConsoleOutput.php +++ b/src/Illuminate/Testing/ParallelConsoleOutput.php @@ -2,7 +2,6 @@ namespace Illuminate\Testing; -use Illuminate\Support\Arr; use Illuminate\Support\Str; use Symfony\Component\Console\Output\ConsoleOutput; diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index a8d953363081..3dae083034a2 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -2,8 +2,6 @@ namespace Illuminate\Testing; -use Illuminate\Contracts\Console\Kernel; -use Illuminate\Support\Facades\Schema; use ParaTest\Runners\PHPUnit\Options; use ParaTest\Runners\PHPUnit\RunnerInterface; use ParaTest\Runners\PHPUnit\WrapperRunner; diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 102ebc588e64..3efdfa670d10 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -5,8 +5,8 @@ use Illuminate\Foundation\Testing; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; class ParallelTesting { @@ -64,7 +64,7 @@ public function setUpIfNeeded($testCase) if (Arr::hasAny($uses, [ Testing\DatabaseMigrations::class, Testing\DatabaseTransactions::class, - Testing\RefreshDatabase::class + Testing\RefreshDatabase::class, ])) { $this->switchToTemporaryDatabase(); } @@ -135,7 +135,7 @@ protected function switchToTemporaryDatabase() } /** - * Ensure a temporary database exists + * Ensure a temporary database exists. * * @param string $database * diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 5a8562076c06..2605d3c0984d 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Foundation; -use Illuminate\Contracts\Foundation\Application; use Illuminate\Testing\ParallelTesting; use Mockery as m; use PHPUnit\Framework\TestCase; From 83f648d73eb1da1540286b23ff8ae4fc598ab820 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 19:24:42 +0100 Subject: [PATCH 10/39] Removes unused catch --- src/Illuminate/Testing/ParallelRunner.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index a8d953363081..0d813cc105a1 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -78,8 +78,6 @@ public function run(): void { try { $this->runner->run(); - } catch (Throwable $e) { - throw $e; } finally { $this->forEachProcess(function ($app) { $app[ParallelTesting::class]->beforeProcessDestroyed(); From 66d2dcca68c80a273a895417554d0d6487a438fb Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 23:38:21 +0100 Subject: [PATCH 11/39] Refactors into service providers --- .../Providers/FoundationServiceProvider.php | 2 + .../Foundation/Testing/TestCase.php | 4 +- .../Support/Facades/ParallelTesting.php | 24 +++ src/Illuminate/Support/Facades/Storage.php | 6 +- .../Testing/Concerns/TemporaryDatabases.php | 120 ++++++++++++++ src/Illuminate/Testing/ParallelRunner.php | 3 +- src/Illuminate/Testing/ParallelTesting.php | 147 ++++++------------ .../ParallelTestingServiceProvider.php | 38 +++++ tests/Testing/ParallelTestingTest.php | 17 +- 9 files changed, 248 insertions(+), 113 deletions(-) create mode 100644 src/Illuminate/Support/Facades/ParallelTesting.php create mode 100644 src/Illuminate/Testing/Concerns/TemporaryDatabases.php create mode 100644 src/Illuminate/Testing/ParallelTestingServiceProvider.php diff --git a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php index 2f39afd43d36..f5ffb33658f5 100644 --- a/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Http\Request; use Illuminate\Support\AggregateServiceProvider; use Illuminate\Support\Facades\URL; +use Illuminate\Testing\ParallelTestingServiceProvider; use Illuminate\Validation\ValidationException; class FoundationServiceProvider extends AggregateServiceProvider @@ -16,6 +17,7 @@ class FoundationServiceProvider extends AggregateServiceProvider */ protected $providers = [ FormRequestServiceProvider::class, + ParallelTestingServiceProvider::class, ]; /** diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index fd74d7b6fe3b..0c50d980bfed 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -7,8 +7,8 @@ use Illuminate\Console\Application as Artisan; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Facade; +use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Str; -use Illuminate\Testing\ParallelTesting; use Mockery; use Mockery\Exception\InvalidCountException; use PHPUnit\Framework\TestCase as BaseTestCase; @@ -84,7 +84,7 @@ protected function setUp(): void $this->refreshApplication(); } - $this->app[ParallelTesting::class]->setUpIfNeeded($this); + ParallelTesting::callSetUpCallbacks($this); $this->setUpTraits(); diff --git a/src/Illuminate/Support/Facades/ParallelTesting.php b/src/Illuminate/Support/Facades/ParallelTesting.php new file mode 100644 index 000000000000..5a988b178e3b --- /dev/null +++ b/src/Illuminate/Support/Facades/ParallelTesting.php @@ -0,0 +1,24 @@ +addTokenIfNeeded($root); + if ($token = ParallelTesting::token()) { + $root = "{$root}_{$token}"; + } static::set($disk, $fake = static::createLocalDriver(array_merge($config, [ 'root' => $root, diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php new file mode 100644 index 000000000000..9b52d346dde4 --- /dev/null +++ b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php @@ -0,0 +1,120 @@ +useTemporaryDatabase(); + } + }); + + ParallelTesting::beforeProcessDestroyed(function () { + $this->whenNotUsingInMemoryDatabase(function ($database) { + Schema::dropDatabaseIfExists( + $this->temporaryDatabaseName($database) + ); + }); + }); + } + + /** + * Use a temporary database. + * + * @return void + */ + protected function useTemporaryDatabase() + { + $this->whenNotUsingInMemoryDatabase(function ($database) { + $database = $this->ensureTemporaryDatabaseExists($database); + + DB::purge(); + + $default = config('database.default'); + + config()->set( + "database.connections.{$default}.database", + $database, + ); + }); + } + + /** + * Ensure a temporary database exists. + * + * @param string $database + * + * @return string + */ + protected function ensureTemporaryDatabaseExists($database) + { + if (! static::$temporaryDatabase) { + static::$temporaryDatabase = $this->temporaryDatabaseName($database); + + Schema::dropDatabaseIfExists(static::$temporaryDatabase); + Schema::createDatabase(static::$temporaryDatabase); + + $this->useTemporaryDatabase(); + + Artisan::call('migrate:fresh'); + } + + return static::$temporaryDatabase; + } + + /** + * Apply the callback when tests are not using in memory database. + * + * @param callable $callback + * @return void + */ + protected function whenNotUsingInMemoryDatabase($callback) + { + $database = DB::getConfig('database'); + + if ($database != ':memory:') { + $callback($database); + } + } + + /** + * Returns the temporary database name. + * + * @return string + */ + protected function temporaryDatabaseName($database) + { + $token = ParallelTesting::token(); + + return "{$database}_test_{$token}"; + } +} diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index b4fde8181d2e..ead73e36d026 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -7,6 +7,7 @@ use ParaTest\Runners\PHPUnit\WrapperRunner; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; +use Illuminate\Support\Facades\ParallelTesting; class ParallelRunner implements RunnerInterface { @@ -78,7 +79,7 @@ public function run(): void $this->runner->run(); } finally { $this->forEachProcess(function ($app) { - $app[ParallelTesting::class]->beforeProcessDestroyed(); + ParallelTesting::callBeforeProcessDestroyedCallbacks(); }); } } diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 3efdfa670d10..e0f2598c0415 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -15,146 +15,111 @@ class ParallelTesting * * @var \Closure|null */ - protected static $tokenResolver; + protected $tokenResolver; /** - * The temporary database name, if any. + * All of the registered "setUp" callbacks. * - * @var string|null + * @var array */ - protected static $temporaryDatabase; + protected $setUpCallbacks = []; /** - * Set a callback that should be used when resolving tokens. + * All of the registered "beforeProcessDestroyed" callbacks. * - * @param \Closure|null $callback - * @return void + * @var array */ - public static function resolveTokenUsing($resolver) - { - static::$tokenResolver = $resolver; - } + protected $beforeProcessDestroyedCallbacks = []; /** - * Runs before the process gets destroyed. + * Set a callback that should be used when resolving the unique process token. * + * @param \Closure|null $callback * @return void */ - public function beforeProcessDestroyed() + public function resolveTokenUsing($resolver) { - $this->whenNotUsingInMemoryDatabase(function ($database) { - Schema::dropDatabaseIfExists( - $this->addTokenIfNeeded($database), - ); - }); + $this->tokenResolver = $resolver; } /** - * SetUp the given test case for parallel testing, if needed. - * - * @param \Illuminate\Foundation\Testing\TestCase $testCase + * Register a callback to run before process gets destroyed. * - * @return void + * @param callable $callback + * @return $this */ - public function setUpIfNeeded($testCase) + public function beforeProcessDestroyed($callback) { - $this->whenRunningInParallel(function () use ($testCase) { - $uses = array_flip(class_uses_recursive(get_class($testCase))); + $this->beforeProcessDestroyedCallbacks[] = $callback; - if (Arr::hasAny($uses, [ - Testing\DatabaseMigrations::class, - Testing\DatabaseTransactions::class, - Testing\RefreshDatabase::class, - ])) { - $this->switchToTemporaryDatabase(); - } - }); + return $this; } /** - * Adds an unique test token to the given string, if needed. + * Register a callback to run on test setup. * - * @return string + * @param callable $callback + * @return $this */ - public function addTokenIfNeeded($string) + public function setUp($callback) { - if (! $this->inParallel()) { - return $string; - } + $this->setUpCallbacks[] = $callback; - return "{$string}_test_{$this->token()}"; + return $this; } /** - * Apply the callback when tests are not using in memory database. + * Call all of the "beforeProcessDestroyed" callbacks. * - * @param callable $callback * @return void */ - protected function whenNotUsingInMemoryDatabase($callback) + public function callBeforeProcessDestroyedCallbacks() { - $database = DB::getConfig('database'); - - if ($database != ':memory:') { - $callback($database); - } + $this->whenRunningInParallel(function () { + foreach ($this->beforeProcessDestroyedCallbacks as $callback) { + $callback(); + } + }); } /** - * Apply the callback if tests are running in parallel. + * Call all of the "setUp" callbacks. * - * @param callable $callback + * @param \Illuminate\Foundation\Testing\TestCase $testCase * @return void */ - protected function whenRunningInParallel($callback) + public function callSetUpCallbacks($testCase) { - if ($this->inParallel()) { - $callback(); - } + $this->whenRunningInParallel(function () use ($testCase) { + foreach ($this->setUpCallbacks as $callback) { + $callback($testCase); + } + }); } /** - * Switch to the temporary database. + * Gets an unique test token. * - * @return void + * @return int|false */ - protected function switchToTemporaryDatabase() + public function token() { - $this->whenNotUsingInMemoryDatabase(function ($database) { - $database = $this->ensureTemporaryDatabaseExists($database); - - DB::purge(); - - $default = config('database.default'); - - config()->set( - "database.connections.{$default}.database", - $database, - ); - }); + return $token = $this->tokenResolver + ? call_user_func($this->tokenResolver) + : ($_SERVER['TEST_TOKEN'] ?? false); } /** - * Ensure a temporary database exists. - * - * @param string $database + * Apply the callback if tests are running in parallel. * - * @return string + * @param callable $callback + * @return void */ - protected function ensureTemporaryDatabaseExists($database) + protected function whenRunningInParallel($callback) { - if (! static::$temporaryDatabase) { - static::$temporaryDatabase = $this->addTokenIfNeeded($database); - - Schema::dropDatabaseIfExists(static::$temporaryDatabase); - Schema::createDatabase(static::$temporaryDatabase); - - $this->switchToTemporaryDatabase(); - - Artisan::call('migrate:fresh'); + if ($this->inParallel()) { + $callback(); } - - return static::$temporaryDatabase; } /** @@ -166,16 +131,4 @@ protected function inParallel() { return ! empty($_SERVER['LARAVEL_PARALLEL_TESTING']) && $this->token(); } - - /** - * Gets an unique test token. - * - * @return int|false - */ - protected function token() - { - return static::$tokenResolver - ? call_user_func(static::$tokenResolver) - : ($_SERVER['TEST_TOKEN'] ?? false); - } } diff --git a/src/Illuminate/Testing/ParallelTestingServiceProvider.php b/src/Illuminate/Testing/ParallelTestingServiceProvider.php new file mode 100644 index 000000000000..a881a652347c --- /dev/null +++ b/src/Illuminate/Testing/ParallelTestingServiceProvider.php @@ -0,0 +1,38 @@ +app->runningUnitTests()) { + $this->bootTemporaryDatabases(); + } + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + if ($this->app->runningUnitTests()) { + $this->app->singleton(ParallelTesting::class, function () { + return new ParallelTesting(); + }); + } + } +} diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 2605d3c0984d..2c1cab07dc5c 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -15,21 +15,17 @@ protected function setUp(): void $_SERVER['LARAVEL_PARALLEL_TESTING'] = 1; } - public function testAddTokenIfNeeded() + public function testToken() { - $this->assertSame( - 'my_local_storage', - (new ParallelTesting())->addTokenIfNeeded('my_local_storage') - ); + $parallelTesting = new ParallelTesting(); - ParallelTesting::resolveTokenUsing(function () { + $this->assertFalse($parallelTesting->token()); + + $parallelTesting->resolveTokenUsing(function () { return 1; }); - $this->assertSame( - 'my_local_storage_test_1', - (new ParallelTesting())->addTokenIfNeeded('my_local_storage') - ); + $this->assertSame(1, $parallelTesting->token()); } public function tearDown(): void @@ -37,7 +33,6 @@ public function tearDown(): void parent::tearDown(); m::close(); - ParallelTesting::resolveTokenUsing(null); unset($_SERVER['LARAVEL_PARALLEL_TESTING']); } } From 70a231e3de6abda0d6c5c09b726546cb9d8d9a93 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 22:39:52 +0000 Subject: [PATCH 12/39] Apply fixes from StyleCI --- src/Illuminate/Support/Facades/Storage.php | 1 - src/Illuminate/Testing/Concerns/TemporaryDatabases.php | 3 +-- src/Illuminate/Testing/ParallelRunner.php | 2 +- src/Illuminate/Testing/ParallelTesting.php | 6 ------ 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index c182ee1909c7..93b4a4357f97 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -3,7 +3,6 @@ namespace Illuminate\Support\Facades; use Illuminate\Filesystem\Filesystem; -use Illuminate\Support\Facades\ParallelTesting; /** * @method static \Illuminate\Contracts\Filesystem\Filesystem assertExists(string|array $path) diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php index 9b52d346dde4..3e2bdd949d30 100644 --- a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php +++ b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php @@ -6,8 +6,8 @@ use Illuminate\Support\Arr; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\ParallelTesting; +use Illuminate\Support\Facades\Schema; trait TemporaryDatabases { @@ -26,7 +26,6 @@ trait TemporaryDatabases protected function bootTemporaryDatabases() { ParallelTesting::setUp(function ($testCase) { - $uses = array_flip(class_uses_recursive(get_class($testCase))); if (Arr::hasAny($uses, [ diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index ead73e36d026..2b373758090c 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -2,12 +2,12 @@ namespace Illuminate\Testing; +use Illuminate\Support\Facades\ParallelTesting; use ParaTest\Runners\PHPUnit\Options; use ParaTest\Runners\PHPUnit\RunnerInterface; use ParaTest\Runners\PHPUnit\WrapperRunner; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; -use Illuminate\Support\Facades\ParallelTesting; class ParallelRunner implements RunnerInterface { diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index e0f2598c0415..e685ecb7b281 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -2,12 +2,6 @@ namespace Illuminate\Testing; -use Illuminate\Foundation\Testing; -use Illuminate\Support\Arr; -use Illuminate\Support\Facades\Artisan; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Schema; - class ParallelTesting { /** From 2f60c87d976653ab27ff5e1cefdfa0fdfba7d579 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Thu, 7 Jan 2021 23:45:16 +0100 Subject: [PATCH 13/39] Removes token resolver from the public API --- src/Illuminate/Support/Facades/ParallelTesting.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/ParallelTesting.php b/src/Illuminate/Support/Facades/ParallelTesting.php index 5a988b178e3b..2a9229ed84d6 100644 --- a/src/Illuminate/Support/Facades/ParallelTesting.php +++ b/src/Illuminate/Support/Facades/ParallelTesting.php @@ -4,7 +4,6 @@ /** * @method static void beforeProcessDestroyed(callable $callback) - * @method static void resolveTokenUsing(callable $callback) * @method static void setUp(callable $callback) * @method static string token() * From 208bf65f53f63140a1cde0b57c400846fcbfa587 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 00:05:01 +0100 Subject: [PATCH 14/39] Renames Parallel Testing register callbacks --- .../Foundation/Testing/TestCase.php | 2 +- .../Support/Facades/ParallelTesting.php | 4 +- .../Testing/Concerns/TemporaryDatabases.php | 4 +- src/Illuminate/Testing/ParallelRunner.php | 4 +- src/Illuminate/Testing/ParallelTesting.php | 40 +++++++++---------- 5 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 0c50d980bfed..3e48d6032f73 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -84,7 +84,7 @@ protected function setUp(): void $this->refreshApplication(); } - ParallelTesting::callSetUpCallbacks($this); + ParallelTesting::callSetUpTestCaseCallbacks($this); $this->setUpTraits(); diff --git a/src/Illuminate/Support/Facades/ParallelTesting.php b/src/Illuminate/Support/Facades/ParallelTesting.php index 2a9229ed84d6..eb4873677b05 100644 --- a/src/Illuminate/Support/Facades/ParallelTesting.php +++ b/src/Illuminate/Support/Facades/ParallelTesting.php @@ -3,8 +3,8 @@ namespace Illuminate\Support\Facades; /** - * @method static void beforeProcessDestroyed(callable $callback) - * @method static void setUp(callable $callback) + * @method static void tearDownProcess(callable $callback) + * @method static void setUpTestCase(callable $callback) * @method static string token() * * @see \Illuminate\Testing\ParallelTesting diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php index 9b52d346dde4..107ffb9fc28c 100644 --- a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php +++ b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php @@ -25,7 +25,7 @@ trait TemporaryDatabases */ protected function bootTemporaryDatabases() { - ParallelTesting::setUp(function ($testCase) { + ParallelTesting::setUpTestCase(function ($testCase) { $uses = array_flip(class_uses_recursive(get_class($testCase))); @@ -38,7 +38,7 @@ protected function bootTemporaryDatabases() } }); - ParallelTesting::beforeProcessDestroyed(function () { + ParallelTesting::tearDownProcess(function () { $this->whenNotUsingInMemoryDatabase(function ($database) { Schema::dropDatabaseIfExists( $this->temporaryDatabaseName($database) diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index ead73e36d026..5498d84a8b15 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -78,8 +78,8 @@ public function run(): void try { $this->runner->run(); } finally { - $this->forEachProcess(function ($app) { - ParallelTesting::callBeforeProcessDestroyedCallbacks(); + $this->forEachProcess(function () { + ParallelTesting::callTearDownProcessCallbacks(); }); } } diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index e0f2598c0415..c378b2c05757 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -18,18 +18,18 @@ class ParallelTesting protected $tokenResolver; /** - * All of the registered "setUp" callbacks. + * All of the registered "setUp" test case callbacks. * * @var array */ - protected $setUpCallbacks = []; + protected $setUpTestCaseCallbacks = []; /** - * All of the registered "beforeProcessDestroyed" callbacks. + * All of the registered "tearDown" process callbacks. * * @var array */ - protected $beforeProcessDestroyedCallbacks = []; + protected $tearDownProcessCallbacks = []; /** * Set a callback that should be used when resolving the unique process token. @@ -43,55 +43,51 @@ public function resolveTokenUsing($resolver) } /** - * Register a callback to run before process gets destroyed. + * Register a "setUp" test case callback. * * @param callable $callback - * @return $this + * @return void */ - public function beforeProcessDestroyed($callback) + public function setUpTestCase($callback) { - $this->beforeProcessDestroyedCallbacks[] = $callback; - - return $this; + $this->setUpTestCaseCallbacks[] = $callback; } /** - * Register a callback to run on test setup. + * Register a "tearDown" process callback. * * @param callable $callback - * @return $this + * @return void */ - public function setUp($callback) + public function tearDownProcess($callback) { - $this->setUpCallbacks[] = $callback; - - return $this; + $this->tearDownProcessCallbacks[] = $callback; } /** - * Call all of the "beforeProcessDestroyed" callbacks. + * Call all of the "tearDown" process callbacks. * * @return void */ - public function callBeforeProcessDestroyedCallbacks() + public function callTearDownProcessCallbacks() { $this->whenRunningInParallel(function () { - foreach ($this->beforeProcessDestroyedCallbacks as $callback) { + foreach ($this->tearDownProcessCallbacks as $callback) { $callback(); } }); } /** - * Call all of the "setUp" callbacks. + * Call all of the "setUp" test case callbacks. * * @param \Illuminate\Foundation\Testing\TestCase $testCase * @return void */ - public function callSetUpCallbacks($testCase) + public function callSetUpTestCaseCallbacks($testCase) { $this->whenRunningInParallel(function () use ($testCase) { - foreach ($this->setUpCallbacks as $callback) { + foreach ($this->setUpTestCaseCallbacks as $callback) { $callback($testCase); } }); From 27a2c49954ee467cdcbb0c32661a8b566bded78d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 12:29:18 +0100 Subject: [PATCH 15/39] Adds "setUp" process and "tearDown" test case --- .../Foundation/Testing/TestCase.php | 2 + src/Illuminate/Testing/ParallelRunner.php | 4 ++ src/Illuminate/Testing/ParallelTesting.php | 71 ++++++++++++++++++- .../ParallelTestingServiceProvider.php | 4 +- 4 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 3e48d6032f73..17d229baac88 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -155,6 +155,8 @@ protected function tearDown(): void if ($this->app) { $this->callBeforeApplicationDestroyedCallbacks(); + ParallelTesting::callTearDownTestCaseCallbacks($this); + $this->app->flush(); $this->app = null; diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index 535bcb33b556..031269fa7fe8 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -75,6 +75,10 @@ public static function resolveApplicationUsing($resolver) */ public function run(): void { + $this->forEachProcess(function () { + ParallelTesting::callSetUpProcessCallbacks(); + }); + try { $this->runner->run(); } finally { diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 1ed532bbef06..9622a810c2d2 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -11,6 +11,13 @@ class ParallelTesting */ protected $tokenResolver; + /** + * All of the registered "setUp" process callbacks. + * + * @var array + */ + protected $setUpProcessCallbacks = []; + /** * All of the registered "setUp" test case callbacks. * @@ -25,6 +32,13 @@ class ParallelTesting */ protected $tearDownProcessCallbacks = []; + /** + * All of the registered "tearDown" test case callbacks. + * + * @var array + */ + protected $tearDownTestCaseCallbacks = []; + /** * Set a callback that should be used when resolving the unique process token. * @@ -36,6 +50,17 @@ public function resolveTokenUsing($resolver) $this->tokenResolver = $resolver; } + /** + * Register a "setUp" process callback. + * + * @param callable $callback + * @return void + */ + public function setUpProcess($callback) + { + $this->setUpProcessCallbacks[] = $callback; + } + /** * Register a "setUp" test case callback. * @@ -59,14 +84,25 @@ public function tearDownProcess($callback) } /** - * Call all of the "tearDown" process callbacks. + * Register a "tearDown" test case callback. * + * @param callable $callback * @return void */ - public function callTearDownProcessCallbacks() + public function tearDownTestCase($callback) + { + $this->tearDownTestCaseCallbacks[] = $callback; + } + + /** + * Call all of the "setUp" process callbacks. + * + * @return void + */ + public function callSetUpProcessCallbacks() { $this->whenRunningInParallel(function () { - foreach ($this->tearDownProcessCallbacks as $callback) { + foreach ($this->setUpProcessCallbacks as $callback) { $callback(); } }); @@ -87,6 +123,35 @@ public function callSetUpTestCaseCallbacks($testCase) }); } + /** + * Call all of the "tearDown" process callbacks. + * + * @return void + */ + public function callTearDownProcessCallbacks() + { + $this->whenRunningInParallel(function () { + foreach ($this->tearDownProcessCallbacks as $callback) { + $callback(); + } + }); + } + + /** + * Call all of the "tearDown" test case callbacks. + * + * @param \Illuminate\Foundation\Testing\TestCase $testCase + * @return void + */ + public function callTearDownTestCaseCallbacks($testCase) + { + $this->whenRunningInParallel(function () use ($testCase) { + foreach ($this->tearDownTestCaseCallbacks as $callback) { + $callback($testCase); + } + }); + } + /** * Gets an unique test token. * diff --git a/src/Illuminate/Testing/ParallelTestingServiceProvider.php b/src/Illuminate/Testing/ParallelTestingServiceProvider.php index a881a652347c..b762f96fbd1f 100644 --- a/src/Illuminate/Testing/ParallelTestingServiceProvider.php +++ b/src/Illuminate/Testing/ParallelTestingServiceProvider.php @@ -17,7 +17,7 @@ class ParallelTestingServiceProvider extends ServiceProvider implements Deferrab */ public function boot() { - if ($this->app->runningUnitTests()) { + if ($this->app->runningInConsole()) { $this->bootTemporaryDatabases(); } } @@ -29,7 +29,7 @@ public function boot() */ public function register() { - if ($this->app->runningUnitTests()) { + if ($this->app->runningInConsole()) { $this->app->singleton(ParallelTesting::class, function () { return new ParallelTesting(); }); From e18d25d45f7b152d742ffa23c73dd6526cb1757a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 12:30:50 +0100 Subject: [PATCH 16/39] Updates Parallel Testing facade --- src/Illuminate/Support/Facades/ParallelTesting.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/ParallelTesting.php b/src/Illuminate/Support/Facades/ParallelTesting.php index eb4873677b05..deed8a1b6aba 100644 --- a/src/Illuminate/Support/Facades/ParallelTesting.php +++ b/src/Illuminate/Support/Facades/ParallelTesting.php @@ -3,8 +3,10 @@ namespace Illuminate\Support\Facades; /** - * @method static void tearDownProcess(callable $callback) + * @method static void setUpProcess(callable $callback) * @method static void setUpTestCase(callable $callback) + * @method static void tearDownProcess(callable $callback) + * @method static void tearDownTestCase(callable $callback) * @method static string token() * * @see \Illuminate\Testing\ParallelTesting From f140288e8a8a4a8cf3d55c72ffab6cb2ea802192 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 13:45:35 +0100 Subject: [PATCH 17/39] Suffixes _test_ on storage too --- src/Illuminate/Support/Facades/Storage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index 93b4a4357f97..b7f43a34980a 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -55,7 +55,7 @@ public static function fake($disk = null, array $config = []) ); if ($token = ParallelTesting::token()) { - $root = "{$root}_{$token}"; + $root = "{$root}_test_{$token}"; } static::set($disk, $fake = static::createLocalDriver(array_merge($config, [ From 2793882f1e7daa34a42e50543e428f10215696fc Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 15:25:58 +0100 Subject: [PATCH 18/39] Fixes missing envs for process callbacks --- src/Illuminate/Testing/ParallelRunner.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index 031269fa7fe8..ecf34c4b4fb9 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -8,6 +8,7 @@ use ParaTest\Runners\PHPUnit\WrapperRunner; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; +use PHPUnit\TextUI\XmlConfiguration\PhpHandler; class ParallelRunner implements RunnerInterface { @@ -75,6 +76,8 @@ public static function resolveApplicationUsing($resolver) */ public function run(): void { + (new PhpHandler)->handle($this->options->configuration()->php()); + $this->forEachProcess(function () { ParallelTesting::callSetUpProcessCallbacks(); }); From 0cd2df726b25fa6cb3c793585f5105f3b694b00a Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 14:35:19 +0000 Subject: [PATCH 19/39] Apply fixes from StyleCI --- src/Illuminate/Testing/ParallelRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Testing/ParallelRunner.php b/src/Illuminate/Testing/ParallelRunner.php index ecf34c4b4fb9..e26528703bbe 100644 --- a/src/Illuminate/Testing/ParallelRunner.php +++ b/src/Illuminate/Testing/ParallelRunner.php @@ -6,9 +6,9 @@ use ParaTest\Runners\PHPUnit\Options; use ParaTest\Runners\PHPUnit\RunnerInterface; use ParaTest\Runners\PHPUnit\WrapperRunner; +use PHPUnit\TextUI\XmlConfiguration\PhpHandler; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; -use PHPUnit\TextUI\XmlConfiguration\PhpHandler; class ParallelRunner implements RunnerInterface { From 8c560a67af93607cf0123369d275e92b7641a974 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 19:51:45 +0100 Subject: [PATCH 20/39] Ensures "drops" only happen when needed --- .../Testing/Concerns/TemporaryDatabases.php | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php index 660ec2702017..018cbe0f14aa 100644 --- a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php +++ b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php @@ -6,18 +6,12 @@ use Illuminate\Support\Arr; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Facades\Schema; trait TemporaryDatabases { - /** - * The current temporary database name, if any. - * - * @var string|null - */ - protected static $temporaryDatabase; - /** * Boot temporary databases service. * @@ -25,6 +19,15 @@ trait TemporaryDatabases */ protected function bootTemporaryDatabases() { + ParallelTesting::setUpProcess(function () { + $this->whenNotUsingInMemoryDatabase(function ($database) { + [$name, $path] = $this->temporaryDatabase($database); + + File::ensureDirectoryExists(dirname($path)); + File::delete($path); + }); + }); + ParallelTesting::setUpTestCase(function ($testCase) { $uses = array_flip(class_uses_recursive(get_class($testCase))); @@ -39,9 +42,12 @@ protected function bootTemporaryDatabases() ParallelTesting::tearDownProcess(function () { $this->whenNotUsingInMemoryDatabase(function ($database) { - Schema::dropDatabaseIfExists( - $this->temporaryDatabaseName($database) - ); + [$name, $path] = $this->temporaryDatabase($database); + + if (File::exists($path)) { + Schema::dropDatabaseIfExists($name); + File::delete($path); + } }); }); } @@ -54,7 +60,7 @@ protected function bootTemporaryDatabases() protected function useTemporaryDatabase() { $this->whenNotUsingInMemoryDatabase(function ($database) { - $database = $this->ensureTemporaryDatabaseExists($database); + $name = $this->ensureTemporaryDatabaseExists($database); DB::purge(); @@ -62,13 +68,13 @@ protected function useTemporaryDatabase() config()->set( "database.connections.{$default}.database", - $database, + $name, ); }); } /** - * Ensure a temporary database exists. + * Ensure a temporary database exists, and returns it's name. * * @param string $database * @@ -76,18 +82,19 @@ protected function useTemporaryDatabase() */ protected function ensureTemporaryDatabaseExists($database) { - if (! static::$temporaryDatabase) { - static::$temporaryDatabase = $this->temporaryDatabaseName($database); + [$name, $path] = $this->temporaryDatabase($database); - Schema::dropDatabaseIfExists(static::$temporaryDatabase); - Schema::createDatabase(static::$temporaryDatabase); + if (! File::exists($path)) { + Schema::dropDatabaseIfExists($name); + File::put($path, ''); + Schema::createDatabase($name); $this->useTemporaryDatabase(); Artisan::call('migrate:fresh'); } - return static::$temporaryDatabase; + return $name; } /** @@ -106,14 +113,17 @@ protected function whenNotUsingInMemoryDatabase($callback) } /** - * Returns the temporary database name. + * Returns the temporary database name and path. * - * @return string + * @return array */ - protected function temporaryDatabaseName($database) + protected function temporaryDatabase($database) { $token = ParallelTesting::token(); - return "{$database}_test_{$token}"; + $name = "{$database}_test_{$token}"; + $path = storage_path('framework/testing/temporary-databases/' . $name); + + return [$name, $path]; } } From 0f1e621496b86527fac81658811c24bbf28ed7d7 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Fri, 8 Jan 2021 18:53:57 +0000 Subject: [PATCH 21/39] Apply fixes from StyleCI --- src/Illuminate/Testing/Concerns/TemporaryDatabases.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php index 018cbe0f14aa..9457d68848ed 100644 --- a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php +++ b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php @@ -122,7 +122,7 @@ protected function temporaryDatabase($database) $token = ParallelTesting::token(); $name = "{$database}_test_{$token}"; - $path = storage_path('framework/testing/temporary-databases/' . $name); + $path = storage_path('framework/testing/temporary-databases/'.$name); return [$name, $path]; } From 29d705c63cb3a2a8c7d9c285cf1cf35885df26f9 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 13:48:48 +0100 Subject: [PATCH 22/39] Adds support for SQLite file databases --- .../Database/Schema/SQLiteBuilder.php | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Illuminate/Database/Schema/SQLiteBuilder.php b/src/Illuminate/Database/Schema/SQLiteBuilder.php index 78b6b9c78d2e..6a1dbae23ec6 100644 --- a/src/Illuminate/Database/Schema/SQLiteBuilder.php +++ b/src/Illuminate/Database/Schema/SQLiteBuilder.php @@ -2,8 +2,34 @@ namespace Illuminate\Database\Schema; +use Illuminate\Support\Facades\File; + class SQLiteBuilder extends Builder { + /** + * Create a database in the schema. + * + * @param string $name + * @return bool + */ + public function createDatabase($name) + { + return File::put($name, '') !== false; + } + + /** + * Drop a database from the schema if the database exists. + * + * @param string $name + * @return bool + */ + public function dropDatabaseIfExists($name) + { + return File::exists($name) + ? File::delete($name) + : true; + } + /** * Drop all tables from the database. * From 52de4b4136163af9f27afead564dc3e46a3a5eb0 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 14:40:32 +0100 Subject: [PATCH 23/39] Adds tests for SQLite file databases --- tests/Database/DatabaseSQLiteBuilderTest.php | 91 ++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/Database/DatabaseSQLiteBuilderTest.php diff --git a/tests/Database/DatabaseSQLiteBuilderTest.php b/tests/Database/DatabaseSQLiteBuilderTest.php new file mode 100644 index 000000000000..e14fa936c8aa --- /dev/null +++ b/tests/Database/DatabaseSQLiteBuilderTest.php @@ -0,0 +1,91 @@ +singleton('files', Filesystem::class); + + Facade::setFacadeApplication($app); + } + + protected function tearDown(): void + { + m::close(); + + Container::setInstance(null); + Facade::setFacadeApplication(null); + } + + public function testCreateDatabase() + { + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaGrammar')->once(); + + $builder = new SQLiteBuilder($connection); + + File::shouldReceive('put') + ->once() + ->with('my_temporary_database_a', '') + ->andReturn(20); // bytes + + $this->assertTrue($builder->createDatabase('my_temporary_database_a')); + + File::shouldReceive('put') + ->once() + ->with('my_temporary_database_b', '') + ->andReturn(false); + + $this->assertFalse($builder->createDatabase('my_temporary_database_b')); + } + + public function testDropDatabaseIfExists() + { + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaGrammar')->once(); + + $builder = new SQLiteBuilder($connection); + + File::shouldReceive('exists') + ->once() + ->andReturn(true); + + File::shouldReceive('delete') + ->once() + ->with('my_temporary_database_b') + ->andReturn(true); + + $this->assertTrue($builder->dropDatabaseIfExists('my_temporary_database_b')); + + File::shouldReceive('exists') + ->once() + ->andReturn(false); + + $this->assertTrue($builder->dropDatabaseIfExists('my_temporary_database_c')); + + File::shouldReceive('exists') + ->once() + ->andReturn(true); + + File::shouldReceive('delete') + ->once() + ->with('my_temporary_database_c') + ->andReturn(false); + + $this->assertFalse($builder->dropDatabaseIfExists('my_temporary_database_c')); + } +} From f255cee3e3896129470d0f02c1e51d7bf2b32eff Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 14:47:11 +0100 Subject: [PATCH 24/39] Lower case create/drop statements --- src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php | 4 ++-- .../Database/Schema/Grammars/PostgresGrammar.php | 4 ++-- tests/Database/DatabaseMySqlSchemaGrammarTest.php | 8 ++++---- tests/Database/DatabaseMysqlBuilderTest.php | 4 ++-- tests/Database/DatabasePostgresBuilderTest.php | 4 ++-- tests/Database/DatabasePostgresSchemaGrammarTest.php | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php index 77909c336675..ecaf96e2a3a1 100755 --- a/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/MySqlGrammar.php @@ -36,7 +36,7 @@ class MySqlGrammar extends Grammar public function compileCreateDatabase($name, $connection) { return sprintf( - 'CREATE DATABASE %s DEFAULT CHARACTER SET %s DEFAULT COLLATE %s', + 'create database %s default character set %s default collate %s', $this->wrapValue($name), $this->wrapValue($connection->getConfig('charset')), $this->wrapValue($connection->getConfig('collation')), @@ -52,7 +52,7 @@ public function compileCreateDatabase($name, $connection) public function compileDropDatabaseIfExists($name) { return sprintf( - 'DROP DATABASE IF EXISTS %s', + 'drop database if exists %s', $this->wrapValue($name) ); } diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 0817998ec4d8..58328f2e09e2 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -45,7 +45,7 @@ class PostgresGrammar extends Grammar public function compileCreateDatabase($name, $connection) { return sprintf( - 'CREATE DATABASE %s ENCODING %s', + 'create database %s encoding %s', $this->wrapValue($name), $this->wrapValue($connection->getConfig('charset')), ); @@ -60,7 +60,7 @@ public function compileCreateDatabase($name, $connection) public function compileDropDatabaseIfExists($name) { return sprintf( - 'DROP DATABASE IF EXISTS %s', + 'drop database if exists %s', $this->wrapValue($name) ); } diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php index 1eb17e0cc56a..ebb190ccb46b 100755 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ b/tests/Database/DatabaseMySqlSchemaGrammarTest.php @@ -1158,7 +1158,7 @@ public function testCreateDatabase() $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); $this->assertSame( - 'CREATE DATABASE `my_database_a` DEFAULT CHARACTER SET `utf8mb4_foo` DEFAULT COLLATE `utf8mb4_unicode_ci_foo`', + 'create database `my_database_a` default character set `utf8mb4_foo` default collate `utf8mb4_unicode_ci_foo`', $statement ); @@ -1169,7 +1169,7 @@ public function testCreateDatabase() $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); $this->assertSame( - 'CREATE DATABASE `my_database_b` DEFAULT CHARACTER SET `utf8mb4_bar` DEFAULT COLLATE `utf8mb4_unicode_ci_bar`', + 'create database `my_database_b` default character set `utf8mb4_bar` default collate `utf8mb4_unicode_ci_bar`', $statement ); } @@ -1179,14 +1179,14 @@ public function testDropDatabaseIfExists() $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); $this->assertSame( - 'DROP DATABASE IF EXISTS `my_database_a`', + 'drop database if exists `my_database_a`', $statement ); $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); $this->assertSame( - 'DROP DATABASE IF EXISTS `my_database_b`', + 'drop database if exists `my_database_b`', $statement ); } diff --git a/tests/Database/DatabaseMysqlBuilderTest.php b/tests/Database/DatabaseMysqlBuilderTest.php index 88d1b568b1f1..dd36209eb40e 100644 --- a/tests/Database/DatabaseMysqlBuilderTest.php +++ b/tests/Database/DatabaseMysqlBuilderTest.php @@ -24,7 +24,7 @@ public function testCreateDatabase() $connection->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8mb4_unicode_ci'); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'CREATE DATABASE `my_temporary_database` DEFAULT CHARACTER SET `utf8mb4` DEFAULT COLLATE `utf8mb4_unicode_ci`' + 'create database `my_temporary_database` default character set `utf8mb4` default collate `utf8mb4_unicode_ci`' )->andReturn(true); $builder = new MySqlBuilder($connection); @@ -38,7 +38,7 @@ public function testDropDatabaseIfExists() $connection = m::mock(Connection::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'DROP DATABASE IF EXISTS `my_database_a`' + 'drop database if exists `my_database_a`' )->andReturn(true); $builder = new MySqlBuilder($connection); diff --git a/tests/Database/DatabasePostgresBuilderTest.php b/tests/Database/DatabasePostgresBuilderTest.php index 8f6bdfcc124b..bb5b7a023b64 100644 --- a/tests/Database/DatabasePostgresBuilderTest.php +++ b/tests/Database/DatabasePostgresBuilderTest.php @@ -24,7 +24,7 @@ public function testCreateDatabase() $connection->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'CREATE DATABASE "my_temporary_database" ENCODING "utf8"' + 'create database "my_temporary_database" encoding "utf8"' )->andReturn(true); $builder = $this->getBuilder($connection); @@ -38,7 +38,7 @@ public function testDropDatabaseIfExists() $connection = m::mock(Connection::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); $connection->shouldReceive('statement')->once()->with( - 'DROP DATABASE IF EXISTS "my_database_a"' + 'drop database if exists "my_database_a"' )->andReturn(true); $builder = $this->getBuilder($connection); diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php index f7d08453918b..7e10fae23d55 100755 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ b/tests/Database/DatabasePostgresSchemaGrammarTest.php @@ -1004,7 +1004,7 @@ public function testCreateDatabase() $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); $this->assertSame( - 'CREATE DATABASE "my_database_a" ENCODING "utf8_foo"', + 'create database "my_database_a" encoding "utf8_foo"', $statement ); @@ -1013,7 +1013,7 @@ public function testCreateDatabase() $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); $this->assertSame( - 'CREATE DATABASE "my_database_b" ENCODING "utf8_bar"', + 'create database "my_database_b" encoding "utf8_bar"', $statement ); } @@ -1023,14 +1023,14 @@ public function testDropDatabaseIfExists() $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); $this->assertSame( - 'DROP DATABASE IF EXISTS "my_database_a"', + 'drop database if exists "my_database_a"', $statement ); $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); $this->assertSame( - 'DROP DATABASE IF EXISTS "my_database_b"', + 'drop database if exists "my_database_b"', $statement ); } From 3d62d4216f2db15fa1b2940993925f9ed613f416 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 15:41:30 +0100 Subject: [PATCH 25/39] Adds support for SQL Server --- .../Schema/Grammars/SqlServerGrammar.php | 29 ++++++++++++ .../Database/Schema/SqlServerBuilder.php | 26 +++++++++++ .../DatabaseSqlServerSchemaGrammarTest.php | 36 +++++++++++++++ tests/Database/SqlServerBuilderTest.php | 46 +++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 tests/Database/SqlServerBuilderTest.php diff --git a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php index 79f3f0f5e3ad..c3fc442e2368 100755 --- a/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php @@ -28,6 +28,35 @@ class SqlServerGrammar extends Grammar */ protected $serials = ['tinyInteger', 'smallInteger', 'mediumInteger', 'integer', 'bigInteger']; + /** + * Compile a create database command. + * + * @param string $name + * @param \Illuminate\Database\Connection $connection + * @return string + */ + public function compileCreateDatabase($name, $connection) + { + return sprintf( + 'create database %s', + $this->wrapValue($name), + ); + } + + /** + * Compile a drop database if exists command. + * + * @param string $name + * @return string + */ + public function compileDropDatabaseIfExists($name) + { + return sprintf( + 'drop database if exists %s', + $this->wrapValue($name) + ); + } + /** * Compile the query to determine if a table exists. * diff --git a/src/Illuminate/Database/Schema/SqlServerBuilder.php b/src/Illuminate/Database/Schema/SqlServerBuilder.php index 0b3e47bec9a6..223abd44ed1c 100644 --- a/src/Illuminate/Database/Schema/SqlServerBuilder.php +++ b/src/Illuminate/Database/Schema/SqlServerBuilder.php @@ -4,6 +4,32 @@ class SqlServerBuilder extends Builder { + /** + * Create a database in the schema. + * + * @param string $name + * @return bool + */ + public function createDatabase($name) + { + return $this->connection->statement( + $this->grammar->compileCreateDatabase($name, $this->connection) + ); + } + + /** + * Drop a database from the schema if the database exists. + * + * @param string $name + * @return bool + */ + public function dropDatabaseIfExists($name) + { + return $this->connection->statement( + $this->grammar->compileDropDatabaseIfExists($name) + ); + } + /** * Drop all tables from the database. * diff --git a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php index acb2ed68367e..4513a93001a3 100755 --- a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php +++ b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php @@ -914,6 +914,42 @@ public function testQuoteStringOnArray() $this->assertSame("N'中文', N'測試'", $this->getGrammar()->quoteString(['中文', '測試'])); } + public function testCreateDatabase() + { + $connection = $this->getConnection(); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_a', $connection); + + $this->assertSame( + 'create database "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileCreateDatabase('my_database_b', $connection); + + $this->assertSame( + 'create database "my_database_b"', + $statement + ); + } + + public function testDropDatabaseIfExists() + { + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_a'); + + $this->assertSame( + 'drop database if exists "my_database_a"', + $statement + ); + + $statement = $this->getGrammar()->compileDropDatabaseIfExists('my_database_b'); + + $this->assertSame( + 'drop database if exists "my_database_b"', + $statement + ); + } + protected function getConnection() { return m::mock(Connection::class); diff --git a/tests/Database/SqlServerBuilderTest.php b/tests/Database/SqlServerBuilderTest.php new file mode 100644 index 000000000000..de82f796056c --- /dev/null +++ b/tests/Database/SqlServerBuilderTest.php @@ -0,0 +1,46 @@ +shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'create database "my_temporary_database_a"' + )->andReturn(true); + + $builder = new SqlServerBuilder($connection); + $builder->createDatabase('my_temporary_database_a'); + } + + public function testDropDatabaseIfExists() + { + $grammar = new SqlServerGrammar(); + + $connection = m::mock(Connection::class); + $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); + $connection->shouldReceive('statement')->once()->with( + 'drop database if exists "my_temporary_database_b"' + )->andReturn(true); + + $builder = new SqlServerBuilder($connection); + + $builder->dropDatabaseIfExists('my_temporary_database_b'); + } +} From 2a5c678244899b73a4928b82396c48f234a175fa Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 14:42:09 +0000 Subject: [PATCH 26/39] Apply fixes from StyleCI --- tests/Database/DatabaseSQLiteBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/DatabaseSQLiteBuilderTest.php b/tests/Database/DatabaseSQLiteBuilderTest.php index e14fa936c8aa..b2587ea67d82 100644 --- a/tests/Database/DatabaseSQLiteBuilderTest.php +++ b/tests/Database/DatabaseSQLiteBuilderTest.php @@ -5,11 +5,11 @@ use Illuminate\Container\Container; use Illuminate\Database\Connection; use Illuminate\Database\Schema\SQLiteBuilder; +use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Facades\Facade; use Illuminate\Support\Facades\File; use Mockery as m; use PHPUnit\Framework\TestCase; -use Illuminate\Filesystem\Filesystem; class DatabaseSQLiteBuilderTest extends TestCase { From d29bb9d4e8aed246f78db9175b0b4071d5aca60d Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 16:35:15 +0100 Subject: [PATCH 27/39] Adds tests against Parallel Testing callbacks --- tests/Testing/ParallelTestingTest.php | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 2c1cab07dc5c..91743bcf7f81 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -15,6 +15,33 @@ protected function setUp(): void $_SERVER['LARAVEL_PARALLEL_TESTING'] = 1; } + /** + * @dataProvider callbacks + */ + public function testCallbacks($callback) + { + $parallelTesting = new ParallelTesting(); + $caller = 'call' . ucfirst($callback) . 'Callbacks'; + + $state = false; + $parallelTesting->{$caller}($this); + $this->assertFalse($state); + + $parallelTesting->{$callback}(function () use (&$state) { + $state = true; + }); + + $parallelTesting->{$caller}($this); + $this->assertFalse($state); + + $parallelTesting->resolveTokenUsing(function () { + return 1; + }); + + $parallelTesting->{$caller}($this); + $this->assertTrue($state); + } + public function testToken() { $parallelTesting = new ParallelTesting(); @@ -28,6 +55,17 @@ public function testToken() $this->assertSame(1, $parallelTesting->token()); } + public function callbacks() + { + return [ + ['setUpProcess'], + ['setUpTestCase'], + ['tearDownTestCase'], + ['tearDownProcess'], + ]; + } + + public function tearDown(): void { parent::tearDown(); From b2c3296e8b4f64c9f867007d2c21a7e6cef5b4ba Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 16:47:26 +0100 Subject: [PATCH 28/39] Adds tests for parallel console output --- tests/Testing/ParallelConsoleOutputTest.php | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/Testing/ParallelConsoleOutputTest.php diff --git a/tests/Testing/ParallelConsoleOutputTest.php b/tests/Testing/ParallelConsoleOutputTest.php new file mode 100644 index 000000000000..130b6ecada6a --- /dev/null +++ b/tests/Testing/ParallelConsoleOutputTest.php @@ -0,0 +1,25 @@ +write('Running phpunit in 12 processes with laravel/laravel.'); + $this->assertEmpty($original->fetch()); + + $output->write('Configuration read from phpunit.xml.dist'); + $this->assertEmpty($original->fetch()); + + $output->write('... 3/3 (100%)'); + $this->assertSame('... 3/3 (100%)', $original->fetch()); + } +} From 49c4b2862791e53aa642701a5905fe8dac303928 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 15:48:59 +0000 Subject: [PATCH 29/39] Apply fixes from StyleCI --- tests/Testing/ParallelTestingTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 91743bcf7f81..54ffcb77014b 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -21,7 +21,7 @@ protected function setUp(): void public function testCallbacks($callback) { $parallelTesting = new ParallelTesting(); - $caller = 'call' . ucfirst($callback) . 'Callbacks'; + $caller = 'call'.ucfirst($callback).'Callbacks'; $state = false; $parallelTesting->{$caller}($this); @@ -65,7 +65,6 @@ public function callbacks() ]; } - public function tearDown(): void { parent::tearDown(); From e85ed40a7ae556df2d4444a8c3ff712177c896cf Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 16:50:28 +0100 Subject: [PATCH 30/39] Removes unused lines in tests --- tests/Testing/ParallelTestingTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 91743bcf7f81..36db0f07b0b5 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -3,7 +3,6 @@ namespace Illuminate\Tests\Foundation; use Illuminate\Testing\ParallelTesting; -use Mockery as m; use PHPUnit\Framework\TestCase; class ParallelTestingTest extends TestCase @@ -70,7 +69,6 @@ public function tearDown(): void { parent::tearDown(); - m::close(); unset($_SERVER['LARAVEL_PARALLEL_TESTING']); } } From cc4bffab87295fe48d1f15f17aca6e0cf4644380 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 17:00:17 +0100 Subject: [PATCH 31/39] Calls Parallel Testing setUp callbacks after refresh app only --- src/Illuminate/Foundation/Testing/TestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Foundation/Testing/TestCase.php b/src/Illuminate/Foundation/Testing/TestCase.php index 17d229baac88..6bbc653102da 100644 --- a/src/Illuminate/Foundation/Testing/TestCase.php +++ b/src/Illuminate/Foundation/Testing/TestCase.php @@ -82,9 +82,9 @@ protected function setUp(): void if (! $this->app) { $this->refreshApplication(); - } - ParallelTesting::callSetUpTestCaseCallbacks($this); + ParallelTesting::callSetUpTestCaseCallbacks($this); + } $this->setUpTraits(); From 4f57aaca3511d5d2d7f8caa3d3eb8d8c7383b54f Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Mon, 11 Jan 2021 21:27:03 +0100 Subject: [PATCH 32/39] Cleans folders for parallel testing --- src/Illuminate/Support/Facades/Storage.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Support/Facades/Storage.php b/src/Illuminate/Support/Facades/Storage.php index b7f43a34980a..66fa1303000e 100644 --- a/src/Illuminate/Support/Facades/Storage.php +++ b/src/Illuminate/Support/Facades/Storage.php @@ -50,14 +50,14 @@ public static function fake($disk = null, array $config = []) { $disk = $disk ?: static::$app['config']->get('filesystems.default'); - (new Filesystem)->cleanDirectory( - $root = storage_path('framework/testing/disks/'.$disk) - ); + $root = storage_path('framework/testing/disks/'.$disk); if ($token = ParallelTesting::token()) { $root = "{$root}_test_{$token}"; } + (new Filesystem)->cleanDirectory($root); + static::set($disk, $fake = static::createLocalDriver(array_merge($config, [ 'root' => $root, ]))); From 69b9eb04b8f79066630cce153840250d93435744 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Jan 2021 02:16:18 +0100 Subject: [PATCH 33/39] Makes test databases persist --- .../Testing/Concerns/TemporaryDatabases.php | 129 -------------- .../Testing/Concerns/TestDatabases.php | 163 ++++++++++++++++++ src/Illuminate/Testing/ParallelTesting.php | 37 ++++ .../ParallelTestingServiceProvider.php | 6 +- tests/Testing/ParallelTestingTest.php | 14 ++ 5 files changed, 217 insertions(+), 132 deletions(-) delete mode 100644 src/Illuminate/Testing/Concerns/TemporaryDatabases.php create mode 100644 src/Illuminate/Testing/Concerns/TestDatabases.php diff --git a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php b/src/Illuminate/Testing/Concerns/TemporaryDatabases.php deleted file mode 100644 index 9457d68848ed..000000000000 --- a/src/Illuminate/Testing/Concerns/TemporaryDatabases.php +++ /dev/null @@ -1,129 +0,0 @@ -whenNotUsingInMemoryDatabase(function ($database) { - [$name, $path] = $this->temporaryDatabase($database); - - File::ensureDirectoryExists(dirname($path)); - File::delete($path); - }); - }); - - ParallelTesting::setUpTestCase(function ($testCase) { - $uses = array_flip(class_uses_recursive(get_class($testCase))); - - if (Arr::hasAny($uses, [ - Testing\DatabaseMigrations::class, - Testing\DatabaseTransactions::class, - Testing\RefreshDatabase::class, - ])) { - $this->useTemporaryDatabase(); - } - }); - - ParallelTesting::tearDownProcess(function () { - $this->whenNotUsingInMemoryDatabase(function ($database) { - [$name, $path] = $this->temporaryDatabase($database); - - if (File::exists($path)) { - Schema::dropDatabaseIfExists($name); - File::delete($path); - } - }); - }); - } - - /** - * Use a temporary database. - * - * @return void - */ - protected function useTemporaryDatabase() - { - $this->whenNotUsingInMemoryDatabase(function ($database) { - $name = $this->ensureTemporaryDatabaseExists($database); - - DB::purge(); - - $default = config('database.default'); - - config()->set( - "database.connections.{$default}.database", - $name, - ); - }); - } - - /** - * Ensure a temporary database exists, and returns it's name. - * - * @param string $database - * - * @return string - */ - protected function ensureTemporaryDatabaseExists($database) - { - [$name, $path] = $this->temporaryDatabase($database); - - if (! File::exists($path)) { - Schema::dropDatabaseIfExists($name); - File::put($path, ''); - Schema::createDatabase($name); - - $this->useTemporaryDatabase(); - - Artisan::call('migrate:fresh'); - } - - return $name; - } - - /** - * Apply the callback when tests are not using in memory database. - * - * @param callable $callback - * @return void - */ - protected function whenNotUsingInMemoryDatabase($callback) - { - $database = DB::getConfig('database'); - - if ($database != ':memory:') { - $callback($database); - } - } - - /** - * Returns the temporary database name and path. - * - * @return array - */ - protected function temporaryDatabase($database) - { - $token = ParallelTesting::token(); - - $name = "{$database}_test_{$token}"; - $path = storage_path('framework/testing/temporary-databases/'.$name); - - return [$name, $path]; - } -} diff --git a/src/Illuminate/Testing/Concerns/TestDatabases.php b/src/Illuminate/Testing/Concerns/TestDatabases.php new file mode 100644 index 000000000000..f8aac12fc2f0 --- /dev/null +++ b/src/Illuminate/Testing/Concerns/TestDatabases.php @@ -0,0 +1,163 @@ +whenNotUsingInMemoryDatabase(function ($database) { + if (ParallelTesting::option('refresh_databases')) { + Schema::dropDatabaseIfExists( + $this->testDatabase($database) + ); + } + }); + }); + + ParallelTesting::setUpTestCase(function ($testCase) { + $uses = array_flip(class_uses_recursive(get_class($testCase))); + + $databaseTraits = [ + Testing\DatabaseMigrations::class, + Testing\DatabaseTransactions::class, + Testing\RefreshDatabase::class, + ]; + + if (Arr::hasAny($uses, $databaseTraits)) { + $this->whenNotUsingInMemoryDatabase(function ($database) use ($uses) { + $testDatabase = $this->ensureTestDatabaseExists($database); + + $this->switchToDatabase($testDatabase); + + if (isset($uses[Testing\DatabaseTransactions::class])) { + $this->ensureSchemaIsUpToDate(); + } + }); + } + }); + } + + /** + * Ensure a test database exists, and returns its name. + * + * @param string $database + * + * @return string + */ + protected function ensureTestDatabaseExists($database) + { + return tap($this->testDatabase($database), function ($testDatabase) use ($database) { + try { + $this->usingDatabase($testDatabase, function () { + Schema::hasTable('dummy'); + }); + } catch (QueryException $e) { + $this->usingDatabase($database, function () use ($testDatabase) { + Schema::dropDatabaseIfExists($testDatabase); + Schema::createDatabase($testDatabase); + }); + } + }); + } + + /** + * Ensure the current the database test schema is up to date. + * + * @return void + */ + protected function ensureSchemaIsUpToDate() + { + if (! static::$schemaIsUpToDate) { + Artisan::call('migrate'); + + static::$schemaIsUpToDate = true; + } + } + + /** + * Runs the given callable using the given database. + * + * @param string $database + * @param callable $database + * @return void + */ + protected function usingDatabase($database, $callable) + { + $original = DB::getConfig('database'); + + try { + $this->switchToDatabase($database); + $callable(); + } finally { + $this->switchToDatabase($original); + } + } + + /** + * Apply the callback when tests are not using in memory database. + * + * @param callable $callback + * @return void + */ + protected function whenNotUsingInMemoryDatabase($callback) + { + $database = DB::getConfig('database'); + + if ($database != ':memory:') { + $callback($database); + } + } + + + /** + * Switch to the given database. + * + * @param string $database + * @return void + */ + protected function switchToDatabase($database) + { + DB::purge(); + + $default = config('database.default'); + + config()->set( + "database.connections.{$default}.database", + $database, + ); + } + + /** + * Returns the test database name. + * + * @return string + */ + protected function testDatabase($database) + { + $token = ParallelTesting::token(); + + return "{$database}_test_{$token}"; + } +} diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 9622a810c2d2..612cd056a9d0 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -2,8 +2,17 @@ namespace Illuminate\Testing; +use Illuminate\Support\Str; + class ParallelTesting { + /** + * The options resolver callback. + * + * @var \Closure|null + */ + protected $optionsResolver; + /** * The token resolver callback. * @@ -39,6 +48,17 @@ class ParallelTesting */ protected $tearDownTestCaseCallbacks = []; + /** + * Set a callback that should be used when resolving options. + * + * @param \Closure|null $callback + * @return void + */ + public function resolveOptionsUsing($resolver) + { + $this->optionsResolver = $resolver; + } + /** * Set a callback that should be used when resolving the unique process token. * @@ -152,6 +172,23 @@ public function callTearDownTestCaseCallbacks($testCase) }); } + /** + * Get an parallel testing option. + * + * @param string $option + * @return mixed + */ + public function option($option) + { + $optionsResolver = $this->optionsResolver ?: function ($option) { + $option = 'LARAVEL_PARALLEL_TESTING_' . Str::upper($option); + + return $_SERVER[$option] ?? false; + }; + + return call_user_func($optionsResolver, $option); + } + /** * Gets an unique test token. * diff --git a/src/Illuminate/Testing/ParallelTestingServiceProvider.php b/src/Illuminate/Testing/ParallelTestingServiceProvider.php index b762f96fbd1f..9e15ef35aa55 100644 --- a/src/Illuminate/Testing/ParallelTestingServiceProvider.php +++ b/src/Illuminate/Testing/ParallelTestingServiceProvider.php @@ -4,11 +4,11 @@ use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider; -use Illuminate\Testing\Concerns\TemporaryDatabases; +use Illuminate\Testing\Concerns\TestDatabases; class ParallelTestingServiceProvider extends ServiceProvider implements DeferrableProvider { - use TemporaryDatabases; + use TestDatabases; /** * Boot the application's service providers. @@ -18,7 +18,7 @@ class ParallelTestingServiceProvider extends ServiceProvider implements Deferrab public function boot() { if ($this->app->runningInConsole()) { - $this->bootTemporaryDatabases(); + $this->bootTestDatabase(); } } diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 8db2f6b2109f..29915a5176d6 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -41,6 +41,20 @@ public function testCallbacks($callback) $this->assertTrue($state); } + public function testOptions() + { + $parallelTesting = new ParallelTesting(); + + $this->assertFalse($parallelTesting->option('refresh_databases')); + + $parallelTesting->resolveOptionsUsing(function ($option) { + return $option == 'refresh_databases'; + }); + + $this->assertFalse($parallelTesting->option('recreate_caches')); + $this->assertTrue($parallelTesting->option('refresh_databases')); + } + public function testToken() { $parallelTesting = new ParallelTesting(); From b389c43aa7c64feffe4f67012aac90209fbc5e58 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Jan 2021 01:18:33 +0000 Subject: [PATCH 34/39] Apply fixes from StyleCI --- src/Illuminate/Testing/Concerns/TestDatabases.php | 3 +-- src/Illuminate/Testing/ParallelTesting.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Testing/Concerns/TestDatabases.php b/src/Illuminate/Testing/Concerns/TestDatabases.php index f8aac12fc2f0..b06cd8a6a2cb 100644 --- a/src/Illuminate/Testing/Concerns/TestDatabases.php +++ b/src/Illuminate/Testing/Concerns/TestDatabases.php @@ -2,13 +2,13 @@ namespace Illuminate\Testing\Concerns; +use Illuminate\Database\QueryException; use Illuminate\Foundation\Testing; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\ParallelTesting; use Illuminate\Support\Facades\Schema; -use Illuminate\Database\QueryException; trait TestDatabases { @@ -130,7 +130,6 @@ protected function whenNotUsingInMemoryDatabase($callback) } } - /** * Switch to the given database. * diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 612cd056a9d0..ea29aaaa46c8 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -181,7 +181,7 @@ public function callTearDownTestCaseCallbacks($testCase) public function option($option) { $optionsResolver = $this->optionsResolver ?: function ($option) { - $option = 'LARAVEL_PARALLEL_TESTING_' . Str::upper($option); + $option = 'LARAVEL_PARALLEL_TESTING_'.Str::upper($option); return $_SERVER[$option] ?? false; }; From 528b6bdc1253da69da7e4dbae1538f2473bea764 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Jan 2021 11:41:06 +0100 Subject: [PATCH 35/39] Resolve parallel testing callbacks from container --- src/Illuminate/Testing/ParallelTesting.php | 37 +++++++++++++++++-- .../ParallelTestingServiceProvider.php | 2 +- tests/Testing/ParallelTestingTest.php | 20 ++++++++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 612cd056a9d0..cdf4cf5e58fe 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -2,10 +2,18 @@ namespace Illuminate\Testing; +use Illuminate\Contracts\Container\Container; use Illuminate\Support\Str; class ParallelTesting { + /** + * The container instance. + * + * @var \Illuminate\Contracts\Container\Container + */ + protected $container; + /** * The options resolver callback. * @@ -48,6 +56,17 @@ class ParallelTesting */ protected $tearDownTestCaseCallbacks = []; + /** + * Create a new parallel testing instance. + * + * @param \Illuminate\Contracts\Container\Container $container + * @return void + */ + public function __construct(Container $container) + { + $this->container = $container; + } + /** * Set a callback that should be used when resolving options. * @@ -123,7 +142,9 @@ public function callSetUpProcessCallbacks() { $this->whenRunningInParallel(function () { foreach ($this->setUpProcessCallbacks as $callback) { - $callback(); + $this->container->call($callback, [ + 'token' => $this->token(), + ]); } }); } @@ -138,7 +159,10 @@ public function callSetUpTestCaseCallbacks($testCase) { $this->whenRunningInParallel(function () use ($testCase) { foreach ($this->setUpTestCaseCallbacks as $callback) { - $callback($testCase); + $this->container->call($callback, [ + 'testCase' => $testCase, + 'token' => $this->token(), + ]); } }); } @@ -152,7 +176,9 @@ public function callTearDownProcessCallbacks() { $this->whenRunningInParallel(function () { foreach ($this->tearDownProcessCallbacks as $callback) { - $callback(); + $this->container->call($callback, [ + 'token' => $this->token(), + ]); } }); } @@ -167,7 +193,10 @@ public function callTearDownTestCaseCallbacks($testCase) { $this->whenRunningInParallel(function () use ($testCase) { foreach ($this->tearDownTestCaseCallbacks as $callback) { - $callback($testCase); + $this->container->call($callback, [ + 'testCase' => $testCase, + 'token' => $this->token(), + ]); } }); } diff --git a/src/Illuminate/Testing/ParallelTestingServiceProvider.php b/src/Illuminate/Testing/ParallelTestingServiceProvider.php index 9e15ef35aa55..20b900d2e58e 100644 --- a/src/Illuminate/Testing/ParallelTestingServiceProvider.php +++ b/src/Illuminate/Testing/ParallelTestingServiceProvider.php @@ -31,7 +31,7 @@ public function register() { if ($this->app->runningInConsole()) { $this->app->singleton(ParallelTesting::class, function () { - return new ParallelTesting(); + return new ParallelTesting($this->app); }); } } diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 29915a5176d6..19402debf7b7 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -2,6 +2,7 @@ namespace Illuminate\Tests\Foundation; +use Illuminate\Container\Container; use Illuminate\Testing\ParallelTesting; use PHPUnit\Framework\TestCase; @@ -11,6 +12,8 @@ protected function setUp(): void { parent::setUp(); + Container::setInstance(new Container); + $_SERVER['LARAVEL_PARALLEL_TESTING'] = 1; } @@ -19,14 +22,21 @@ protected function setUp(): void */ public function testCallbacks($callback) { - $parallelTesting = new ParallelTesting(); + $parallelTesting = new ParallelTesting(Container::getInstance()); $caller = 'call'.ucfirst($callback).'Callbacks'; $state = false; $parallelTesting->{$caller}($this); $this->assertFalse($state); - $parallelTesting->{$callback}(function () use (&$state) { + $parallelTesting->{$callback}(function ($token, $testCase = null) use ($callback, &$state) { + if (in_array($callback, ['setUpTestCase', 'tearDownTestCase'])) { + $this->assertSame($this, $testCase); + } else { + $this->assertNull($testCase); + } + + $this->assertEquals(1, $token); $state = true; }); @@ -43,7 +53,7 @@ public function testCallbacks($callback) public function testOptions() { - $parallelTesting = new ParallelTesting(); + $parallelTesting = new ParallelTesting(Container::getInstance()); $this->assertFalse($parallelTesting->option('refresh_databases')); @@ -57,7 +67,7 @@ public function testOptions() public function testToken() { - $parallelTesting = new ParallelTesting(); + $parallelTesting = new ParallelTesting(Container::getInstance()); $this->assertFalse($parallelTesting->token()); @@ -82,6 +92,8 @@ public function tearDown(): void { parent::tearDown(); + Container::setInstance(null); + unset($_SERVER['LARAVEL_PARALLEL_TESTING']); } } From 0f4c024f99d641a3a991b29a253fbff1a514418b Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Tue, 12 Jan 2021 10:44:30 +0000 Subject: [PATCH 36/39] Apply fixes from StyleCI --- src/Illuminate/Testing/ParallelTesting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index 8d76d3af031d..c4e241cdac5a 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -193,7 +193,7 @@ public function callTearDownTestCaseCallbacks($testCase) { $this->whenRunningInParallel(function () use ($testCase) { foreach ($this->tearDownTestCaseCallbacks as $callback) { - $this->container->call($callback, [ + $this->container->call($callback, [ 'testCase' => $testCase, 'token' => $this->token(), ]); From f52fa9e6fd2b81f6c7c9783980e430e1a4d358a9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Jan 2021 13:45:39 -0600 Subject: [PATCH 37/39] Update TestDatabases.php --- src/Illuminate/Testing/Concerns/TestDatabases.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Testing/Concerns/TestDatabases.php b/src/Illuminate/Testing/Concerns/TestDatabases.php index b06cd8a6a2cb..cac40aa59812 100644 --- a/src/Illuminate/Testing/Concerns/TestDatabases.php +++ b/src/Illuminate/Testing/Concerns/TestDatabases.php @@ -60,7 +60,7 @@ protected function bootTestDatabase() } /** - * Ensure a test database exists, and returns its name. + * Ensure a test database exists and returns its name. * * @param string $database * @@ -83,7 +83,7 @@ protected function ensureTestDatabaseExists($database) } /** - * Ensure the current the database test schema is up to date. + * Ensure the current database test schema is up to date. * * @return void */ @@ -116,7 +116,7 @@ protected function usingDatabase($database, $callable) } /** - * Apply the callback when tests are not using in memory database. + * Apply the given callback when tests are not using in memory database. * * @param callable $callback * @return void From 985c3d3c59f3be2b3742a76c401bc0af587ec843 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Jan 2021 13:52:49 -0600 Subject: [PATCH 38/39] Update ParallelTesting.php --- src/Illuminate/Testing/ParallelTesting.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Testing/ParallelTesting.php b/src/Illuminate/Testing/ParallelTesting.php index c4e241cdac5a..ba67e16c5c74 100644 --- a/src/Illuminate/Testing/ParallelTesting.php +++ b/src/Illuminate/Testing/ParallelTesting.php @@ -244,7 +244,7 @@ protected function whenRunningInParallel($callback) } /** - * Indicates if the current tests are been run in Parallel. + * Indicates if the current tests are been run in parallel. * * @return bool */ From a3109db3904109f8dc7fba9776736b1b144e6d69 Mon Sep 17 00:00:00 2001 From: Nuno Maduro Date: Wed, 13 Jan 2021 10:30:58 +0100 Subject: [PATCH 39/39] Renames "refresh-databases" to "recreate-databases" --- src/Illuminate/Testing/Concerns/TestDatabases.php | 2 +- tests/Testing/ParallelTestingTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Illuminate/Testing/Concerns/TestDatabases.php b/src/Illuminate/Testing/Concerns/TestDatabases.php index b06cd8a6a2cb..a9da82eead99 100644 --- a/src/Illuminate/Testing/Concerns/TestDatabases.php +++ b/src/Illuminate/Testing/Concerns/TestDatabases.php @@ -28,7 +28,7 @@ protected function bootTestDatabase() { ParallelTesting::setUpProcess(function () { $this->whenNotUsingInMemoryDatabase(function ($database) { - if (ParallelTesting::option('refresh_databases')) { + if (ParallelTesting::option('recreate_databases')) { Schema::dropDatabaseIfExists( $this->testDatabase($database) ); diff --git a/tests/Testing/ParallelTestingTest.php b/tests/Testing/ParallelTestingTest.php index 19402debf7b7..25d41f7f1b5c 100644 --- a/tests/Testing/ParallelTestingTest.php +++ b/tests/Testing/ParallelTestingTest.php @@ -55,14 +55,14 @@ public function testOptions() { $parallelTesting = new ParallelTesting(Container::getInstance()); - $this->assertFalse($parallelTesting->option('refresh_databases')); + $this->assertFalse($parallelTesting->option('recreate_databases')); $parallelTesting->resolveOptionsUsing(function ($option) { - return $option == 'refresh_databases'; + return $option == 'recreate_databases'; }); $this->assertFalse($parallelTesting->option('recreate_caches')); - $this->assertTrue($parallelTesting->option('refresh_databases')); + $this->assertTrue($parallelTesting->option('recreate_databases')); } public function testToken()