Skip to content

Commit 356c209

Browse files
committed
Fix CompressArchiveUtil unit tests
The tar method's contract is very clear, it creates a tar archive from files in a folder. The unit tests should validate that the tar archive was created and that the content is what it is supposed to be. Same apply to the archiveTARFiles method. Before, in order to validate that the tar archive was created, the unit tests were un-archiving the tar. This is wrong as you end up implicitly testing the extraction code which could be faulty. In addition, the 2 tests for symlinks were not even checking if the extracted file was a symlink, they were only checking if the file or folder were readable. Rewrite the tests to assert directly the content of the archive. Also fix the 2 symlinks tests to make sure the tar preserve them. Fixing the unit tests exposed that CompressArchiveUtil methods do not preserve the symlinks which is a bug. Add @ignore annotation for those tests until the bug is fixed. This change is a preparation change to fix the archive creation to preserve symlinks so add tests to increase archive creation code coverage close to 100%. This will allow to fix the code while making sure no regression are introduced. Related to issue #532
1 parent 6566fd8 commit 356c209

File tree

1 file changed

+292
-60
lines changed

1 file changed

+292
-60
lines changed

src/test/java/com/github/dockerjava/core/util/CompressArchiveUtilTest.java

Lines changed: 292 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,93 +2,325 @@
22

33
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
44
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
5-
import org.apache.commons.io.IOUtils;
5+
import org.junit.Ignore;
66
import org.junit.Rule;
77
import org.junit.Test;
88
import org.junit.rules.TemporaryFolder;
99

1010
import java.io.BufferedInputStream;
11+
import java.io.ByteArrayInputStream;
1112
import java.io.File;
1213
import java.io.FileInputStream;
13-
import java.io.FileOutputStream;
1414
import java.io.IOException;
15+
import java.io.InputStream;
1516
import java.nio.file.Files;
1617
import java.nio.file.Path;
18+
import java.util.ArrayList;
19+
import java.util.List;
1720
import java.util.zip.GZIPInputStream;
1821

1922
import static java.util.Arrays.asList;
20-
import static org.hamcrest.CoreMatchers.is;
21-
import static org.hamcrest.MatcherAssert.assertThat;
23+
import static org.junit.Assert.assertEquals;
24+
import static org.junit.Assert.assertNotNull;
25+
import static org.junit.Assert.assertTrue;
2226

