diff --git a/tests/phpunit/tests/admin/wpUpgrader.php b/tests/phpunit/tests/admin/wpUpgrader.php index 52fcdd0b457d4..a9b88e512c101 100644 --- a/tests/phpunit/tests/admin/wpUpgrader.php +++ b/tests/phpunit/tests/admin/wpUpgrader.php @@ -1,26 +1,26 @@ assertInstanceOf( WP_Upgrader_Skin::class, $instance->skin ); } /** - * The init method should set up the class and skin. + * Tests that `WP_Upgrader::init()` sets up the skin and strings. + * + * @covers WP_Upgrader::init */ - public function test_init() { + public function test_init_should_set_up_the_skin_and_strings() { $this->upgrader_skin_mock - ->expects( $this->once() ) - ->method( 'set_upgrader' ) - ->with( $this->instance ); + ->expects( $this->once() ) + ->method( 'set_upgrader' ) + ->with( $this->instance ); $this->instance->init(); - $this->assertNotEmpty( $this->instance->strings['bad_request'] ); - $this->assertNotEmpty( $this->instance->strings['fs_unavailable'] ); - $this->assertNotEmpty( $this->instance->strings['fs_error'] ); - $this->assertNotEmpty( $this->instance->strings['fs_no_root_dir'] ); - $this->assertNotEmpty( $this->instance->strings['fs_no_content_dir'] ); - $this->assertNotEmpty( $this->instance->strings['fs_no_plugins_dir'] ); - $this->assertNotEmpty( $this->instance->strings['fs_no_themes_dir'] ); - $this->assertNotEmpty( $this->instance->strings['fs_no_folder'] ); - $this->assertNotEmpty( $this->instance->strings['download_failed'] ); - $this->assertNotEmpty( $this->instance->strings['installing_package'] ); - $this->assertNotEmpty( $this->instance->strings['no_files'] ); - $this->assertNotEmpty( $this->instance->strings['folder_exists'] ); - $this->assertNotEmpty( $this->instance->strings['mkdir_failed'] ); - $this->assertNotEmpty( $this->instance->strings['incompatible_archive'] ); - $this->assertNotEmpty( $this->instance->strings['files_not_writable'] ); - $this->assertNotEmpty( $this->instance->strings['maintenance_start'] ); - $this->assertNotEmpty( $this->instance->strings['maintenance_end'] ); - $this->assertNotEmpty( $this->instance->strings['temp_backup_mkdir_failed'] ); - $this->assertNotEmpty( $this->instance->strings['temp_backup_move_failed'] ); - $this->assertNotEmpty( $this->instance->strings['temp_backup_restore_failed'] ); - - // The backup cleanup is scheduled. - $this->assertIsInt( wp_next_scheduled( 'delete_temp_updater_backups' ) ); - $this->assertIsInt( - has_action( - 'delete_temp_updater_backups', - array( - $this->instance, - 'delete_all_temp_backups', - ) - ) + $this->assertNotEmpty( $this->instance->strings['bad_request'], 'The "bad_request" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_unavailable'], 'The "fs_unavailable" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_error'], 'The "fs_error" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_no_root_dir'], 'The "fs_no_root_dir" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_no_content_dir'], 'The "fs_no_content_dir" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_no_plugins_dir'], 'The "fs_no_plugins_dir" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_no_themes_dir'], 'The "fs_no_themes_dir" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['fs_no_folder'], 'The "fs_no_folder" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['download_failed'], 'The "download_failed" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['installing_package'], 'The "installing_package" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['no_files'], 'The "no_files" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['folder_exists'], 'The "folder_exists" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['mkdir_failed'], 'The "mkdir_failed" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['incompatible_archive'], 'The "incompatible_archive" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['files_not_writable'], 'The "files_not_writable" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['maintenance_start'], 'The "maintenance_start" string was not created' ); + $this->assertNotEmpty( $this->instance->strings['maintenance_end'], 'The "maintenance_end" string was not created' ); + } + + /** + * Tests that `WP_Upgrader::flatten_dirlist()` returns the expected file list. + * + * @ticket 54245 + * + * @dataProvider data_should_flatten_dirlist + * + * @covers WP_Upgrader::flatten_dirlist + * + * @param array $expected The expected flattened dirlist. + * @param array $nested_files Array of files as returned by WP_Filesystem_Base::dirlist(). + * @param string $path Optional. Relative path to prepend to child nodes. Default empty string. + */ + public function test_should_flatten_dirlist( $expected, $nested_files, $path = '' ) { + // `WP_Upgrader::flatten_dirlist()` has `protected` access. + $flatten_dirlist = new ReflectionMethod( $this->instance, 'flatten_dirlist' ); + $flatten_dirlist->setAccessible( true ); + + $this->assertSameSetsWithIndex( + $expected, + $flatten_dirlist->invoke( $this->instance, $nested_files, $path ) ); } /** - * Disabling maintenance mode should remove the .maintenance file. + * Data provider: Provides fake results from `WP_Filesystem_*::dirlist()` + * for use in `WP_Upgrader::flatten_dirlist()`. + * + * @return array */ - public function test_maintenance_mode_disable() { - $this->wp_filesystem_mock->expects( $this->once() )->method( 'abspath' )->willReturn( '/' ); - $this->wp_filesystem_mock->expects( $this->once() )->method( 'exists' )->with( '/.maintenance' )->willReturn( true ); - $this->upgrader_skin_mock->expects( $this->once() )->method( 'feedback' )->with( 'maintenance_end' ); + public function data_should_flatten_dirlist() { + return array( + 'empty array, default path' => array( + 'expected' => array(), + 'nested_files' => array(), + ), + 'root only' => array( + 'expected' => array( + 'file1.php' => array( 'name' => 'file1.php' ), + 'file2.php' => array( 'name' => 'file2.php' ), + ), + 'nested_files' => array( + 'file1.php' => array( 'name' => 'file1.php' ), + 'file2.php' => array( 'name' => 'file2.php' ), + ), + ), + 'root only and custom path' => array( + 'expected' => array( + 'custom_path/file1.php' => array( 'name' => 'file1.php' ), + 'custom_path/file2.php' => array( 'name' => 'file2.php' ), + ), + 'nested_files' => array( + 'file1.php' => array( 'name' => 'file1.php' ), + 'file2.php' => array( 'name' => 'file2.php' ), + ), + 'path' => 'custom_path/', + ), + 'one level deep' => array( + 'expected' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + ), + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + ), + ), + 'subdir1/subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subdir1/subfile2.php' => array( 'name' => 'subfile2.php' ), + 'subdir2/subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subdir2/subfile4.php' => array( 'name' => 'subfile4.php' ), + ), + 'nested_files' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + ), + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + ), + ), + ), + ), + 'one level deep and custom path' => array( + 'expected' => array( + 'custom_path/subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + ), + ), + 'custom_path/subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + ), + ), + 'custom_path/subdir1/subfile1.php' => array( + 'name' => 'subfile1.php', + ), + 'custom_path/subdir1/subfile2.php' => array( + 'name' => 'subfile2.php', + ), + 'custom_path/subdir2/subfile3.php' => array( + 'name' => 'subfile3.php', + ), + 'custom_path/subdir2/subfile4.php' => array( + 'name' => 'subfile4.php', + ), + ), + 'nested_files' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + ), + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + ), + ), + ), + 'path' => 'custom_path/', + ), + 'two levels deep' => array( + 'expected' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( + 'name' => 'subfile1.php', + ), + 'subfile2.php' => array( + 'name' => 'subfile2.php', + ), + 'subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + ), + ), + 'subdir1/subfile1.php' => array( + 'name' => 'subfile1.php', + ), + 'subdir1/subfile2.php' => array( + 'name' => 'subfile2.php', + ), + 'subdir1/subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + 'subdir1/subsubdir1/subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subdir1/subsubdir1/subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + 'subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + ), + ), + 'subdir2/subfile3.php' => array( + 'name' => 'subfile3.php', + ), + 'subdir2/subfile4.php' => array( + 'name' => 'subfile4.php', + ), + 'subdir2/subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + 'subdir2/subsubdir2/subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subdir2/subsubdir2/subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + 'nested_files' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + 'subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + ), + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + 'subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + ), + ), + ), + ), + 'two levels deep and custom path' => array( + 'expected' => array( + 'custom_path/subdir1' => array( + 'files' => array( + 'subfile1.php' => array( + 'name' => 'subfile1.php', + ), + 'subfile2.php' => array( + 'name' => 'subfile2.php', + ), + 'subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + ), + ), + 'custom_path/subdir1/subfile1.php' => array( + 'name' => 'subfile1.php', + ), + 'custom_path/subdir1/subfile2.php' => array( + 'name' => 'subfile2.php', + ), + 'custom_path/subdir1/subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + 'custom_path/subdir1/subsubdir1/subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'custom_path/subdir1/subsubdir1/subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + 'custom_path/subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + 'subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + ), + ), + 'custom_path/subdir2/subfile3.php' => array( + 'name' => 'subfile3.php', + ), + 'custom_path/subdir2/subfile4.php' => array( + 'name' => 'subfile4.php', + ), + 'custom_path/subdir2/subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + 'custom_path/subdir2/subsubdir2/subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'custom_path/subdir2/subsubdir2/subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + 'nested_files' => array( + 'subdir1' => array( + 'files' => array( + 'subfile1.php' => array( 'name' => 'subfile1.php' ), + 'subfile2.php' => array( 'name' => 'subfile2.php' ), + 'subsubdir1' => array( + 'files' => array( + 'subsubfile1.php' => array( + 'name' => 'subsubfile1.php', + ), + 'subsubfile2.php' => array( + 'name' => 'subsubfile2.php', + ), + ), + ), + ), + ), + 'subdir2' => array( + 'files' => array( + 'subfile3.php' => array( 'name' => 'subfile3.php' ), + 'subfile4.php' => array( 'name' => 'subfile4.php' ), + 'subsubdir2' => array( + 'files' => array( + 'subsubfile3.php' => array( + 'name' => 'subsubfile3.php', + ), + 'subsubfile4.php' => array( + 'name' => 'subsubfile4.php', + ), + ), + ), + ), + ), + ), + 'path' => 'custom_path/', + ), + ); + } - $this->wp_filesystem_mock->expects( $this->once() )->method( 'delete' )->with( '/.maintenance' ); + /** + * Tests that `WP_Upgrader::clear_destination()` returns early with `true` + * when the destination does not exist. + * + * @ticket 54245 + * + * @covers WP_Upgrader::clear_destination + */ + public function test_clear_destination_should_return_early_when_the_destination_does_not_exist() { + $this->wp_filesystem_mock->expects( $this->never() )->method( 'is_writable' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'chmod' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); - $this->instance->maintenance_mode(); + $destination = DIR_TESTDATA . '/upgrade/'; + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( $destination ) + ->willReturn( false ); + + $this->assertTrue( $this->instance->clear_destination( $destination ) ); } /** - * Disabling maintenance mode do nothing if the .maintenance file does not exist. + * Tests that `WP_Upgrader::clear_destination()` clears + * the destination directory. + * + * @ticket 54245 + * + * @covers WP_Upgrader::clear_destination */ - public function test_maintenance_mode_disable_no_file_exists() { - $this->wp_filesystem_mock->expects( $this->once() )->method( 'abspath' )->willReturn( '/' ); - $this->wp_filesystem_mock->expects( $this->once() )->method( 'exists' )->with( '/.maintenance' )->willReturn( false ); - $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + public function test_should_clear_the_destination_directory() { + $destination = DIR_TESTDATA . '/upgrade/'; + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( $destination ) + ->willReturn( array() ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'delete' ) + ->with( $destination ) + ->willReturn( true ); + + $this->assertTrue( $this->instance->clear_destination( $destination ) ); + } + + /** + * Tests that `WP_Upgrader::clear_destination()` returns a WP_Error object + * if files are not writable. + * + * @ticket 54245 + * + * @covers WP_Upgrader::clear_destination + */ + public function test_should_return_wp_error_if_files_are_not_writable() { + $this->instance->generic_strings(); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); - $this->instance->maintenance_mode(); + if ( ! defined( 'FS_CHMOD_DIR' ) ) { + define( 'FS_CHMOD_DIR', 0755 ); + } + + if ( ! defined( 'FS_CHMOD_FILE' ) ) { + define( 'FS_CHMOD_FILE', 0644 ); + } + + $destination = DIR_TESTDATA . '/upgrade/'; + $dirlist = array( + 'file1.php' => array( + 'name' => 'file1.php', + 'type' => 'f', + ), + 'subdir' => array( + 'name' => 'subdir', + 'type' => 'd', + ), + ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( $destination ) + ->willReturn( $dirlist ); + + $unwritable_checks = array( + array( $destination . 'file1.php' ), + array( $destination . 'file1.php' ), + array( $destination . 'subdir' ), + array( $destination . 'subdir' ), + ); + + $this->wp_filesystem_mock + ->expects( $this->exactly( 4 ) ) + ->method( 'is_writable' ) + ->withConsecutive( ...$unwritable_checks ) + ->willReturn( false ); + + $actual = $this->instance->clear_destination( $destination ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::clear_destination() did not return a WP_Error object' + ); + + $this->assertSame( + 'files_not_writable', + $actual->get_error_code(), + 'The WP_Error code was not "files_not_writable"' + ); + + $this->assertSameSets( + array( 'file1.php, subdir' ), + $actual->get_all_error_data(), + 'The WP_Error object did not contain the expected data' + ); } /** - * Enabling maintenance mode should create a .maintenance file. + * Tests that `WP_Upgrader::clear_destination()` returns a WP_Error object + * if the destination directory is not cleared. + * + * @ticket 54245 + * + * @covers WP_Upgrader::clear_destination */ - public function test_maintenance_mode_enable() { - $this->wp_filesystem_mock->expects( $this->once() )->method( 'abspath' )->willReturn( '/' ); - $this->upgrader_skin_mock->expects( $this->once() )->method( 'feedback' )->with( 'maintenance_start' ); - $this->wp_filesystem_mock->expects( $this->once() )->method( 'delete' )->with( '/.maintenance' ); - $this->wp_filesystem_mock->expects( $this->once() )->method( 'put_contents' )->with( '/.maintenance', $this->stringContains( 'instance->generic_strings(); - $this->instance->maintenance_mode( true ); + $destination = DIR_TESTDATA . '/upgrade/'; + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( $destination ) + ->willReturn( array() ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'delete' ) + ->with( $destination ) + ->willReturn( false ); + + $actual = $this->instance->clear_destination( $destination ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::clear_destination() did not return a WP_Error object' + ); + + $this->assertSame( + 'remove_old_failed', + $actual->get_error_code(), + 'The WP_Error code was not "remove_old_failed"' + ); } /** - * Enabling maintenance mode should create a .maintenance file. + * Tests that `WP_Upgrader::install_package()` returns a WP_Error object + * when invalid arguments are passed. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package + * + * @dataProvider data_install_package_should_return_wp_error_with_invalid_arguments + * + * @param array $arguments The arguments to test against. */ - public function test_maintenance_mode_non_boolean() { - $this->wp_filesystem_mock->expects( $this->never() )->method( 'abspath' ); + public function test_install_package_should_return_wp_error_with_invalid_arguments( $arguments ) { + $this->instance->generic_strings(); + $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'dirlist' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'find_folder' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'is_dir' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'exists' ); $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); - $this->wp_filesystem_mock->expects( $this->never() )->method( 'put_contents' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'mkdir' ); - $this->instance->maintenance_mode( new stdClass() ); + $actual = $this->instance->install_package( $arguments ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::install_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'bad_request', + $actual->get_error_code(), + 'The WP_Error code was not "bad_request"' + ); } /** - * Release lock should remove the option. + * Data provider: Provides invalid arguments for `WP_Upgrader::install_package()`. + * + * @return array */ - public function test_release_lock() { - update_option( 'lock.lock', 'content' ); + public function data_install_package_should_return_wp_error_with_invalid_arguments() { + return array( + 'no source' => array( 'arguments' => array( 'destination' => '/' ) ), + 'no destination' => array( 'arguments' => array( 'source' => '/' ) ), + ); + } - WP_Upgrader::release_lock( 'lock' ); + /** + * Tests that `WP_Upgrader::install_package()` returns a WP_Error object + * when the 'upgrader_pre_install' filter returns a WP_Error object. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package + */ + public function test_install_package_should_return_wp_error_when_pre_install_filter_returns_wp_error() { + $this->instance->generic_strings(); - $this->assertNotSame( 'content', get_option( 'lock.lock' ) ); + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + add_filter( + 'upgrader_pre_install', + static function() { + return new WP_Error( 'from_upgrader_pre_install' ); + } + ); + + $args = array( + 'source' => '/', + 'destination' => '/', + ); + + $actual = $this->instance->install_package( $args ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::install_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'from_upgrader_pre_install', + $actual->get_error_code(), + 'The WP_Error object was not returned from the filter' + ); } /** - * Delete temp backup should call delete on the filesystem. + * Tests that `WP_Upgrader::install_package()` adds a trailing slash to + * the source directory and a single subdirectory. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package */ - public function test_delete_temp_backup() { - $this->wp_filesystem_mock->expects( $this->once() )->method( 'wp_content_dir' )->willReturn( '/' ); - $this->wp_filesystem_mock->expects( $this->once() )->method( 'delete' )->with( '/upgrade/temp-backup/dir/slug', true ); + public function test_install_package_should_add_trailing_slash_to_source_and_subdirectory() { + $this->instance->generic_strings(); - $this->instance->delete_temp_backup( - array( - 'slug' => 'slug', - 'dir' => 'dir', - ) + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + $dirlist = array( + 'subdir' => array( + 'name' => 'subdir', + 'type' => 'd', + 'files' => array( 'subfile.php' ), + ), + ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( '/source_dir' ) + ->willReturn( $dirlist ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'is_dir' ) + ->with( '/source_dir/subdir/' ) + ->willReturn( true ); + + add_filter( + 'upgrader_source_selection', + function( $source ) { + $this->assertSame( '/source_dir/subdir/', $source ); + + // Return a WP_Error to exit before `move_dir()/copy_dir()`. + return new WP_Error(); + } + ); + + $args = array( + 'source' => '/source_dir', + 'destination' => '/dest_dir', ); + + $this->instance->install_package( $args ); } /** - * Delete temp backup should return early with invalid arguments. + * Tests that `WP_Upgrader::install_package()` returns a WP_Error object + * when no source files exist. * - * @param array $arguments The arguments to test against. + * @ticket 54245 * - * @dataProvider delete_temp_backup_invalid_arguments_dataprovider + * @covers WP_Upgrader::install_package */ - public function test_delete_temp_backup_invalid_arguments( $arguments ) { - $this->wp_filesystem_mock->expects( $this->never() )->method( 'wp_content_dir' ); - $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); + public function test_install_package_should_return_wp_error_when_no_source_files_exist() { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( '/' ) + ->willReturn( array() ); + + $args = array( + 'source' => '/', + 'destination' => '/', + ); + + $actual = $this->instance->install_package( $args ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::install_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'incompatible_archive_empty', + $actual->get_error_code(), + 'The WP_Error code was not "incompatible_archive_empty"' + ); + } + + /** + * Tests that `WP_Upgrader::install_package()` adds a trailing slash to + * the source directory of a single file. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package + */ + public function test_install_package_should_add_trailing_slash_to_the_source_directory_of_single_file() { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'dirlist' ) + ->with( '/source_dir' ) + ->willReturn( array( 'file1.php' ) ); + + add_filter( + 'upgrader_source_selection', + function( $source ) { + $this->assertSame( '/source_dir/', $source ); + + // Return a WP_Error to exit before `move_dir()/copy_dir()`. + return new WP_Error(); + } + ); + + $args = array( + 'source' => '/source_dir', + 'destination' => '/dest_dir', + ); - $this->instance->delete_temp_backup( $arguments ); + $this->instance->install_package( $args ); } /** - * Invalid arguments for the delete_temp_backup method call. + * Tests that `WP_Upgrader::install_package()` applies + * 'upgrader_clear_destination' filters with arguments. + * + * @ticket 54245 * - * @return array[][] + * @covers WP_Upgrader::install_package */ - public function delete_temp_backup_invalid_arguments_dataprovider() { + public function test_install_package_should_clear_destination_when_clear_destination_is_true() { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->exactly( 2 ) ) + ->method( 'feedback' ) + ->withConsecutive( + array( 'installing_package' ), + array( 'remove_old' ) + ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'find_folder' ) + ->with( '/dest_dir' ) + ->willReturn( '/dest_dir/' ); + + $dirlist_args = array( + array( '/source_dir' ), + array( '/source_dir/' ), + array( '/dest_dir/' ), + ); + + $dirlist_results = array( + 'file1.php' => array( + 'name' => 'file1.php', + 'type' => 'f', + ), + ); + + $this->wp_filesystem_mock + ->expects( $this->exactly( 3 ) ) + ->method( 'dirlist' ) + ->withConsecutive( ...$dirlist_args ) + ->willReturn( $dirlist_results ); + + add_filter( + 'upgrader_clear_destination', + function( $removed, $local_destination, $remote_destination, $hook_extra ) { + $this->assertTrue( + is_bool( $removed ) || is_wp_error( $removed ), + 'The "removed" argument is not a bool or WP_Error' + ); + + $this->assertIsString( + $local_destination, + 'The "local_destination" argument is not a string' + ); + + $this->assertIsString( + $remote_destination, + 'The "remote_destination" argument is not a string' + ); + + $this->assertIsArray( + $hook_extra, + 'The "hook_extra" argument is not an array' + ); + + return new WP_Error( 'exit_early' ); + }, + 10, + 4 + ); + + $args = array( + 'source' => '/source_dir', + 'destination' => '/dest_dir', + 'clear_destination' => true, + ); + + $this->instance->install_package( $args ); + } + + /** + * Tests that `WP_Upgrader::install_package()` makes the + * remote destination safe when set to a protected directory. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package + * + * @dataProvider data_install_package_should_make_remote_destination_safe_when_set_to_a_protected_directory + * + * @param string $protected_directory The path to a protected directory. + * @param string $expected The expected safe remote destination. + */ + public function test_install_package_should_make_remote_destination_safe_when_set_to_a_protected_directory( $protected_directory, $expected ) { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->exactly( 2 ) ) + ->method( 'feedback' ) + ->withConsecutive( + array( 'installing_package' ), + array( 'remove_old' ) + ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'find_folder' ) + ->with( $protected_directory ) + ->willReturn( trailingslashit( $protected_directory ) ); + + $dirlist_args = array( + array( '/source_dir' ), + array( '/source_dir/' ), + array( $expected ), + ); + + $dirlist_results = array( + 'file1.php' => array( + 'name' => 'file1.php', + 'type' => 'f', + ), + ); + + $this->wp_filesystem_mock + ->expects( $this->exactly( 3 ) ) + ->method( 'dirlist' ) + ->withConsecutive( ...$dirlist_args ) + ->willReturn( $dirlist_results ); + + add_filter( + 'upgrader_clear_destination', + function( $removed, $local_destination, $remote_destination ) use ( $expected ) { + $this->assertSame( $expected, $remote_destination ); + return new WP_Error( 'exit_early' ); + }, + 10, + 3 + ); + + $args = array( + 'source' => '/source_dir', + 'destination' => $protected_directory, + 'clear_destination' => true, + ); + + $this->instance->install_package( $args ); + } + + /** + * Data provider: Provides protected directories to `WP_Upgrader::install_package()`. + * + * @return array + */ + public function data_install_package_should_make_remote_destination_safe_when_set_to_a_protected_directory() { return array( - 'missing slug and dir' => array( - 'arguments' => array( - 'slug' => '', - 'dir' => '', - ), + 'ABSPATH' => array( + 'protected_directory' => ABSPATH, + 'expected' => ABSPATH . 'source_dir/', ), - 'missing dir' => array( - 'arguments' => array( - 'slug' => 'slug', - 'dir' => '', - ), + 'WP_CONTENT_DIR' => array( + 'protected_directory' => WP_CONTENT_DIR, + 'expected' => WP_CONTENT_DIR . '/source_dir/', ), - 'missing slug' => array( - 'arguments' => array( - 'slug' => '', - 'dir' => 'dir', - ), + 'WP_PLUGIN_DIR' => array( + 'protected_directory' => WP_PLUGIN_DIR, + 'expected' => WP_PLUGIN_DIR . '/source_dir/', ), - 'boolean - non-string slug and dir' => array( - 'arguments' => array( - 'slug' => true, - 'dir' => true, - ), + 'WP_CONTENT_DIR/themes' => array( + 'protected_directory' => WP_CONTENT_DIR . '/themes', + 'expected' => WP_CONTENT_DIR . '/themes/source_dir/', ), - 'object - non-string slug and dir' => array( - 'arguments' => array( - 'slug' => new stdClass(), - 'dir' => new stdClass(), - ), + ); + } + + /** + * Tests that `WP_Upgrader::install_package()` returns a WP_Error object + * if the destination directory exists. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package + */ + public function test_install_package_should_abort_if_the_destination_directory_exists() { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'find_folder' ) + ->with( '/dest_dir' ) + ->willReturn( '/dest_dir/' ); + + $dirlist_args = array( + array( '/source_dir' ), + array( '/source_dir/' ), + array( '/dest_dir/' ), + ); + + $dirlist_results = array( + 'file1.php' => array( + 'name' => 'file1.php', + 'type' => 'f', ), ); + + $this->wp_filesystem_mock + ->expects( $this->exactly( 3 ) ) + ->method( 'dirlist' ) + ->withConsecutive( ...$dirlist_args ) + ->willReturn( $dirlist_results ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'exists' ) + ->with( '/dest_dir/' ) + ->willReturn( true ); + + $args = array( + 'source' => '/source_dir', + 'destination' => '/dest_dir', + ); + + $actual = $this->instance->install_package( $args ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::install_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'folder_exists', + $actual->get_error_code(), + 'The WP_Error code was not "folder_exists"' + ); } /** - * Download package should exit early when filter returns non-false value. + * Tests that `WP_Upgrader::install_package()` returns a WP_Error + * if the destination directory cannot be created. + * + * @ticket 54245 + * + * @covers WP_Upgrader::install_package */ - public function test_download_package_pre_download_exit() { - $callback = function ( $reply ) { - return ! $reply; - }; + public function test_install_package_should_return_wp_error_if_destination_cannot_be_created() { + $this->instance->generic_strings(); - add_filter( 'upgrader_pre_download', $callback, 10, 1 ); + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'installing_package' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'find_folder' ) + ->with( '/dest_dir' ) + ->willReturn( '/dest_dir/' ); + + $dirlist_args = array( + array( '/source_dir' ), + array( '/source_dir/' ), + ); - $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + $dirlist_results = array( + 'file1.php' => array( + 'name' => 'file1.php', + 'type' => 'f', + ), + ); - $result = $this->instance->download_package( 'package' ); + if ( ! defined( 'FS_CHMOD_DIR' ) ) { + define( 'FS_CHMOD_DIR', 0755 ); + } + + $this->wp_filesystem_mock + ->expects( $this->exactly( 2 ) ) + ->method( 'dirlist' ) + ->withConsecutive( ...$dirlist_args ) + ->willReturn( $dirlist_results ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'exists' ) + ->with( '/dest_dir/' ) + ->willReturn( false ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'mkdir' ) + ->with( '/dest_dir/' ) + ->willReturn( false ); + + $args = array( + 'source' => '/source_dir', + 'destination' => '/dest_dir', + 'abort_if_destination_exists' => false, + ); - $this->assertTrue( $result ); + $actual = $this->instance->install_package( $args ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::install_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'mkdir_failed_destination', + $actual->get_error_code(), + 'The WP_Error code was not "mkdir_failed_destination"' + ); + } + + /** + * Tests that `WP_Upgrader::run()` returns `false` when + * requesting filesystem credentials fails. + * + * @ticket 54245 + * + * @covers WP_Upgrader::run + */ + public function test_run_returns_false_when_requesting_filesystem_credentials_fails() { + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'request_filesystem_credentials' ) + ->willReturn( false ); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'footer' ); + + $this->assertFalse( $this->instance->run( array() ) ); + } + + /** + * Tests that `WP_Upgrader::run()` returns a WP_Error object when + * downloading the package fails. + * + * @ticket 54245 + * + * @covers WP_Upgrader::run + */ + public function test_run_returns_false_when_package_download_fails() { + $this->instance->generic_strings(); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'request_filesystem_credentials' ) + ->willReturn( true ); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'error' ); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'after' ); + + $actual = $this->instance->run( array() ); + + $this->assertWPError( + $actual, + 'WP_Upgrader::run() did not return a WP_Error object' + ); - remove_filter( 'upgrader_pre_download', $callback ); + $this->assertSame( + 'no_package', + $actual->get_error_code(), + 'The WP_Error code was not "no_package"' + ); } /** - * Download package should call the upgrader_pre_download with expected arguments. + * Tests that `WP_Upgrader::maintenance_mode()` removes the `.maintenance` file. + * + * @ticket 54245 + * + * @covers WP_Upgrader::maintenance_mode */ - public function test_download_package_pre_download_arguments() { - $callback = function ( $reply, $package, $upgrader, $hook_extra ) { - $this->assertFalse( $reply ); - $this->assertSame( 'package', $package ); - $this->assertSame( $this->instance, $upgrader ); - $this->assertSame( array( 'hook_extra' ), $hook_extra ); + public function test_should_disable_maintenance_mode() { + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'abspath' ) + ->willReturn( '/' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'exists' ) + ->with( '/.maintenance' ) + ->willReturn( true ); + + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'maintenance_end' ); - return ! $reply; - }; + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'delete' ) + ->with( '/.maintenance' ); - add_filter( 'upgrader_pre_download', $callback, 10, 4 ); + $this->instance->maintenance_mode(); + } + /** + * Tests that `WP_Upgrader::maintenance_mode()` does nothing if + * the `.maintenance` file does not exist. + * + * @ticket 54245 + * + * @covers WP_Upgrader::maintenance_mode + */ + public function test_should_not_disable_maintenance_mode_if_no_maintenance_file_exists() { $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); - $result = $this->instance->download_package( 'package', false, array( 'hook_extra' ) ); + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'abspath' ) + ->willReturn( '/' ); - $this->assertTrue( $result ); + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'exists' ) + ->with( '/.maintenance' ) + ->willReturn( false ); - remove_filter( 'upgrader_pre_download', $callback ); + $this->instance->maintenance_mode(); } /** - * Download package for an existing file should return the file. + * Tests that `WP_Upgrader::maintenance_mode()` creates + * a `.maintenance` file with a boolean `$enable` argument. + * + * @ticket 54245 + * + * @covers WP_Upgrader::maintenance_mode */ - public function test_download_package_package_constraints() { - $result = $this->instance->download_package( __FILE__ ); + public function test_should_create_maintenance_file_with_boolean() { + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'abspath' ) + ->willReturn( '/' ); - $this->assertSame( __FILE__, $result ); + $this->upgrader_skin_mock + ->expects( $this->once() ) + ->method( 'feedback' ) + ->with( 'maintenance_start' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'delete' ) + ->with( '/.maintenance' ); + + $this->wp_filesystem_mock + ->expects( $this->once() ) + ->method( 'put_contents' ) + ->with( + '/.maintenance', + $this->stringContains( 'instance->maintenance_mode( true ); } /** - * Download package should trigger an error for an empty package. + * Tests that `WP_Upgrader::maintenance_mode()` creates + * a `.maintenance` file with a non-boolean `$enable` argument. + * + * @ticket 54245 + * + * @covers WP_Upgrader::maintenance_mode */ - public function test_download_package_empty_package() { - $this->instance->generic_strings(); + public function test_should_create_maintenance_file_with_non_boolean() { + $this->wp_filesystem_mock->expects( $this->never() )->method( 'abspath' ); + $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'delete' ); + $this->wp_filesystem_mock->expects( $this->never() )->method( 'put_contents' ); - $result = $this->instance->download_package( '' ); + $this->instance->maintenance_mode( new stdClass() ); + } + + /** + * Tests that `WP_Upgrader::release_lock()` removes the 'lock' option. + * + * @ticket 54245 + * + * @covers WP_Upgrader::release_lock + */ + public function test_should_remove_lock_option() { + update_option( 'lock.lock', 'content' ); + + WP_Upgrader::release_lock( 'lock' ); - $this->assertWPError( $result ); - $this->assertSame( 'no_package', $result->get_error_code() ); + $this->assertNotSame( 'content', get_option( 'lock.lock' ) ); } /** - * Download package should return a file with the package name in it. + * Tests that `WP_Upgrader::download_package()` returns early when + * the 'upgrader_pre_download' filter returns a non-false value. + * + * @ticket 54245 + * + * @covers WP_Upgrader::download_package */ - public function test_download_package() { - $callback = function () { - return array( 'response' => array( 'code' => 200 ) ); - }; + public function test_download_package_should_exit_early_when_the_upgrader_pre_download_filter_returns_non_false() { + $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); - add_filter( 'pre_http_request', $callback ); + add_filter( + 'upgrader_pre_download', + static function ( $reply ) { + return ! $reply; + }, + ); - $result = $this->instance->download_package( 'wordpress-seo' ); + $result = $this->instance->download_package( 'package' ); - $this->assertStringContainsString( '/wordpress-seo-', $result ); + $this->assertTrue( $result ); + } - remove_filter( 'pre_http_request', $callback ); + /** + * Tests that `WP_Upgrader::download_package()` should apply + * 'upgrader_pre_download' filters with expected arguments. + * + * @ticket 54245 + * + * @covers WP_Upgrader::download_package + */ + public function test_download_package_should_apply_upgrader_pre_download_filter_with_arguments() { + $this->upgrader_skin_mock->expects( $this->never() )->method( 'feedback' ); + + add_filter( + 'upgrader_pre_download', + function ( $reply, $package, $upgrader, $hook_extra ) { + $this->assertFalse( $reply, '"$reply" was not false' ); + + $this->assertSame( + 'package', + $package, + 'The package file name was not "package"' + ); + + $this->assertSame( + $this->instance, + $upgrader, + 'The wrong WP_Upgrader instance was passed' + ); + + $this->assertSameSets( + array( 'hook_extra' ), + $hook_extra, + 'The "$hook_extra" array was not the expected array' + ); + + return ! $reply; + }, + 10, + 4 + ); + + $result = $this->instance->download_package( 'package', false, array( 'hook_extra' ) ); + + $this->assertTrue( + $result, + 'WP_Upgrader::download_package() did not return true' + ); } /** - * Downloads the package URL error should be returned as WP Error. + * Tests that `WP_Upgrader::download_package()` returns an existing file. + * + * @ticket 54245 + * + * @covers WP_Upgrader::download_package */ - public function test_download_package_wp_error() { - $this->instance->generic_strings(); + public function test_download_package_should_return_an_existing_file() { + $result = $this->instance->download_package( __FILE__ ); - $callback = function () { - return array( - 'response' => array( - 'code' => 400, - 'message' => 'error', - ), - ); - }; + $this->assertSame( __FILE__, $result ); + } - add_filter( 'pre_http_request', $callback ); + /** + * Tests that `WP_Upgrader::download_package()` returns a WP_Error object + * for an empty package. + * + * @ticket 54245 + * + * @covers WP_Upgrader::download_package + */ + public function test_download_package_should_trigger_an_error_for_an_empty_package() { + $this->instance->generic_strings(); - $result = $this->instance->download_package( 'wordpress-seo' ); + $result = $this->instance->download_package( '' ); - $this->assertWPError( $result ); - $this->assertSame( 'download_failed', $result->get_error_code() ); + $this->assertWPError( + $result, + 'WP_Upgrader::download_package() did not return a WP_Error object' + ); - remove_filter( 'pre_http_request', $callback ); + $this->assertSame( + 'no_package', + $result->get_error_code(), + 'The WP_Error code was not "no_package"' + ); } /** - * Restore temp backup should return early if not all arguments are provided. + * Tests that `WP_Upgrader::download_package()` returns a file with the + * package name in it. * - * @param array $arguments The arguments to test against. + * @ticket 54245 * - * @dataProvider restore_temp_backup_invalid_arguments_dataprovider + * @covers WP_Upgrader::download_package */ - public function test_restore_temp_backup_invalid_arguments( $arguments ) { - $this->assertFalse( $this->instance->restore_temp_backup( $arguments ) ); - $this->wp_filesystem_mock->expects( $this->never() )->method( 'wp_content_dir' ); + public function test_download_package_should_return_a_file_with_the_package_name() { + add_filter( + 'pre_http_request', + static function() { + return array( 'response' => array( 'code' => 200 ) ); + } + ); + + $result = $this->instance->download_package( 'wordpress-seo' ); + + $this->assertStringContainsString( '/wordpress-seo-', $result ); } /** - * Provides arguments for the temp backup invalid arguments. + * Tests that `WP_Upgrader::download_package()` returns a package URL error + * as a `WP_Error` object. + * + * @ticket 54245 * - * @return string[][][] + * @covers WP_Upgrader::download_package */ - public function restore_temp_backup_invalid_arguments_dataprovider() { - return array( - 'missing slug' => array( - 'arguments' => array( - 'slug' => '', - 'src' => 'src', - 'dir' => 'dir', - ), - ), - 'missing src' => array( - 'arguments' => array( - 'slug' => 'slug', - 'src' => '', - 'dir' => 'dir', - ), - ), - 'missing dir' => array( - 'arguments' => array( - 'slug' => 'dir', - 'src' => 'src', - 'dir' => '', - ), - ), - 'missing slug and dir' => array( - 'arguments' => array( - 'slug' => '', - 'src' => 'src', - 'dir' => '', - ), - ), - 'missing slug and src' => array( - 'arguments' => array( - 'slug' => '', - 'src' => '', - 'dir' => 'dir', - ), - ), - 'missing src and dir' => array( - 'arguments' => array( - 'slug' => 'slug', - 'src' => '', - 'dir' => '', - ), - ), - 'missing all' => array( - 'arguments' => array( - 'slug' => '', - 'src' => '', - 'dir' => '', - ), - ), - 'not string - all' => array( - 'arguments' => array( - 'slug' => true, - 'src' => true, - 'dir' => true, - ), - ), - 'not string - only src' => array( - 'arguments' => array( - 'slug' => 'true', - 'src' => true, - 'dir' => 'true', - ), - ), - 'not string - only dir' => array( - 'arguments' => array( - 'slug' => 'true', - 'src' => 'true', - 'dir' => true, - ), - ), - 'not string - only slug' => array( - 'arguments' => array( - 'slug' => true, - 'src' => 'true', - 'dir' => 'true', - ), - ), - 'not string - src and dir' => array( - 'arguments' => array( - 'slug' => 'true', - 'src' => true, - 'dir' => true, - ), - ), - 'not string - slug and dir' => array( - 'arguments' => array( - 'slug' => true, - 'src' => 'true', - 'dir' => true, - ), - ), - 'not string - slug and src' => array( - 'arguments' => array( - 'slug' => true, - 'src' => true, - 'dir' => 'true', - ), - ), + public function test_download_package_should_return_a_wp_error_object() { + $this->instance->generic_strings(); + + add_filter( + 'pre_http_request', + static function () { + return array( + 'response' => array( + 'code' => 400, + 'message' => 'error', + ), + ); + } + ); + + $result = $this->instance->download_package( 'wordpress-seo' ); + + $this->assertWPError( + $result, + 'WP_Upgrader::download_package() did not return a WP_Error object' + ); + + $this->assertSame( + 'download_failed', + $result->get_error_code(), + 'The WP_Error code was not "download_failed"' ); } }