Skip to content

Commit 1f11a5a

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 1f1eab6 commit 1f11a5a

1 file changed

Lines changed: 292 additions & 60 deletions

File tree

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

Lines changed: 292 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,93 +3,325 @@
33
import com.github.dockerjava.core.util.CompressArchiveUtil;
44
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
55
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
6-
import org.apache.commons.io.IOUtils;
6+
import org.junit.Ignore;
77
import org.junit.Rule;
88
import org.junit.Test;
99
import org.junit.rules.TemporaryFolder;
1010

1111
import java.io.BufferedInputStream;
12+
import java.io.ByteArrayInputStream;
1213
import java.io.File;
1314
import java.io.FileInputStream;
14-
import java.io.FileOutputStream;
1515
import java.io.IOException;
16+
import java.io.InputStream;
1617
import java.nio.file.Files;
1718
import java.nio.file.Path;
19+
import java.util.ArrayList;
20+
import java.util.List;
1821
import java.util.zip.GZIPInputStream;
1922

2023
import static java.util.Arrays.asList;
21-
import static org.hamcrest.CoreMatchers.is;
22-
import static org.hamcrest.MatcherAssert.assertThat;
24+
import static org.junit.Assert.assertEquals;
25+
import static org.junit.Assert.assertNotNull;
26+
import static org.junit.Assert.assertTrue;
2327

2428
public class CompressArchiveUtilTest {
2529

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

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

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

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

0 commit comments

Comments
 (0)