You are here

public function DirectoryTest::testMultiplePrepareDirectory in Drupal 9

Tests asynchronous directory creation.

Image style generation can result in many calls to create similar directory paths. This test forks the process to create the same situation.

File

core/tests/Drupal/KernelTests/Core/File/DirectoryTest.php, line 206

Class

DirectoryTest
Tests operations dealing with directories.

Namespace

Drupal\KernelTests\Core\File

Code

public function testMultiplePrepareDirectory() {
  if (!function_exists('pcntl_fork')) {
    $this
      ->markTestSkipped('Requires the pcntl_fork() function');
  }
  $directories = [];
  for ($i = 1; $i <= 10; $i++) {
    $directories[] = 'public://a/b/c/d/e/f/g/h/' . $i;
  }
  $file_system = $this->container
    ->get('file_system');
  $time_to_start = microtime(TRUE) + 0.1;

  // This loop creates a new fork to create each directory.
  foreach ($directories as $directory) {
    $pid = pcntl_fork();
    if ($pid == -1) {
      $this
        ->fail("Error forking");
    }
    elseif ($pid == 0) {

      // Sleep so that all the forks start preparing the directory at the same
      // time.
      usleep(($time_to_start - microtime(TRUE)) * 1000000);
      $file_system
        ->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY);
      exit;
    }
  }

  // This while loop holds the parent process until all the child threads
  // are complete - at which point the script continues to execute.
  while (pcntl_waitpid(0, $status) != -1) {
  }
  foreach ($directories as $directory) {
    $this
      ->assertDirectoryExists($directory);
  }

  // Remove the database connection because it will have been destroyed when
  // the forks exited. This allows
  // \Drupal\KernelTests\KernelTestBase::tearDown() to reopen it.
  Database::removeConnection('default');
}