2327
public class CompressArchiveUtilTest {
2428

2529
@Rule
2630
public TemporaryFolder tempFolder = new TemporaryFolder();
2731

2832
@Test
29-
public void testExecutableFlagIsPreserved() throws Exception {
33+
public void tarWithRegularFileAsInput() throws Exception {
34+
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("sourceFile");
35+
createFileWithContent(archiveSourceFile);
36+
37+
// ChildrenOnly = false
38+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
39+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
40+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
41+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "sourceFile");
42+
43+
// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
44+
// to make sure it behaves as expected
45+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
46+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
47+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
48+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "sourceFile");
49+
}
50+
51+
@Test
52+
public void tarWithExecutableFileAsInput() throws Exception {
53+
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("executableFile.sh");
54+
createFileWithContent(archiveSourceFile);
55+
archiveSourceFile.toFile().setExecutable(true);
56+
57+
// ChildrenOnly = false
58+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
59+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
60+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
61+
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
62+
63+
// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
64+
// to make sure it behaves as expected
65+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
66+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
67+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
68+
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
69+
}
70+
71+
@Test
72+
@Ignore("Symlink creation is broken so test do not pass")
73+
public void tarWithSymbolicLinkFileAsInput() throws IOException {
74+
Path archiveSourceFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
75+
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
76+
Files.createSymbolicLink(archiveSourceFile, linkTargetFile);
77+
78+
// ChildrenOnly = false
79+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
80+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
81+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
82+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
83+
84+
// ChildrenOnly = true, this option make no sense when input is a file but still, let's test it
85+
// to make sure it behaves as expected
86+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
87+
CompressArchiveUtil.tar(archiveSourceFile, tarGzFile, true, false);
88+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
89+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
90+
}
91+
92+
@Test
93+
public void tarWithfolderAsInput() throws Exception {
94+
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
95+
createFoldersAndSubFolderWithFiles(archiveSourceDir);
96+
97+
// ChildrenOnly = false
98+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
99+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
100+
assertEquals(7, getNumberOfEntryInArchive(tarGzFile.toFile()));
101+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
102+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderA");
103+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderB");
104+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "subFolderB");
105+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileA");
106+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileB");
107+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "subFileB");
108+
109+
// ChildrenOnly = true
110+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
111+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
112+
assertEquals(6, getNumberOfEntryInArchive(tarGzFile.toFile()));
113+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderA");
114+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "folderB");
115+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "subFolderB");
116+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileA");
117+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "fileB");
118+
assertTarArchiveEntryIsNonEmptyFile(tarGzFile.toFile(), "subFileB");
119+
}
120+
121+
@Test
122+
public void tarWithfolderAsInputAndNestedExecutableFile() throws Exception {
123+
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
124+
Path executableFile = archiveSourceDir.resolve("executableFile.sh");
125+
createFileWithContent(executableFile);
126+
executableFile.toFile().setExecutable(true);
127+
128+
// ChildrenOnly = false
129+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
130+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
131+
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
132+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
133+
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
134+
135+
// ChildrenOnly = true
136+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
137+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
138+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
139+
assertTarArchiveEntryIsExecutableFile(tarGzFile.toFile(), "executableFile.sh");
140+
}
141+
142+
@Test
143+
@Ignore("Symlink creation is broken so test do not pass")
144+
public void tarWithfolderAsInputAndNestedSymbolicLinkFile() throws Exception {
145+
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
146+
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
147+
Path symlinkFile = archiveSourceDir.resolve("symlinkFile");
148+
Files.createSymbolicLink(symlinkFile, linkTargetFile);
149+
150+
// ChildrenOnly = false
151+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
152+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
153+
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
154+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
155+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
156+
157+
// ChildrenOnly = true
158+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
159+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
160+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
161+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetFile.toString());
162+
}
163+
164+
@Test
165+
@Ignore("Symlink creation is broken so test do not pass")
166+
public void tarWithfolderAsInputAndNestedSymbolicLinkDir() throws Exception {
167+
Path archiveSourceDir = tempFolder.newFolder("archive-source").toPath();
168+
Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
169+
Path symlinkFile = archiveSourceDir.resolve("symlinkFile");
170+
Files.createSymbolicLink(symlinkFile, linkTargetDir);
171+
172+
// ChildrenOnly = false
173+
Path tarGzFile = tempFolder.newFile("archive.tar.gz").toPath();
174+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, false);
175+
assertEquals(2, getNumberOfEntryInArchive(tarGzFile.toFile()));
176+
assertTarArchiveEntryIsDirectory(tarGzFile.toFile(), "archive-source");
177+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetDir.toString());
178+
179+
// ChildrenOnly = true
180+
tarGzFile = tempFolder.newFile("archiveChildrenOnly.tar.gz").toPath();
181+
CompressArchiveUtil.tar(archiveSourceDir, tarGzFile, true, true);
182+
assertEquals(1, getNumberOfEntryInArchive(tarGzFile.toFile()));
183+
assertTarArchiveEntryIsSymlink(tarGzFile.toFile(), "symlinkFile", linkTargetDir.toString());
184+
}
185+
186+
@Test
187+
public void archiveTARFilesWithFolderAndFiles() throws Exception {
188+
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(),
189+
createFoldersAndSubFolderWithFiles(tempFolder.getRoot().toPath()), "archive");
190+
assertEquals(6, getNumberOfEntryInArchive(archive));
191+
assertTarArchiveEntryIsDirectory(archive, "folderA");
192+
assertTarArchiveEntryIsDirectory(archive, "folderB");
193+
assertTarArchiveEntryIsDirectory(archive, "subFolderB");
194+
assertTarArchiveEntryIsNonEmptyFile(archive, "fileA");
195+
assertTarArchiveEntryIsNonEmptyFile(archive, "fileB");
196+
assertTarArchiveEntryIsNonEmptyFile(archive, "subFileB");
197+
}
198+
199+
@Test
200+
public void archiveTARFilesWithExecutableFile() throws Exception {
30201
File executableFile = tempFolder.newFile("executableFile.sh");
31202
executableFile.setExecutable(true);
32-
assertThat(executableFile.canExecute(), is(true));
33-
34-
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(executableFile),
35-
"archive");
36-
File expectedFile = extractFileByName(archive, "executableFile.sh", "executableFile.sh.result");
37-
38-
assertThat("should be executable", expectedFile.canExecute());
39-
}
40-
41-
private File extractFileByName(File archive, String filenameToExtract, String outputName) throws IOException {
42-
File expectedFile = new File(tempFolder.newFolder(), outputName);
43-
expectedFile.delete();
44-
assertThat(expectedFile.exists(), is(false));
45-
46-
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(new GZIPInputStream(
47-
new BufferedInputStream(new FileInputStream(archive))));
48-
TarArchiveEntry entry;
49-
boolean found = false;
50-
while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
51-
String individualFiles = entry.getName();
52-
if (individualFiles.equals(filenameToExtract) || individualFiles.endsWith("/" + filenameToExtract)) {
53-
found = true;
54-
IOUtils.copy(tarArchiveInputStream, new FileOutputStream(expectedFile));
55-
if ((entry.getMode() & 0755) == 0755) {
56-
expectedFile.setExecutable(true);
57-
}
58-
break;
59-
}
60-
}
61-
assertThat("should extracted the file", found);
62-
tarArchiveInputStream.close();
63-
return expectedFile;
203+
204+
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(executableFile), "archive");
205+
assertEquals(1, getNumberOfEntryInArchive(archive));
206+
assertTarArchiveEntryIsExecutableFile(archive, "executableFile.sh");
64207
}
65208

