Skip to content

Commit 300ccf3

Browse files
Add support for running tests against SQLite database (#181)
* Add support for running tests against SQLite database This adds support for the SQLite Database Integration plugin, which allows WordPress to be installed using a SQLite databse instead of MySQL. If the environment variable DB_TYPE is set to SQLITE then we will install the SQLite Database Integration plugin and uses its db.php dropin to addn SQLite support to the copy of WordPress that tests are run against. * Update src/Context/FeatureContext.php Co-authored-by: Pascal Birchler <[email protected]> * Suggested changes * Change this logic since exiting tests do not define DB_TYPE * Make sure cached core files are in the proper state When switching between MySQL and SQLite behat might use the same set of cached core files, so make sure they are set up properly to use the correct database method for the current run. * Fix phpunit tests to work with new tags * Fix more phpunit tests * Remove previous hardcoded cache dir * Make downloading and configuring sqlite two separate steps per suggetions Now we can keep one cached copy of the plugin separate from core WordPress files and only use it when DB_TYPE=sqlite * Fix Typo * Update FeatureContext.php Co-authored-by: Pascal Birchler <[email protected]> * Remove db type check --------- Co-authored-by: Pascal Birchler <[email protected]>
1 parent a67825b commit 300ccf3

File tree

4 files changed

+171
-13
lines changed

4 files changed

+171
-13
lines changed

features/testing.feature

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,21 @@ Feature: Test that WP-CLI loads.
2323
"""
2424
DISABLE_WP_CRON is: true
2525
"""
26+
27+
@require-sqlite
28+
Scenario: Uses SQLite
29+
Given a WP install
30+
When I run `wp eval 'echo DB_ENGINE;'`
31+
Then STDOUT should contain:
32+
"""
33+
sqlite
34+
"""
35+
36+
@require-mysql
37+
Scenario: Uses MySQL
38+
Given a WP install
39+
When I run `wp eval 'var_export( defined("DB_ENGINE") );'`
40+
Then STDOUT should be:
41+
"""
42+
false
43+
"""

src/Context/FeatureContext.php

Lines changed: 125 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class FeatureContext implements SnippetAcceptingContext {
4545
*/
4646
private static $install_cache_dir;
4747

48+
/**
49+
* The directory that holds a copy of the sqlite-database-integration plugin, and which is copied to RUN_DIR during a "Given a WP installation" step. Lives until manually deleted.
50+
*/
51+
private static $sqlite_cache_dir;
52+
4853
/**
4954
* The directory that the WP-CLI cache (WP_CLI_CACHE_DIR, normally "$HOME/.wp-cli/cache") is set to on a "Given an empty cache" step.
5055
* Variable SUITE_CACHE_DIR. Lives until the end of the scenario (or until another "Given an empty cache" step within the scenario).
@@ -67,6 +72,11 @@ class FeatureContext implements SnippetAcceptingContext {
6772
'dbhost' => '127.0.0.1',
6873
];
6974

75+
/**
76+
* What type of database should WordPress use for the test installations. Default to MySQL
77+
*/
78+
private static $db_type = 'mysql';
79+
7080
/**
7181
* Array of background process ids started by the current scenario. Used to terminate them at the end of the scenario.
7282
*/
@@ -319,14 +329,82 @@ private static function get_behat_internal_variables() {
319329
return $variables;
320330
}
321331

332+
/**
333+
* Download and extract a single copy of the sqlite-database-integration plugin
334+
* for use in subsequent WordPress copies
335+
*/
336+
private static function download_sqlite_plugin( $dir ) {
337+
$download_url = 'https://downloads.wordpress.org/plugin/sqlite-database-integration.zip';
338+
$download_location = $dir . '/sqlite-database-integration.zip';
339+
340+
if ( ! is_dir( $dir ) ) {
341+
mkdir( $dir );
342+
}
343+
344+
Process::create(
345+
Utils\esc_cmd(
346+
'curl -sSfL %1$s > %2$s',
347+
$download_url,
348+
$download_location
349+
)
350+
)->run_check();
351+
352+
$zip = new \ZipArchive();
353+
$new_zip_file = $download_location;
354+
355+
if ( $zip->open( $new_zip_file ) === true ) {
356+
if ( $zip->extractTo( $dir ) ) {
357+
$zip->close();
358+
unlink( $new_zip_file );
359+
} else {
360+
$error_message = $zip->getStatusString();
361+
throw new RuntimeException( sprintf( 'Failed to extract files from the zip: %s', $error_message ) );
362+
}
363+
} else {
364+
$error_message = $zip->getStatusString();
365+
throw new RuntimeException( sprintf( 'Failed to open the zip file: %s', $error_message ) );
366+
}
367+
}
368+
369+
/**
370+
* Given a WordPress installation with the sqlite-database-integration plugin,
371+
* configure it to use SQLite as the database by placing the db.php dropin file
372+
*/
373+
private static function configure_sqlite( $dir ) {
374+
$db_copy = $dir . '/wp-content/plugins/sqlite-database-integration/db.copy';
375+
$db_dropin = $dir . '/wp-content/db.php';
376+
377+
/* similar to https://github.com/WordPress/sqlite-database-integration/blob/3306576c9b606bc23bbb26c15383fef08e03ab11/activate.php#L95 */
378+
$file_contents = str_replace(
379+
array(
380+
'\'{SQLITE_IMPLEMENTATION_FOLDER_PATH}\'',
381+
'{SQLITE_PLUGIN}',
382+
),
383+
array(
384+
'__DIR__ . \'plugins/sqlite-database-integration\'',
385+
'sqlite-database-integration/load.php',
386+
),
387+
file_get_contents( $db_copy )
388+
);
389+
390+
file_put_contents( $db_dropin, $file_contents );
391+
}
392+
322393
/**
323394
* We cache the results of `wp core download` to improve test performance.
324395
* Ideally, we'd cache at the HTTP layer for more reliable tests.
325396
*/
326397
private static function cache_wp_files() {
327-
$wp_version = getenv( 'WP_VERSION' );
328-
$wp_version_suffix = ( false !== $wp_version ) ? "-$wp_version" : '';
329-
self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix;
398+
$wp_version = getenv( 'WP_VERSION' );
399+
$wp_version_suffix = ( false !== $wp_version ) ? "-$wp_version" : '';
400+
self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix;
401+
self::$sqlite_cache_dir = sys_get_temp_dir() . '/wp-cli-test-sqlite-integration-cache';
402+
403+
if ( 'sqlite' === getenv( 'DB_TYPE' ) ) {
404+
if ( ! is_readable( self::$sqlite_cache_dir . '/sqlite-database-integration/db.copy' ) ) {
405+
self::download_sqlite_plugin( self::$sqlite_cache_dir );
406+
}
407+
}
330408

331409
if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) {
332410
return;
@@ -533,6 +611,10 @@ public function __construct() {
533611
$this->variables['MYSQL_HOST'] = getenv( 'MYSQL_HOST' );
534612
}
535613

614+
if ( 'sqlite' === getenv( 'DB_TYPE' ) ) {
615+
self::$db_type = 'sqlite';
616+
}
617+
536618
self::$db_settings['dbname'] = $this->variables['DB_NAME'];
537619
self::$db_settings['dbuser'] = $this->variables['DB_USER'];
538620
self::$db_settings['dbpass'] = $this->variables['DB_PASSWORD'];
@@ -757,11 +839,18 @@ private static function run_sql( $sql_cmd, $assoc_args = [], $add_database = fal
757839
}
758840

759841
public function create_db() {
842+
if ( 'sqlite' === self::$db_type ) {
843+
return;
844+
}
845+
760846
$dbname = self::$db_settings['dbname'];
761847
self::run_sql( 'mysql --no-defaults', [ 'execute' => "CREATE DATABASE IF NOT EXISTS $dbname" ] );
762848
}
763849

764850
public function drop_db() {
851+
if ( 'sqlite' === self::$db_type ) {
852+
return;
853+
}
765854
$dbname = self::$db_settings['dbname'];
766855
self::run_sql( 'mysql --no-defaults', [ 'execute' => "DROP DATABASE IF EXISTS $dbname" ] );
767856
}
@@ -854,6 +943,12 @@ public function download_wp( $subdir = '' ) {
854943

855944
// Add polyfills.
856945
copy( dirname( dirname( __DIR__ ) ) . '/utils/polyfills.php', $dest_dir . '/wp-content/mu-plugins/polyfills.php' );
946+
947+
if ( 'sqlite' === self::$db_type ) {
948+
self::copy_dir( self::$sqlite_cache_dir, $dest_dir . '/wp-content/plugins' );
949+
self::configure_sqlite( $dest_dir );
950+
951+
}
857952
}
858953

859954
public function create_config( $subdir = '', $extra_php = false ) {
@@ -897,7 +992,9 @@ public function install_wp( $subdir = '' ) {
897992
// Disable WP Cron by default to avoid bogus HTTP requests in CLI context.
898993
$config_extra_php = "if ( ! defined( 'DISABLE_WP_CRON' ) ) { define( 'DISABLE_WP_CRON', true ); }\n";
899994

900-
$this->create_db();
995+
if ( 'mysql' === self::$db_type ) {
996+
$this->create_db();
997+
}
901998
$this->create_run_dir();
902999
$this->download_wp( $subdir );
9031000
$this->create_config( $subdir, $config_extra_php );
@@ -919,21 +1016,33 @@ public function install_wp( $subdir = '' ) {
9191016

9201017
if ( $install_cache_path && file_exists( $install_cache_path ) ) {
9211018
self::copy_dir( $install_cache_path, $run_dir );
922-
self::run_sql( 'mysql --no-defaults', [ 'execute' => "source {$install_cache_path}.sql" ], true /*add_database*/ );
1019+
1020+
// This is the sqlite equivalent of restoring a database dump in MySQL
1021+
if ( 'sqlite' === self::$db_type ) {
1022+
copy( "{$install_cache_path}.sqlite", "$run_dir/wp-content/database/.ht.sqlite" );
1023+
} else {
1024+
self::run_sql( 'mysql --no-defaults', [ 'execute' => "source {$install_cache_path}.sql" ], true /*add_database*/ );
1025+
}
9231026
} else {
9241027
$this->proc( 'wp core install', $install_args, $subdir )->run_check();
9251028
if ( $install_cache_path ) {
9261029
mkdir( $install_cache_path );
9271030
self::dir_diff_copy( $run_dir, self::$cache_dir, $install_cache_path );
9281031

929-
$mysqldump_binary = Utils\force_env_on_nix_systems( 'mysqldump' );
930-
$support_column_statistics = exec( "{$mysqldump_binary} --help | grep 'column-statistics'" );
931-
$command = "{$mysqldump_binary} --no-defaults --no-tablespaces";
932-
if ( $support_column_statistics ) {
933-
$command .= ' --skip-column-statistics';
1032+
if ( 'mysql' === self::$db_type ) {
1033+
$mysqldump_binary = Utils\force_env_on_nix_systems( 'mysqldump' );
1034+
$support_column_statistics = exec( "{$mysqldump_binary} --help | grep 'column-statistics'" );
1035+
$command = "{$mysqldump_binary} --no-defaults --no-tablespaces";
1036+
if ( $support_column_statistics ) {
1037+
$command .= ' --skip-column-statistics';
1038+
}
1039+
self::run_sql( $command, [ 'result-file' => "{$install_cache_path}.sql" ], true /*add_database*/ );
9341040
}
9351041

936-
self::run_sql( $command, [ 'result-file' => "{$install_cache_path}.sql" ], true /*add_database*/ );
1042+
if ( 'sqlite' === self::$db_type ) {
1043+
// This is the sqlite equivalent of creating a database dump in MySQL
1044+
copy( "$run_dir/wp-content/database/.ht.sqlite", "{$install_cache_path}.sqlite" );
1045+
}
9371046
}
9381047
}
9391048
}
@@ -969,6 +1078,11 @@ public function install_wp_with_composer( $vendor_directory = 'vendor' ) {
9691078
'skip-email' => true,
9701079
];
9711080

1081+
if ( 'sqlite' === self::$db_type ) {
1082+
self::copy_dir( self::$sqlite_cache_dir, $this->variables['RUN_DIR'] . '/WordPress/wp-content/plugins' );
1083+
self::configure_sqlite( $this->variables['RUN_DIR'] . '/WordPress' );
1084+
}
1085+
9721086
$this->proc( 'wp core install', $install_args )->run_check();
9731087
}
9741088

tests/test-behat-tags.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ protected function tear_down() {
3434
public function test_behat_tags_wp_version_github_token( $env, $expected ) {
3535
$env_wp_version = getenv( 'WP_VERSION' );
3636
$env_github_token = getenv( 'GITHUB_TOKEN' );
37+
$db_type = getenv( 'DB_TYPE' );
3738

3839
putenv( 'WP_VERSION' );
3940
putenv( 'GITHUB_TOKEN' );
@@ -49,6 +50,14 @@ public function test_behat_tags_wp_version_github_token( $env, $expected ) {
4950
if ( in_array( $env, array( 'WP_VERSION=trunk', 'WP_VERSION=nightly' ), true ) ) {
5051
$expected .= '&&~@broken-trunk';
5152
}
53+
54+
if ( 'sqlite' !== $db_type ) {
55+
$expected .= '&&~@require-sqlite';
56+
}
57+
if ( 'sqlite' === $db_type ) {
58+
$expected .= '&&~@require-mysql';
59+
}
60+
5261
$this->assertSame( '--tags=' . $expected, $output );
5362

5463
putenv( false === $env_wp_version ? 'WP_VERSION' : "WP_VERSION=$env_wp_version" );
@@ -122,13 +131,14 @@ public function test_behat_tags_php_version() {
122131
file_put_contents( $this->temp_dir . '/features/php_version.feature', $contents );
123132

124133
$output = exec( "cd {$this->temp_dir}; php $behat_tags" );
125-
$this->assertSame( '--tags=' . $expected . '&&~@github-api&&~@broken', $output );
134+
$this->assertSame( '--tags=' . $expected . '&&~@github-api&&~@broken&&~@require-sqlite', $output );
126135

127136
putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" );
128137
}
129138

130139
public function test_behat_tags_extension() {
131140
$env_github_token = getenv( 'GITHUB_TOKEN' );
141+
$db_type = getenv( 'DB_TYPE' );
132142

133143
putenv( 'GITHUB_TOKEN' );
134144

@@ -137,12 +147,20 @@ public function test_behat_tags_extension() {
137147
file_put_contents( $this->temp_dir . '/features/extension.feature', '@require-extension-imagick @require-extension-curl' );
138148

139149
$expecteds = array();
150+
151+
if ( 'sqlite' !== $db_type ) {
152+
$expecteds[] = '~@require-sqlite';
153+
}
154+
if ( 'sqlite' === $db_type ) {
155+
$expecteds[] = '~@require-mysql';
156+
}
140157
if ( ! extension_loaded( 'imagick' ) ) {
141158
$expecteds[] = '~@require-extension-imagick';
142159
}
143160
if ( ! extension_loaded( 'curl' ) ) {
144161
$expecteds[] = '~@require-extension-curl';
145162
}
163+
146164
$expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) );
147165
$output = exec( "cd {$this->temp_dir}; php $behat_tags" );
148166
$this->assertSame( $expected, $output );

utils/behat-tags.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,22 @@ function version_tags(
7272
if ( ! getenv( 'GITHUB_TOKEN' ) ) {
7373
$skip_tags[] = '@github-api';
7474
}
75-
7675
# Skip tests known to be broken.
7776
$skip_tags[] = '@broken';
7877

7978
if ( $wp_version && in_array( $wp_version, array( 'nightly', 'trunk' ), true ) ) {
8079
$skip_tags[] = '@broken-trunk';
8180
}
8281

82+
if ( 'sqlite' === getenv( 'DB_TYPE' ) ) {
83+
$skip_tags[] = '@require-mysql';
84+
}
85+
86+
if ( 'sqlite' !== getenv( 'DB_TYPE' ) ) {
87+
$skip_tags[] = '@require-sqlite';
88+
}
89+
90+
8391
# Require PHP extension, eg 'imagick'.
8492
function extension_tags( $features_folder = 'features' ) {
8593
$extension_tags = array();

0 commit comments

Comments
 (0)