Pull merge.
[yaffs-website] / web / core / modules / migrate / tests / src / Kernel / process / FileCopyTest.php
1 <?php
2
3 namespace Drupal\Tests\migrate\Kernel\process;
4
5 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
6 use Drupal\KernelTests\Core\File\FileTestBase;
7 use Drupal\migrate\MigrateException;
8 use Drupal\migrate\Plugin\migrate\process\FileCopy;
9 use Drupal\migrate\MigrateExecutableInterface;
10 use Drupal\migrate\Plugin\MigrateProcessInterface;
11 use Drupal\migrate\Row;
12 use GuzzleHttp\Client;
13
14 /**
15  * Tests the file_copy process plugin.
16  *
17  * @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\FileCopy
18  *
19  * @group migrate
20  */
21 class FileCopyTest extends FileTestBase {
22
23   /**
24    * {@inheritdoc}
25    */
26   public static $modules = ['migrate', 'system'];
27
28   /**
29    * The file system service.
30    *
31    * @var \Drupal\Core\File\FileSystemInterface
32    */
33   protected $fileSystem;
34
35   /**
36    * {@inheritdoc}
37    */
38   protected function setUp() {
39     parent::setUp();
40     $this->fileSystem = $this->container->get('file_system');
41     $this->container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
42   }
43
44   /**
45    * Test successful imports/copies.
46    */
47   public function testSuccessfulCopies() {
48     $file = $this->createUri(NULL, NULL, 'temporary');
49     $file_absolute = $this->fileSystem->realpath($file);
50     $data_sets = [
51       // Test a local to local copy.
52       [
53         $this->root . '/core/modules/simpletest/files/image-test.jpg',
54         'public://file1.jpg',
55       ],
56       // Test a temporary file using an absolute path.
57       [
58         $file_absolute,
59         'temporary://test.jpg',
60       ],
61       // Test a temporary file using a relative path.
62       [
63         $file_absolute,
64         'temporary://core/modules/simpletest/files/test.jpg',
65       ],
66     ];
67     foreach ($data_sets as $data) {
68       list($source_path, $destination_path) = $data;
69       $actual_destination = $this->doTransform($source_path, $destination_path);
70       $message = sprintf('File %s exists', $destination_path);
71       $this->assertFileExists($destination_path, $message);
72       // Make sure we didn't accidentally do a move.
73       $this->assertFileExists($source_path, $message);
74       $this->assertSame($actual_destination, $destination_path, 'The import returned the copied filename.');
75     }
76   }
77
78   /**
79    * Test successful file reuse.
80    *
81    * @dataProvider providerSuccessfulReuse
82    *
83    * @param string $source_path
84    *   Source path to copy from.
85    * @param string $destination_path
86    *   The destination path to copy to.
87    */
88   public function testSuccessfulReuse($source_path, $destination_path) {
89     $file_reuse = $this->doTransform($source_path, $destination_path);
90     clearstatcache(TRUE, $destination_path);
91
92     $timestamp = (new \SplFileInfo($file_reuse))->getMTime();
93     $this->assertInternalType('int', $timestamp);
94
95     // We need to make sure the modified timestamp on the file is sooner than
96     // the attempted migration.
97     sleep(1);
98     $configuration = ['file_exists' => 'use existing'];
99     $this->doTransform($source_path, $destination_path, $configuration);
100     clearstatcache(TRUE, $destination_path);
101     $modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
102     $this->assertEquals($timestamp, $modified_timestamp);
103
104     $this->doTransform($source_path, $destination_path);
105     clearstatcache(TRUE, $destination_path);
106     $modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
107     $this->assertGreaterThan($timestamp, $modified_timestamp);
108   }
109
110   /**
111    * Provides the source and destination path files.
112    */
113   public function providerSuccessfulReuse() {
114     return [
115       [
116         'local_source_path' => static::getDrupalRoot() . '/core/modules/simpletest/files/image-test.jpg',
117         'local_destination_path' => 'public://file1.jpg',
118       ],
119       [
120         'remote_source_path' => 'https://www.drupal.org/favicon.ico',
121         'remote_destination_path' => 'public://file2.jpg',
122       ],
123     ];
124   }
125
126   /**
127    * Test successful moves.
128    */
129   public function testSuccessfulMoves() {
130     $file_1 = $this->createUri(NULL, NULL, 'temporary');
131     $file_1_absolute = $this->fileSystem->realpath($file_1);
132     $file_2 = $this->createUri(NULL, NULL, 'temporary');
133     $file_2_absolute = $this->fileSystem->realpath($file_2);
134     $local_file = $this->createUri(NULL, NULL, 'public');
135     $data_sets = [
136       // Test a local to local copy.
137       [
138         $local_file,
139         'public://file1.jpg',
140       ],
141       // Test a temporary file using an absolute path.
142       [
143         $file_1_absolute,
144         'temporary://test.jpg',
145       ],
146       // Test a temporary file using a relative path.
147       [
148         $file_2_absolute,
149         'temporary://core/modules/simpletest/files/test.jpg',
150       ],
151     ];
152     foreach ($data_sets as $data) {
153       list($source_path, $destination_path) = $data;
154       $actual_destination = $this->doTransform($source_path, $destination_path, ['move' => TRUE]);
155       $message = sprintf('File %s exists', $destination_path);
156       $this->assertFileExists($destination_path, $message);
157       $message = sprintf('File %s does not exist', $source_path);
158       $this->assertFileNotExists($source_path, $message);
159       $this->assertSame($actual_destination, $destination_path, 'The importer returned the moved filename.');
160     }
161   }
162
163   /**
164    * Test that non-existent files throw an exception.
165    */
166   public function testNonExistentSourceFile() {
167     $source = '/non/existent/file';
168     $this->setExpectedException(MigrateException::class, "File '/non/existent/file' does not exist");
169     $this->doTransform($source, 'public://wontmatter.jpg');
170   }
171
172   /**
173    * Tests that non-writable destination throw an exception.
174    *
175    * @covers ::transform
176    */
177   public function testNonWritableDestination() {
178     $source = $this->createUri('file.txt', NULL, 'temporary');
179
180     // Create the parent location.
181     $this->createDirectory('public://dir');
182
183     // Copy the file under public://dir/subdir1/.
184     $this->doTransform($source, 'public://dir/subdir1/file.txt');
185
186     // Check that 'subdir1' was created and the file was successfully migrated.
187     $this->assertFileExists('public://dir/subdir1/file.txt');
188
189     // Remove all permissions from public://dir to trigger a failure when
190     // trying to create a subdirectory 'subdir2' inside public://dir.
191     $this->fileSystem->chmod('public://dir', 0);
192
193     // Check that the proper exception is raised.
194     $this->setExpectedException(MigrateException::class, "Could not create or write to directory 'public://dir/subdir2'");
195     $this->doTransform($source, 'public://dir/subdir2/file.txt');
196   }
197
198   /**
199    * Test the 'rename' overwrite mode.
200    */
201   public function testRenameFile() {
202     $source = $this->createUri(NULL, NULL, 'temporary');
203     $destination = $this->createUri('foo.txt', NULL, 'public');
204     $expected_destination = 'public://foo_0.txt';
205     $actual_destination = $this->doTransform($source, $destination, ['file_exists' => 'rename']);
206     $this->assertFileExists($expected_destination, 'File was renamed on import');
207     $this->assertSame($actual_destination, $expected_destination, 'The importer returned the renamed filename.');
208   }
209
210   /**
211    * Tests that remote URIs are delegated to the download plugin.
212    */
213   public function testDownloadRemoteUri() {
214     $download_plugin = $this->getMock(MigrateProcessInterface::class);
215     $download_plugin->expects($this->once())->method('transform');
216
217     $plugin = new FileCopy(
218       [],
219       $this->randomMachineName(),
220       [],
221       $this->container->get('stream_wrapper_manager'),
222       $this->container->get('file_system'),
223       $download_plugin
224     );
225
226     $plugin->transform(
227       ['http://drupal.org/favicon.ico', '/destination/path'],
228       $this->getMock(MigrateExecutableInterface::class),
229       new Row([], []),
230       $this->randomMachineName()
231     );
232   }
233
234   /**
235    * Do an import using the destination.
236    *
237    * @param string $source_path
238    *   Source path to copy from.
239    * @param string $destination_path
240    *   The destination path to copy to.
241    * @param array $configuration
242    *   Process plugin configuration settings.
243    *
244    * @return string
245    *   The URI of the copied file.
246    */
247   protected function doTransform($source_path, $destination_path, $configuration = []) {
248     // Prepare a mock HTTP client.
249     $this->container->set('http_client', $this->createMock(Client::class));
250
251     $plugin = FileCopy::create($this->container, $configuration, 'file_copy', []);
252     $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
253     $row = new Row([], []);
254
255     return $plugin->transform([$source_path, $destination_path], $executable, $row, 'foobaz');
256   }
257
258 }