66209
@Test
67-
public void testSymbolicLinkDir() throws IOException {
68-
Path uploadDir = tempFolder.newFolder("upload").toPath();
69-
Path linkTarget = tempFolder.newFolder("link-target").toPath();
70-
Path tmpFile = Files.createTempFile(linkTarget, "link-dir", "rand");
71-
Files.createSymbolicLink(uploadDir.resolve("link-folder"), linkTarget);
72-
Path tarGzFile = tempFolder.newFile("docker-java.tar.gz").toPath();
73-
//follow link only works for childrenOnly=false
74-
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, false);
75-
File expectedFile = extractFileByName(tarGzFile.toFile(), tmpFile.toFile().getName(), "foo1");
76-
assertThat(expectedFile.canRead(), is(true));
210+
@Ignore("Symlink creation is broken so test do not pass")
211+
public void archiveTARFilesWithSymbolicLinkFile() throws Exception {
212+
Path linkTargetFile = tempFolder.newFile("link-target").toPath();
213+
Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
214+
Files.createSymbolicLink(symlinkFile, linkTargetFile);
215+
216+
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(symlinkFile.toFile()), "archive");
217+
assertEquals(1, getNumberOfEntryInArchive(archive));
218+
assertTarArchiveEntryIsSymlink(archive, "symlinkFile", linkTargetFile.toString());
77219
}
78220

