1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.transport.file;
20
21 import java.io.FileNotFoundException;
22 import java.net.URI;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.FileSystem;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28
29 import com.google.common.jimfs.Configuration;
30 import com.google.common.jimfs.Jimfs;
31 import org.eclipse.aether.DefaultRepositorySystemSession;
32 import org.eclipse.aether.internal.test.util.TestFileUtils;
33 import org.eclipse.aether.internal.test.util.TestUtils;
34 import org.eclipse.aether.repository.RemoteRepository;
35 import org.eclipse.aether.spi.connector.transport.GetTask;
36 import org.eclipse.aether.spi.connector.transport.PeekTask;
37 import org.eclipse.aether.spi.connector.transport.PutTask;
38 import org.eclipse.aether.spi.connector.transport.Transporter;
39 import org.eclipse.aether.spi.connector.transport.TransporterFactory;
40 import org.eclipse.aether.transfer.NoTransporterException;
41 import org.eclipse.aether.transfer.TransferCancelledException;
42 import org.junit.jupiter.api.AfterEach;
43 import org.junit.jupiter.api.Assertions;
44 import org.junit.jupiter.api.Test;
45 import org.junit.jupiter.params.ParameterizedTest;
46 import org.junit.jupiter.params.provider.EnumSource;
47
48 import static org.junit.jupiter.api.Assertions.*;
49
50
51
52 public class FileTransporterTest {
53
54 private DefaultRepositorySystemSession session;
55
56 private TransporterFactory factory;
57
58 private Transporter transporter;
59
60 private Path repoDir;
61
62 private Path tempDir;
63
64 private FileSystem fileSystem;
65
66 enum FS {
67 DEFAULT(""),
68 DEFAULT_SL("symlink+"),
69 DEFAULT_HL("hardlink+"),
70 JIMFS(""),
71 JIMFS_SL("symlink+"),
72 JIMFS_HL("hardlink+");
73
74 final String uriPrefix;
75
76 FS(String uriPrefix) {
77 this.uriPrefix = uriPrefix;
78 }
79 }
80
81 private RemoteRepository newRepo(String url) {
82 return new RemoteRepository.Builder("test", "default", url).build();
83 }
84
85 private void newTransporter(String url) throws Exception {
86 if (transporter != null) {
87 transporter.close();
88 transporter = null;
89 }
90 if (factory == null) {
91 factory = new FileTransporterFactory();
92 }
93 if (session == null) {
94 session = TestUtils.newSession();
95 }
96 transporter = factory.newInstance(session, newRepo(url));
97 }
98
99 void setUp(FS fs) {
100 try {
101 fileSystem = fs.name().startsWith("JIMFS") ? Jimfs.newFileSystem() : null;
102 repoDir = fileSystem == null ? TestFileUtils.createTempDir().toPath() : fileSystem.getPath("repo");
103 Files.createDirectories(repoDir);
104 tempDir = fileSystem == null ? TestFileUtils.createTempDir().toPath() : fileSystem.getPath("tmp");
105 Files.createDirectories(tempDir);
106 Files.write(repoDir.resolve("file.txt"), "test".getBytes(StandardCharsets.UTF_8));
107 Files.write(repoDir.resolve("empty.txt"), "".getBytes(StandardCharsets.UTF_8));
108 Files.write(repoDir.resolve("some space.txt"), "space".getBytes(StandardCharsets.UTF_8));
109 newTransporter(fs.uriPrefix + repoDir.toUri().toASCIIString());
110 } catch (Exception e) {
111 Assertions.fail(e);
112 }
113 }
114
115 @AfterEach
116 void tearDown() throws Exception {
117 if (transporter != null) {
118 transporter.close();
119 transporter = null;
120 }
121 if (fileSystem != null) {
122 fileSystem.close();
123 }
124 factory = null;
125 session = null;
126 }
127
128 @ParameterizedTest
129 @EnumSource(FS.class)
130 void testClassify(FS fs) {
131 setUp(fs);
132 assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException()));
133 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new ResourceNotFoundException("test")));
134 }
135
136 @ParameterizedTest
137 @EnumSource(FS.class)
138 void testPeek(FS fs) throws Exception {
139 setUp(fs);
140 transporter.peek(new PeekTask(URI.create("file.txt")));
141 }
142
143 @ParameterizedTest
144 @EnumSource(FS.class)
145 void testPeek_NotFound(FS fs) throws Exception {
146 setUp(fs);
147 try {
148 transporter.peek(new PeekTask(URI.create("missing.txt")));
149 fail("Expected error");
150 } catch (ResourceNotFoundException e) {
151 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
152 }
153 }
154
155 @ParameterizedTest
156 @EnumSource(FS.class)
157 void testPeek_Closed(FS fs) throws Exception {
158 setUp(fs);
159 transporter.close();
160 try {
161 transporter.peek(new PeekTask(URI.create("missing.txt")));
162 fail("Expected error");
163 } catch (IllegalStateException e) {
164 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
165 }
166 }
167
168 @ParameterizedTest
169 @EnumSource(FS.class)
170 void testGet_ToMemory(FS fs) throws Exception {
171 setUp(fs);
172 RecordingTransportListener listener = new RecordingTransportListener();
173 GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
174 transporter.get(task);
175 assertEquals("test", task.getDataString());
176 assertEquals(0L, listener.dataOffset);
177 assertEquals(4L, listener.dataLength);
178 assertEquals(1, listener.startedCount);
179 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
180 assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
181 }
182
183 @ParameterizedTest
184 @EnumSource(FS.class)
185 void testGet_ToFile(FS fs) throws Exception {
186 setUp(fs);
187 Path file = tempDir.resolve("testGet_ToFile");
188 Files.write(file, "whatever".getBytes(StandardCharsets.UTF_8));
189 RecordingTransportListener listener = new RecordingTransportListener();
190 GetTask task = new GetTask(URI.create("file.txt")).setDataPath(file).setListener(listener);
191 transporter.get(task);
192 assertEquals("test", new String(Files.readAllBytes(file), StandardCharsets.UTF_8));
193 assertEquals(0L, listener.dataOffset);
194 assertEquals(4L, listener.dataLength);
195 assertEquals(1, listener.startedCount);
196 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
197 assertEquals("test", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
198 }
199
200 @ParameterizedTest
201 @EnumSource(FS.class)
202 void testGet_EmptyResource(FS fs) throws Exception {
203 setUp(fs);
204 Path file = tempDir.resolve("testGet_EmptyResource");
205 Files.write(file, "".getBytes(StandardCharsets.UTF_8));
206 RecordingTransportListener listener = new RecordingTransportListener();
207 GetTask task = new GetTask(URI.create("empty.txt")).setDataPath(file).setListener(listener);
208 transporter.get(task);
209 assertEquals("", new String(Files.readAllBytes(file), StandardCharsets.UTF_8));
210 assertEquals(0L, listener.dataOffset);
211 assertEquals(0L, listener.dataLength);
212 assertEquals(1, listener.startedCount);
213 assertEquals(0, listener.progressedCount);
214 assertEquals("", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
215 }
216
217 @ParameterizedTest
218 @EnumSource(FS.class)
219 void testGet_EncodedResourcePath(FS fs) throws Exception {
220 setUp(fs);
221 GetTask task = new GetTask(URI.create("some%20space.txt"));
222 transporter.get(task);
223 assertEquals("space", task.getDataString());
224 }
225
226 @ParameterizedTest
227 @EnumSource(FS.class)
228 void testGet_Fragment(FS fs) throws Exception {
229 setUp(fs);
230 GetTask task = new GetTask(URI.create("file.txt#ignored"));
231 transporter.get(task);
232 assertEquals("test", task.getDataString());
233 }
234
235 @ParameterizedTest
236 @EnumSource(FS.class)
237 void testGet_Query(FS fs) throws Exception {
238 setUp(fs);
239 GetTask task = new GetTask(URI.create("file.txt?ignored"));
240 transporter.get(task);
241 assertEquals("test", task.getDataString());
242 }
243
244 @ParameterizedTest
245 @EnumSource(FS.class)
246 void testGet_FileHandleLeak(FS fs) throws Exception {
247 setUp(fs);
248 for (int i = 0; i < 100; i++) {
249 Path file = tempDir.resolve("testGet_FileHandleLeak" + i);
250 transporter.get(new GetTask(URI.create("file.txt")).setDataPath(file));
251 if (fs.uriPrefix.startsWith("symlink+")) {
252 assertTrue(Files.isSymbolicLink(file));
253 assertTrue(Files.deleteIfExists(file), i + ", " + file.toAbsolutePath());
254 } else if (fs.uriPrefix.startsWith("hardlink+")) {
255 assertTrue(Files.isRegularFile(file));
256
257
258
259
260
261 } else {
262 assertTrue(Files.isRegularFile(file));
263 assertTrue(Files.deleteIfExists(file), i + ", " + file.toAbsolutePath());
264 }
265 }
266 }
267
268 @ParameterizedTest
269 @EnumSource(FS.class)
270 void testGet_NotFound(FS fs) throws Exception {
271 setUp(fs);
272 try {
273 transporter.get(new GetTask(URI.create("missing.txt")));
274 fail("Expected error");
275 } catch (ResourceNotFoundException e) {
276 assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
277 }
278 }
279
280 @ParameterizedTest
281 @EnumSource(FS.class)
282 void testGet_Closed(FS fs) throws Exception {
283 setUp(fs);
284 transporter.close();
285 try {
286 transporter.get(new GetTask(URI.create("file.txt")));
287 fail("Expected error");
288 } catch (IllegalStateException e) {
289 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
290 }
291 }
292
293 @ParameterizedTest
294 @EnumSource(FS.class)
295 void testGet_StartCancelled(FS fs) throws Exception {
296 setUp(fs);
297 RecordingTransportListener listener = new RecordingTransportListener();
298 listener.cancelStart = true;
299 GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
300 try {
301 transporter.get(task);
302 fail("Expected error");
303 } catch (TransferCancelledException e) {
304 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
305 }
306 assertEquals(0L, listener.dataOffset);
307 assertEquals(4L, listener.dataLength);
308 assertEquals(1, listener.startedCount);
309 assertEquals(0, listener.progressedCount);
310 }
311
312 @ParameterizedTest
313 @EnumSource(FS.class)
314 void testGet_ProgressCancelled(FS fs) throws Exception {
315 setUp(fs);
316 RecordingTransportListener listener = new RecordingTransportListener();
317 listener.cancelProgress = true;
318 GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
319 try {
320 transporter.get(task);
321 fail("Expected error");
322 } catch (TransferCancelledException e) {
323 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
324 }
325 assertEquals(0L, listener.dataOffset);
326 assertEquals(4L, listener.dataLength);
327 assertEquals(1, listener.startedCount);
328 assertEquals(1, listener.progressedCount);
329 }
330
331 @ParameterizedTest
332 @EnumSource(FS.class)
333 void testPut_FromMemory(FS fs) throws Exception {
334 setUp(fs);
335 RecordingTransportListener listener = new RecordingTransportListener();
336 PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
337 transporter.put(task);
338 assertEquals(0L, listener.dataOffset);
339 assertEquals(6L, listener.dataLength);
340 assertEquals(1, listener.startedCount);
341 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
342 assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
343 }
344
345 @ParameterizedTest
346 @EnumSource(FS.class)
347 void testPut_FromFile(FS fs) throws Exception {
348 setUp(fs);
349 Path file = tempDir.resolve("upload");
350 Files.write(file, "upload".getBytes(StandardCharsets.UTF_8));
351 RecordingTransportListener listener = new RecordingTransportListener();
352 PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataPath(file);
353 transporter.put(task);
354 assertEquals(0L, listener.dataOffset);
355 assertEquals(6L, listener.dataLength);
356 assertEquals(1, listener.startedCount);
357 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
358 assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
359 }
360
361 @ParameterizedTest
362 @EnumSource(FS.class)
363 void testPut_EmptyResource(FS fs) throws Exception {
364 setUp(fs);
365 RecordingTransportListener listener = new RecordingTransportListener();
366 PutTask task = new PutTask(URI.create("file.txt")).setListener(listener);
367 transporter.put(task);
368 assertEquals(0L, listener.dataOffset);
369 assertEquals(0L, listener.dataLength);
370 assertEquals(1, listener.startedCount);
371 assertEquals(0, listener.progressedCount);
372 assertEquals("", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
373 }
374
375 @ParameterizedTest
376 @EnumSource(FS.class)
377 void testPut_NonExistentParentDir(FS fs) throws Exception {
378 setUp(fs);
379 RecordingTransportListener listener = new RecordingTransportListener();
380 PutTask task = new PutTask(URI.create("dir/sub/dir/file.txt"))
381 .setListener(listener)
382 .setDataString("upload");
383 transporter.put(task);
384 assertEquals(0L, listener.dataOffset);
385 assertEquals(6L, listener.dataLength);
386 assertEquals(1, listener.startedCount);
387 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
388 assertEquals(
389 "upload",
390 new String(Files.readAllBytes(repoDir.resolve("dir/sub/dir/file.txt")), StandardCharsets.UTF_8));
391 }
392
393 @ParameterizedTest
394 @EnumSource(FS.class)
395 void testPut_EncodedResourcePath(FS fs) throws Exception {
396 setUp(fs);
397 RecordingTransportListener listener = new RecordingTransportListener();
398 PutTask task = new PutTask(URI.create("some%20space.txt"))
399 .setListener(listener)
400 .setDataString("OK");
401 transporter.put(task);
402 assertEquals(0L, listener.dataOffset);
403 assertEquals(2L, listener.dataLength);
404 assertEquals(1, listener.startedCount);
405 assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
406 assertEquals("OK", new String(Files.readAllBytes(repoDir.resolve("some space.txt")), StandardCharsets.UTF_8));
407 }
408
409 @ParameterizedTest
410 @EnumSource(FS.class)
411 void testPut_FileHandleLeak(FS fs) throws Exception {
412 setUp(fs);
413 for (int i = 0; i < 100; i++) {
414 Path src = tempDir.resolve("upload");
415 Files.write(src, "upload".getBytes(StandardCharsets.UTF_8));
416 Path dst = repoDir.resolve("file.txt");
417 transporter.put(new PutTask(URI.create("file.txt")).setDataPath(src));
418 assertTrue(Files.deleteIfExists(src), i + ", " + src.toAbsolutePath());
419 assertTrue(Files.deleteIfExists(dst), i + ", " + dst.toAbsolutePath());
420 }
421 }
422
423 @ParameterizedTest
424 @EnumSource(FS.class)
425 void testPut_Closed(FS fs) throws Exception {
426 setUp(fs);
427 transporter.close();
428 try {
429 transporter.put(new PutTask(URI.create("missing.txt")));
430 fail("Expected error");
431 } catch (IllegalStateException e) {
432 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
433 }
434 }
435
436 @ParameterizedTest
437 @EnumSource(FS.class)
438 void testPut_StartCancelled(FS fs) throws Exception {
439 setUp(fs);
440 RecordingTransportListener listener = new RecordingTransportListener();
441 listener.cancelStart = true;
442 PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
443 try {
444 transporter.put(task);
445 fail("Expected error");
446 } catch (TransferCancelledException e) {
447 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
448 }
449 assertEquals(0L, listener.dataOffset);
450 assertEquals(6L, listener.dataLength);
451 assertEquals(1, listener.startedCount);
452 assertEquals(0, listener.progressedCount);
453 assertFalse(Files.exists(repoDir.resolve("file.txt")));
454 }
455
456 @ParameterizedTest
457 @EnumSource(FS.class)
458 void testPut_ProgressCancelled(FS fs) throws Exception {
459 setUp(fs);
460 RecordingTransportListener listener = new RecordingTransportListener();
461 listener.cancelProgress = true;
462 PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
463 try {
464 transporter.put(task);
465 fail("Expected error");
466 } catch (TransferCancelledException e) {
467 assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
468 }
469 assertEquals(0L, listener.dataOffset);
470 assertEquals(6L, listener.dataLength);
471 assertEquals(1, listener.startedCount);
472 assertEquals(1, listener.progressedCount);
473 assertFalse(Files.exists(repoDir.resolve("file.txt")));
474 }
475
476 @Test
477 void testInit_BadProtocol() {
478 assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
479 }
480
481 @Test
482 void testInit_CaseInsensitiveProtocol() throws Exception {
483 newTransporter("file:/void");
484 newTransporter("FILE:/void");
485 newTransporter("File:/void");
486 }
487
488 @Test
489 void testInit_OpaqueUrl() throws Exception {
490 testInit("file:repository", "repository");
491 }
492
493 @Test
494 void testInit_OpaqueUrlTrailingSlash() throws Exception {
495 testInit("file:repository/", "repository");
496 }
497
498 @Test
499 void testInit_OpaqueUrlSpaces() throws Exception {
500 testInit("file:repo%20space", "repo space");
501 }
502
503 @Test
504 void testInit_OpaqueUrlSpacesDecoded() throws Exception {
505 testInit("file:repo space", "repo space");
506 }
507
508 @Test
509 void testInit_HierarchicalUrl() throws Exception {
510 testInit("file:/repository", "/repository");
511 }
512
513 @Test
514 void testInit_HierarchicalUrlTrailingSlash() throws Exception {
515 testInit("file:/repository/", "/repository");
516 }
517
518 @Test
519 void testInit_HierarchicalUrlSpaces() throws Exception {
520 testInit("file:/repo%20space", "/repo space");
521 }
522
523 @Test
524 void testInit_HierarchicalUrlSpacesDecoded() throws Exception {
525 testInit("file:/repo space", "/repo space");
526 }
527
528 @Test
529 void testInit_HierarchicalUrlRoot() throws Exception {
530 testInit("file:/", "/");
531 }
532
533 @Test
534 void testInit_HierarchicalUrlHostNoPath() throws Exception {
535 testInit("file://host/", "/");
536 }
537
538 @Test
539 void testInit_HierarchicalUrlHostPath() throws Exception {
540 testInit("file://host/dir", "/dir");
541 }
542
543 @Test
544 void testInit_NonDefaultFileSystemRelative() throws Exception {
545 try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
546 Path path = fs.getPath("dir");
547 testInit(path.toUri().toASCIIString(), "/work/dir");
548 }
549 }
550
551 @Test
552 void testInit_NonDefaultFileSystemAbsolute() throws Exception {
553 try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
554 Path path = fs.getPath("/dir");
555 testInit(path.toUri().toASCIIString(), "/dir");
556 }
557 }
558
559 private void testInit(String base, String expected) throws Exception {
560 newTransporter(base);
561 String exp = expected;
562 if (base.startsWith("file:")) {
563
564
565
566 exp = Paths.get(expected).toAbsolutePath().toString();
567 }
568
569
570 assertEquals(exp, ((FileTransporter) transporter).getBasePath().toString());
571 }
572 }