diff --git a/core/includes/file.inc b/core/includes/file.inc index 12737d3b40f49108ee7bf3a7d2fbe9eb220be9bf..f08070ec47b68917e5064c4fe9033b0eb606b239 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -199,12 +199,14 @@ * @see hook_stream_wrappers_alter() */ function file_get_stream_wrappers($filter = STREAM_WRAPPERS_ALL) { - $wrappers_storage = &drupal_static(__FUNCTION__); + $wrappers_storage = &drupal_static(__FUNCTION__, array()); - if (!isset($wrappers_storage)) { + if (empty($wrappers_storage)) { + // Initialize $wrappers_storage, so that we are not calling this method + // repeatedly if no stream wrappers exist. + $wrappers_storage[STREAM_WRAPPERS_ALL] = array(); $wrappers = array(); - $container = \Drupal::getContainer(); - if (is_object($container) && $container->has('module_handler')) { + if (\Drupal::hasService('module_handler')) { $wrappers = \Drupal::moduleHandler()->invokeAll('stream_wrappers'); foreach ($wrappers as $scheme => $info) { // Add defaults. diff --git a/core/modules/file/lib/Drupal/file/Tests/CopyTest.php b/core/modules/file/lib/Drupal/file/Tests/CopyTest.php index 6ee9c9933a931b4eb0bddbe06e973ff1b1eaf48e..9fb0ed12a2c00c5a5a8a3985501de231c38c58da 100644 --- a/core/modules/file/lib/Drupal/file/Tests/CopyTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/CopyTest.php @@ -10,7 +10,7 @@ /** * Copy related tests. */ -class CopyTest extends FileManagedTestBase { +class CopyTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File copying', diff --git a/core/modules/file/lib/Drupal/file/Tests/DeleteTest.php b/core/modules/file/lib/Drupal/file/Tests/DeleteTest.php index 1f9e6c45a95d3f2a167481af8dd226bcb25e24b5..f345811e47325256f6dc3bb5f2bc650eebc11459 100644 --- a/core/modules/file/lib/Drupal/file/Tests/DeleteTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/DeleteTest.php @@ -10,7 +10,7 @@ /** * Deletion related tests. */ -class DeleteTest extends FileManagedTestBase { +class DeleteTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File delete', diff --git a/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php new file mode 100644 index 0000000000000000000000000000000000000000..324b1b5bf635bc7e5d0440241a41753b36c86583 --- /dev/null +++ b/core/modules/file/lib/Drupal/file/Tests/FileManagedUnitTestBase.php @@ -0,0 +1,216 @@ +<?php + +/** + * @file + * Contains Drupal\file\Tests\FileManagedUnitTestBase. + */ + +namespace Drupal\file\Tests; + +use Drupal\file\FileInterface; +use Drupal\simpletest\DrupalUnitTestBase; + +/** + * Base class for file unit tests that use the file_test module to test uploads and + * hooks. + */ +abstract class FileManagedUnitTestBase extends DrupalUnitTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('file_test', 'file', 'system', 'field', 'user'); + + function setUp() { + parent::setUp(); + // Clear out any hook calls. + file_test_reset(); + + $this->installConfig(array('system')); + $this->installSchema('file', array('file_managed', 'file_usage')); + $this->installSchema('user', array('users', 'users_roles')); + + // Make sure that a user with uid 1 exists, self::createFile() relies on + // it. + $user = entity_create('user', array('uid' => 1, 'name' => $this->randomName())); + $user->enforceIsNew(); + $user->save(); + $this->container->set('current_user', $user); + } + + /** + * Assert that all of the specified hook_file_* hooks were called once, other + * values result in failure. + * + * @param $expected + * Array with string containing with the hook name, e.g. 'load', 'save', + * 'insert', etc. + */ + function assertFileHooksCalled($expected) { + \Drupal::state()->resetCache(); + + // Determine which hooks were called. + $actual = array_keys(array_filter(file_test_get_all_calls())); + + // Determine if there were any expected that were not called. + $uncalled = array_diff($expected, $actual); + if (count($uncalled)) { + $this->assertTrue(FALSE, format_string('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)))); + } + else { + $this->assertTrue(TRUE, format_string('All the expected hooks were called: %expected', array('%expected' => empty($expected) ? '(none)' : implode(', ', $expected)))); + } + + // Determine if there were any unexpected calls. + $unexpected = array_diff($actual, $expected); + if (count($unexpected)) { + $this->assertTrue(FALSE, format_string('Unexpected hooks were called: %unexpected.', array('%unexpected' => empty($unexpected) ? '(none)' : implode(', ', $unexpected)))); + } + else { + $this->assertTrue(TRUE, 'No unexpected hooks were called.'); + } + } + + /** + * Assert that a hook_file_* hook was called a certain number of times. + * + * @param $hook + * String with the hook name, e.g. 'load', 'save', 'insert', etc. + * @param $expected_count + * Optional integer count. + * @param $message + * Optional translated string message. + */ + function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) { + $actual_count = count(file_test_get_calls($hook)); + + if (!isset($message)) { + if ($actual_count == $expected_count) { + $message = format_string('hook_file_@name was called correctly.', array('@name' => $hook)); + } + elseif ($expected_count == 0) { + $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count)); + } + else { + $message = format_string('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count)); + } + } + $this->assertEqual($actual_count, $expected_count, $message); + } + + /** + * Asserts that two files have the same values (except timestamp). + * + * @param \Drupal\file\FileInterface $before + * File object to compare. + * @param \Drupal\file\FileInterface $after + * File object to compare. + */ + function assertFileUnchanged(FileInterface $before, FileInterface $after) { + $this->assertEqual($before->id(), $after->id(), t('File id is the same: %file1 == %file2.', array('%file1' => $before->id(), '%file2' => $after->id())), 'File unchanged'); + $this->assertEqual($before->getOwner()->id(), $after->getOwner()->id(), t('File owner is the same: %file1 == %file2.', array('%file1' => $before->getOwner()->id(), '%file2' => $after->getOwner()->id())), 'File unchanged'); + $this->assertEqual($before->getFilename(), $after->getFilename(), t('File name is the same: %file1 == %file2.', array('%file1' => $before->getFilename(), '%file2' => $after->getFilename())), 'File unchanged'); + $this->assertEqual($before->getFileUri(), $after->getFileUri(), t('File path is the same: %file1 == %file2.', array('%file1' => $before->getFileUri(), '%file2' => $after->getFileUri())), 'File unchanged'); + $this->assertEqual($before->getMimeType(), $after->getMimeType(), t('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->getMimeType(), '%file2' => $after->getMimeType())), 'File unchanged'); + $this->assertEqual($before->getSize(), $after->getSize(), t('File size is the same: %file1 == %file2.', array('%file1' => $before->getSize(), '%file2' => $after->getSize())), 'File unchanged'); + $this->assertEqual($before->isPermanent(), $after->isPermanent(), t('File status is the same: %file1 == %file2.', array('%file1' => $before->isPermanent(), '%file2' => $after->isPermanent())), 'File unchanged'); + } + + /** + * Asserts that two files are not the same by comparing the fid and filepath. + * + * @param \Drupal\file\FileInterface $file1 + * File object to compare. + * @param \Drupal\file\FileInterface $file2 + * File object to compare. + */ + function assertDifferentFile(FileInterface $file1, FileInterface $file2) { + $this->assertNotEqual($file1->id(), $file2->id(), t('Files have different ids: %file1 != %file2.', array('%file1' => $file1->id(), '%file2' => $file2->id())), 'Different file'); + $this->assertNotEqual($file1->getFileUri(), $file2->getFileUri(), t('Files have different paths: %file1 != %file2.', array('%file1' => $file1->getFileUri(), '%file2' => $file2->getFileUri())), 'Different file'); + } + + /** + * Asserts that two files are the same by comparing the fid and filepath. + * + * @param \Drupal\file\FileInterface $file1 + * File object to compare. + * @param \Drupal\file\FileInterface $file2 + * File object to compare. + */ + function assertSameFile(FileInterface $file1, FileInterface $file2) { + $this->assertEqual($file1->id(), $file2->id(), t('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->id(), '%file2-fid' => $file2->id())), 'Same file'); + $this->assertEqual($file1->getFileUri(), $file2->getFileUri(), t('Files have the same path: %file1 == %file2.', array('%file1' => $file1->getFileUri(), '%file2' => $file2->getFileUri())), 'Same file'); + } + + /** + * Create a file and save it to the files table and assert that it occurs + * correctly. + * + * @param $filepath + * Optional string specifying the file path. If none is provided then a + * randomly named file will be created in the site's files directory. + * @param $contents + * Optional contents to save into the file. If a NULL value is provided an + * arbitrary string will be used. + * @param $scheme + * Optional string indicating the stream scheme to use. Drupal core includes + * public, private, and temporary. The public wrapper is the default. + * @return \Drupal\file\FileInterface + * File entity. + */ + function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) { + $file = new \stdClass(); + $file->uri = $this->createUri($filepath, $contents, $scheme); + $file->filename = drupal_basename($file->uri); + $file->filemime = 'text/plain'; + $file->uid = 1; + $file->created = REQUEST_TIME; + $file->changed = REQUEST_TIME; + $file->filesize = filesize($file->uri); + $file->status = 0; + // Write the record directly rather than using the API so we don't invoke + // the hooks. + $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, 'The file was added to the database.', 'Create test file'); + + return entity_create('file', (array) $file); + } + + /** + * Creates a file and returns its URI. + * + * @param string $filepath + * Optional string specifying the file path. If none is provided then a + * randomly named file will be created in the site's files directory. + * @param string $contents + * Optional contents to save into the file. If a NULL value is provided an + * arbitrary string will be used. + * @param string $scheme + * Optional string indicating the stream scheme to use. Drupal core includes + * public, private, and temporary. The public wrapper is the default. + * + * @return string + * File URI. + */ + function createUri($filepath = NULL, $contents = NULL, $scheme = NULL) { + if (!isset($filepath)) { + // Prefix with non-latin characters to ensure that all file-related + // tests work with international filenames. + $filepath = 'Файл Ð´Ð»Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ' . $this->randomName(); + } + if (!isset($scheme)) { + $scheme = file_default_scheme(); + } + $filepath = $scheme . '://' . $filepath; + + if (!isset($contents)) { + $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data."; + } + + file_put_contents($filepath, $contents); + $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); + return $filepath; + } + +} diff --git a/core/modules/file/lib/Drupal/file/Tests/LoadTest.php b/core/modules/file/lib/Drupal/file/Tests/LoadTest.php index 6a85e83eb1d8e11fe31d3592bb6b700f0e2e871a..bf6a1e935149b24497b1bb0efdb2df98cf60173a 100644 --- a/core/modules/file/lib/Drupal/file/Tests/LoadTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/LoadTest.php @@ -10,7 +10,7 @@ /** * Tests the file_load() function. */ -class LoadTest extends FileManagedTestBase { +class LoadTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File loading', diff --git a/core/modules/file/lib/Drupal/file/Tests/MoveTest.php b/core/modules/file/lib/Drupal/file/Tests/MoveTest.php index 8fb7b0771ae69b900c5e8aaf7c564d0b9f9bf27b..b063afa851092ce2746d70617c9b22ec3b60c2c5 100644 --- a/core/modules/file/lib/Drupal/file/Tests/MoveTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/MoveTest.php @@ -10,7 +10,7 @@ /** * Move related tests */ -class MoveTest extends FileManagedTestBase { +class MoveTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File moving', diff --git a/core/modules/file/lib/Drupal/file/Tests/SaveDataTest.php b/core/modules/file/lib/Drupal/file/Tests/SaveDataTest.php index 9b1bf2a20d301c277fcd6569a6d8444f9a724067..081b6b38c43f4712f2b0ea7239e7246c3545e926 100644 --- a/core/modules/file/lib/Drupal/file/Tests/SaveDataTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/SaveDataTest.php @@ -10,7 +10,7 @@ /** * Tests the file_save_data() function. */ -class SaveDataTest extends FileManagedTestBase { +class SaveDataTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File save data', diff --git a/core/modules/file/lib/Drupal/file/Tests/SaveTest.php b/core/modules/file/lib/Drupal/file/Tests/SaveTest.php index c2f0c38ba6776065a11287c302c90ec228ac3b0a..50d684754bb3bf01275792dc93c121dfc9d4cdb9 100644 --- a/core/modules/file/lib/Drupal/file/Tests/SaveTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/SaveTest.php @@ -12,7 +12,7 @@ /** * Tests saving files. */ -class SaveTest extends FileManagedTestBase { +class SaveTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File saving', diff --git a/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php b/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php index b98134bb15eb8c6ec3843b885fc2edb40f73c37c..49e57f8b721fd1db08c1b1c0e8e5dbeaa4def841 100644 --- a/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/SpaceUsedTest.php @@ -10,7 +10,7 @@ /** * This will run tests against the $file_managed->spaceUsed() function. */ -class SpaceUsedTest extends FileManagedTestBase { +class SpaceUsedTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File space used tests', diff --git a/core/modules/file/lib/Drupal/file/Tests/UsageTest.php b/core/modules/file/lib/Drupal/file/Tests/UsageTest.php index 713cc331e968a295516c95e96c26e414f5221f3c..3cff7da4108503882da3294b362cee0817039b87 100644 --- a/core/modules/file/lib/Drupal/file/Tests/UsageTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/UsageTest.php @@ -10,7 +10,7 @@ /** * Tests file usage functions. */ -class UsageTest extends FileManagedTestBase { +class UsageTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File usage', @@ -161,7 +161,7 @@ function testTempFileCleanup() { $this->assertTrue(file_exists($perm_new->getFileUri()), 'New permanent file was created correctly.'); // Run cron and then ensure that only the old, temp file was deleted. - $this->cronRun(); + $this->container->get('cron')->run(); $this->assertFalse(file_exists($temp_old->getFileUri()), 'Old temp file was correctly removed.'); $this->assertTrue(file_exists($temp_new->getFileUri()), 'New temp file was correctly ignored.'); $this->assertTrue(file_exists($perm_old->getFileUri()), 'Old permanent file was correctly ignored.'); diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidateTest.php index 5656fdcbe7cd3067b9f4ee3384978c9e6c3b7393..59fa0146ac35fd7a52ffec5bc2b831523acaa055 100644 --- a/core/modules/file/lib/Drupal/file/Tests/ValidateTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/ValidateTest.php @@ -10,7 +10,7 @@ /** * Tests the file_validate() function. */ -class ValidateTest extends FileManagedTestBase { +class ValidateTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File validate', diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php index 76abd8adcd1f95dddbae5a2e4d1b4e2b019060d6..8c45a76dea1b1091db5ae0bd59ec5f5fd766a499 100644 --- a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php @@ -10,7 +10,7 @@ /** * This will run tests against the file validation functions (file_validate_*). */ -class ValidatorTest extends FileManagedTestBase { +class ValidatorTest extends FileManagedUnitTestBase { public static function getInfo() { return array( 'name' => 'File validator tests', @@ -129,12 +129,11 @@ function testFileValidateNameLength() { * Test file_validate_size(). */ function testFileValidateSize() { - $user = $this->container->get('current_user'); - $original_user = $user; - drupal_save_session(FALSE); - // Run these tests as a regular user. - $user = $this->drupalCreateUser(); + $user = entity_create('user', array('uid' => 2, 'name' => $this->randomName())); + $user->enforceIsNew(); + $user->save(); + $this->container->set('current_user', $user); // Create a file with a size of 1000 bytes, and quotas of only 1 byte. $file = entity_create('file', array('filesize' => 1000)); @@ -146,8 +145,5 @@ function testFileValidateSize() { $this->assertEqual(count($errors), 1, 'Error for the user being over their limit.', 'File'); $errors = file_validate_size($file, 1, 1); $this->assertEqual(count($errors), 2, 'Errors for both the file and their limit.', 'File'); - - $user = $original_user; - drupal_save_session(TRUE); } } diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index cab4b9ea1201daa08c54493d447d74b26a437a81..d10ce9f1a432e23edb179304abdac73e6db68421 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -179,6 +179,7 @@ protected function setUp() { // StreamWrapper APIs. // @todo Move StreamWrapper management into DrupalKernel. // @see https://drupal.org/node/2028109 + $this->streamWrappers = array(); // The public stream wrapper only depends on the file_public_path setting, // which is provided by UnitTestBase::setUp(). $this->registerStreamWrapper('public', 'Drupal\Core\StreamWrapper\PublicStream'); @@ -197,8 +198,8 @@ protected function tearDown() { // of PHP core, which has to be maintained manually. // @todo Move StreamWrapper management into DrupalKernel. // @see https://drupal.org/node/2028109 - foreach ($this->streamWrappers as $scheme) { - $this->unregisterStreamWrapper($scheme); + foreach ($this->streamWrappers as $scheme => $type) { + $this->unregisterStreamWrapper($scheme, $type); } parent::tearDown(); } @@ -417,7 +418,7 @@ protected function registerStreamWrapper($scheme, $class, $type = STREAM_WRAPPER if (isset($this->streamWrappers[$scheme])) { $this->unregisterStreamWrapper($scheme); } - $this->streamWrappers[$scheme] = $scheme; + $this->streamWrappers[$scheme] = $type; if (($type & STREAM_WRAPPERS_LOCAL) == STREAM_WRAPPERS_LOCAL) { stream_wrapper_register($scheme, $class); } @@ -426,12 +427,14 @@ protected function registerStreamWrapper($scheme, $class, $type = STREAM_WRAPPER } // @todo Revamp Drupal's stream wrapper API for D8. // @see https://drupal.org/node/2028109 - $wrappers = &drupal_static('file_get_stream_wrappers'); - $wrappers[$scheme] = array( + $wrappers = &drupal_static('file_get_stream_wrappers', array()); + $wrappers[STREAM_WRAPPERS_ALL][$scheme] = array( 'type' => $type, 'class' => $class, ); - $wrappers[STREAM_WRAPPERS_ALL] = $wrappers; + if (($type & STREAM_WRAPPERS_WRITE_VISIBLE) == STREAM_WRAPPERS_WRITE_VISIBLE) { + $wrappers[STREAM_WRAPPERS_WRITE_VISIBLE][$scheme] = $wrappers[STREAM_WRAPPERS_ALL][$scheme]; + } } /** @@ -442,15 +445,20 @@ protected function registerStreamWrapper($scheme, $class, $type = STREAM_WRAPPER * * @param string $scheme * The scheme to unregister. + * @param int $type + * The Drupal Stream Wrapper API type of the scheme to unregister. */ - protected function unregisterStreamWrapper($scheme) { + protected function unregisterStreamWrapper($scheme, $type) { stream_wrapper_unregister($scheme); unset($this->streamWrappers[$scheme]); // @todo Revamp Drupal's stream wrapper API for D8. // @see https://drupal.org/node/2028109 - $wrappers = &drupal_static('file_get_stream_wrappers'); - unset($wrappers[$scheme]); - unset($wrappers[STREAM_WRAPPERS_ALL][$scheme]); + $wrappers = &drupal_static('file_get_stream_wrappers', array()); + foreach ($wrappers as $filter => $schemes) { + if (is_int($filter) && (($filter & $type) == $filter)) { + unset($wrappers[$filter][$scheme]); + } + } } }