79221
@Test
80-
public void testSymbolicLinkFile() throws IOException {
81-
Path uploadDir = tempFolder.newFolder("upload").toPath();
82-
Path tmpFile = tempFolder.newFile("src").toPath();
83-
Files.createSymbolicLink(uploadDir.resolve("link-file"), tmpFile);
84-
Path tarGzFile = tempFolder.newFile("docker-java.tar.gz").toPath();
85-
boolean childrenOnly = false;
86-
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, childrenOnly);
87-
File expectedFile = extractFileByName(tarGzFile.toFile(), "link-file", "foo1");
88-
assertThat(expectedFile.canRead(), is(true));
89-
childrenOnly = true;
90-
CompressArchiveUtil.tar(uploadDir, tarGzFile, true, childrenOnly);
91-
extractFileByName(tarGzFile.toFile(), "link-file", "foo1");
92-
assertThat(expectedFile.canRead(), is(true));
222+
@Ignore("Symlink creation is broken so test do not pass")
223+
public void archiveTARFilesWithSymbolicLinkDir() throws Exception {
224+
Path linkTargetDir = tempFolder.newFolder("link-target").toPath();
225+
Path symlinkFile = tempFolder.getRoot().toPath().resolve("symlinkFile");
226+
Files.createSymbolicLink(symlinkFile, linkTargetDir);
227+
228+
File archive = CompressArchiveUtil.archiveTARFiles(tempFolder.getRoot(), asList(symlinkFile.toFile()), "archive");
229+
assertEquals(1, getNumberOfEntryInArchive(archive));
230+
assertTarArchiveEntryIsSymlink(archive, "symlinkFile", linkTargetDir.toString());
231+
}
232+
233+
private static void assertTarArchiveEntryIsDirectory(File archive, String directoryName) throws IOException {
234+
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, directoryName);
235+
assertNotNull(tarArchiveEntry);
236+
assertTrue(tarArchiveEntry.isDirectory());
237+
}
238+
239+
private static void assertTarArchiveEntryIsNonEmptyFile(File archive, String fileName) throws IOException {
240+
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
241+
assertNotNull(tarArchiveEntry);
242+
assertTrue(tarArchiveEntry.isFile());
243+
assertTrue(tarArchiveEntry.getSize()>0);
244+
}
245+
246+
private static void assertTarArchiveEntryIsExecutableFile(File archive, String fileName) throws IOException {
247+
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
248+
assertNotNull(tarArchiveEntry);
249+
assertTrue(tarArchiveEntry.isFile());
250+
assertEquals("should be executable", (tarArchiveEntry.getMode() & 0755), 0755);
251+
}
252+
253+
private static void assertTarArchiveEntryIsSymlink(File archive, String fileName, String expectedTarget) throws IOException {
254+
TarArchiveEntry tarArchiveEntry = getTarArchiveEntry(archive, fileName);
255+
assertNotNull(tarArchiveEntry);
256+
assertTrue("should be a symbolic link", tarArchiveEntry.isSymbolicLink());
257+
assertEquals(expectedTarget, tarArchiveEntry.getLinkName());
258+
}
259+
260+
/**
261+
* Creates the following directory structure with files in the specified
262+
* destination folder
263+
*
264+
* destinationFolder
265+
* |__folderA
266+
* | |__fileA
267+
* |__folderB
268+
* |__fileB
269+
* |__subFolderB
270+
* |__subFileB
271+
*
272+
*
273+
* @param destinationFolder where to create the folder/files.
274+
* @return the list of created files.
275+
* @throws IOException if an error occurs while creating the folders/files.
276+
*/
277+
private static List<File> createFoldersAndSubFolderWithFiles(Path destinationFolder) throws IOException {
278+
List<File> createdFiles = new ArrayList<File>();
279+
Path folderA = destinationFolder.resolve("folderA");
280+
createdFiles.add(Files.createDirectories(folderA).toFile());
281+
createdFiles.add(createFileWithContent(folderA.resolve("fileA")));
282+
283+
Path folderB = destinationFolder.resolve("folderB");
284+
createdFiles.add(Files.createDirectories(folderB).toFile());
285+
createdFiles.add(createFileWithContent(folderB.resolve("fileB")));
286+
287+
Path subFolderB = folderB.resolve("subFolderB");
288+
createdFiles.add(Files.createDirectories(subFolderB).toFile());
289+
createdFiles.add(createFileWithContent(folderA.resolve("subFileB")));
290+
return createdFiles;
291+
}
292+
293+
private static File createFileWithContent(Path fileToCreate) throws IOException {
294+
try (InputStream in = new ByteArrayInputStream("some content".getBytes())) {
295+
Files.copy(in, fileToCreate);
296+
}
297+
return fileToCreate.toFile();
298+
}
299+
300+
private static TarArchiveEntry getTarArchiveEntry(File tarArchive, String filename) throws IOException {
301+
try (TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(
302+
new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarArchive))))) {
303+
TarArchiveEntry entry;
304+
while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
305+
if (entry.getName().equals(filename)
306+
|| entry.getName().endsWith("/" + filename)
307+
|| entry.getName().equals(filename + "/")
308+
|| entry.getName().endsWith("/" + filename + "/")) {
309+
return entry;
310+
}
311+
}
312+
}
313+
return null;
314+
}
315+
316+
private static int getNumberOfEntryInArchive(File tarArchive) throws IOException {
317+
int numberOfEntries = 0;
318+
try (TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(
319+
new GZIPInputStream(new BufferedInputStream(new FileInputStream(tarArchive))))) {
320+
while ((tarArchiveInputStream.getNextTarEntry()) != null) {
321+
numberOfEntries++;
322+
}
323+
}
324+
return numberOfEntries;
93325
}
94326
}

0 commit comments

Comments
 (0)