1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.util;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import org.junit.After;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.junit.runners.JUnit4;
28 
29 import java.io.ByteArrayInputStream;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.Set;
37 
38 /** Functional tests for {@link FileUtil}. */
39 @RunWith(JUnit4.class)
40 public class FileUtilFuncTest {
41     private static final String PERMS_NONE = "---------";
42     private static final String PERMS_GRWX = "rwxrwx---";
43     private static final String ROOT_PERMS_GRWX = "rwxrwxr-x";
44     private static final String DPERMS_NONE = "d" + PERMS_NONE;
45     private static final String DPERMS_GRWX = "d" + PERMS_GRWX;
46     private static final String ROOT_DPERMS_GRWX = "d" + ROOT_PERMS_GRWX;
47 
48     private Set<File> mTempFiles = new HashSet<File>();
49 
50     @After
tearDown()51     public void tearDown() throws Exception {
52         for (File file : mTempFiles) {
53             if (file != null && file.exists()) {
54                 if (file.isDirectory()) {
55                     FileUtil.recursiveDelete(file);
56                 } else {
57                     file.delete();
58                 }
59             }
60         }
61     }
62 
63     /**
64      * Make sure that {@link FileUtil#mkdirsRWX} works when there are multiple levels of directories
65      */
66     @Test
testMkdirsRWX_multiLevel()67     public void testMkdirsRWX_multiLevel() throws IOException {
68         final int subdirCount = 5;
69         File tmpParentDir = createTempDir("foo");
70         // create a hierarchy of directories to be created
71         File[] subdirs = new File[subdirCount];
72         subdirs[0] = new File(tmpParentDir, "patient0");
73         for (int i = 1; i < subdirCount; i++) {
74             subdirs[i] = new File(subdirs[i - 1], String.format("subdir%d", i));
75         }
76         assertFalse(subdirs[0].exists());
77         FileUtil.mkdirsRWX(subdirs[subdirs.length - 1]);
78 
79         for (int i = 0; i < subdirCount; i++) {
80             assertTrue(subdirs[i].exists());
81             assertUnixPerms(subdirs[i], DPERMS_GRWX, ROOT_DPERMS_GRWX);
82         }
83     }
84 
85     /** Make sure that {@link FileUtil#mkdirsRWX} works in the basic case */
86     @Test
testMkdirsRWX_singleLevel()87     public void testMkdirsRWX_singleLevel() throws IOException {
88         File tmpParentDir = createTempDir("foo");
89         File subdir = new File(tmpParentDir, "subdirectory");
90         assertFalse(subdir.exists());
91         FileUtil.mkdirsRWX(subdir);
92         assertTrue(subdir.exists());
93         assertUnixPerms(subdir, DPERMS_GRWX, ROOT_DPERMS_GRWX);
94     }
95 
96     /**
97      * Make sure that {@link FileUtil#mkdirsRWX} works when the directory to be touched already
98      * exists
99      */
100     @Test
testMkdirsRWX_preExisting()101     public void testMkdirsRWX_preExisting() throws IOException {
102         File tmpParentDir = createTempDir("foo");
103         File subdir = new File(tmpParentDir, "subdirectory");
104         subdir.mkdir();
105         subdir.setExecutable(false, false);
106         subdir.setReadable(false, false);
107         subdir.setWritable(false, false);
108 
109         assertUnixPerms(subdir, DPERMS_NONE, null);
110         FileUtil.mkdirsRWX(subdir);
111         assertTrue(subdir.exists());
112         assertUnixPerms(subdir, DPERMS_GRWX, null);
113     }
114 
115     /** Simple test for {@link FileUtil#chmodGroupRW(File)}. */
116     @Test
testChmodGroupRW()117     public void testChmodGroupRW() throws IOException {
118         File tmpFile = createTempFile("foo", "txt");
119         tmpFile.setReadable(false);
120         tmpFile.setWritable(false);
121         FileUtil.chmodGroupRW(tmpFile);
122         assertTrue(tmpFile.canRead());
123         assertTrue(tmpFile.canWrite());
124     }
125 
126     /** Simple test for {@link FileUtil#createTempDir(String)}. */
127     @Test
testCreateTempDir()128     public void testCreateTempDir() throws IOException {
129         File tmpDir = createTempDir("foo");
130         assertTrue(tmpDir.exists());
131         assertTrue(tmpDir.isDirectory());
132     }
133 
134     /** Simple test for {@link FileUtil#createTempDir(String, File)}. */
135     @Test
testCreateTempDir_parentFile()136     public void testCreateTempDir_parentFile() throws IOException {
137         File tmpParentDir = createTempDir("foo");
138         File childDir = createTempDir("foochild", tmpParentDir);
139         assertTrue(childDir.exists());
140         assertTrue(childDir.isDirectory());
141         assertEquals(tmpParentDir.getAbsolutePath(), childDir.getParent());
142     }
143 
144     /** Simple test for {@link FileUtil#createTempFile(String, String)}. */
145     @Test
testCreateTempFile()146     public void testCreateTempFile() throws IOException {
147         File tmpFile = createTempFile("foo", ".txt");
148         assertTrue(tmpFile.exists());
149         assertTrue(tmpFile.isFile());
150         assertTrue(tmpFile.getName().startsWith("foo"));
151         assertTrue(tmpFile.getName().endsWith(".txt"));
152     }
153 
154     /** Simple test for {@link FileUtil#createTempFile(String, String, File)}. */
155     @Test
testCreateTempFile_parentDir()156     public void testCreateTempFile_parentDir() throws IOException {
157         File tmpParentDir = createTempDir("foo");
158 
159         File tmpFile = createTempFile("foo", ".txt", tmpParentDir);
160         assertTrue(tmpFile.exists());
161         assertTrue(tmpFile.isFile());
162         assertTrue(tmpFile.getName().startsWith("foo"));
163         assertTrue(tmpFile.getName().endsWith(".txt"));
164         assertEquals(tmpParentDir.getAbsolutePath(), tmpFile.getParent());
165     }
166 
167     /** Simple test method for {@link FileUtil#writeToFile(InputStream, File)}. */
168     @Test
testWriteToFile()169     public void testWriteToFile() throws IOException {
170         final String testContents = "this is the temp file test data";
171         InputStream input = new ByteArrayInputStream(testContents.getBytes());
172         File tmpFile = createTempFile("foo", ".txt");
173         FileUtil.writeToFile(input, tmpFile);
174         String readContents = StreamUtil.getStringFromStream(new FileInputStream(tmpFile));
175         assertEquals(testContents, readContents);
176     }
177 
178     @Test
testRecursiveDelete()179     public void testRecursiveDelete() throws IOException {
180         File tmpParentDir = createTempDir("foo");
181         File childDir = createTempDir("foochild", tmpParentDir);
182         File subFile = createTempFile("foo", ".txt", childDir);
183         FileUtil.recursiveDelete(tmpParentDir);
184         assertFalse(subFile.exists());
185         assertFalse(childDir.exists());
186         assertFalse(tmpParentDir.exists());
187     }
188 
189     /**
190      * Test {@link FileUtil#recursiveCopy(File, File)} to recursively copy a directory to an
191      * existing, empty directory.
192      */
193     @Test
testRecursiveCopy()194     public void testRecursiveCopy() throws IOException {
195         // create source tree
196         File tmpParentDir = createTempDir("foo");
197         File childDir = createTempDir("foochild", tmpParentDir);
198         File subFile = createTempFile("foo", ".txt", childDir);
199         FileUtil.writeToFile("foo", subFile);
200         // create empty destination directory
201         File destDir = createTempDir("dest");
202         // invoke target method
203         FileUtil.recursiveCopy(tmpParentDir, destDir);
204         // check tree was copied
205         File subFileCopy = new File(destDir, String.format("%s%s%s", childDir.getName(),
206                     File.separator, subFile.getName()));
207         assertTrue(subFileCopy.exists());
208         assertTrue(FileUtil.compareFileContents(subFile, subFileCopy));
209     }
210 
211     /**
212      * Test {@link FileUtil#recursiveCopy(File, File)} to recursively copy a directory to a
213      * directory that does not exist.
214      */
215     @Test
testRecursiveCopyToNonexistentTarget()216     public void testRecursiveCopyToNonexistentTarget() throws IOException {
217         // create source tree
218         File tmpParentDir = createTempDir("foo");
219         File childDir = createTempDir("foochild", tmpParentDir);
220         File subFile = createTempFile("foo", ".txt", childDir);
221         FileUtil.writeToFile("foo", subFile);
222         // generate an unique name for destination dir and make sure it doesn't exist
223         File destDir = createTempDir("dest");
224         assertTrue(destDir.delete());
225         assertFalse(destDir.exists());
226         // invoke target method
227         FileUtil.recursiveCopy(tmpParentDir, destDir);
228         // check that destination was created and tree was copied
229         File subFileCopy = new File(destDir, String.format("%s%s%s", childDir.getName(),
230                     File.separator, subFile.getName()));
231         assertTrue(destDir.exists());
232         assertTrue(subFileCopy.exists());
233         assertTrue(FileUtil.compareFileContents(subFile, subFileCopy));
234     }
235 
236     @Test
testFindDirsUnder()237     public void testFindDirsUnder() throws IOException {
238         File absRootDir = createTempDir("rootDir");
239         File relRootDir = new File(absRootDir.getName());
240         File absSubDir1 = createTempDir("subdir1", absRootDir);
241         File relSubDir1 = new File(relRootDir.getName(), absSubDir1.getName());
242         File absSubDir2 = createTempDir("subdir2", absRootDir);
243         File relSubDir2 = new File(relRootDir.getName(), absSubDir2.getName());
244         File aFile = createTempFile("aFile", ".txt", absSubDir2);
245 
246         HashSet<File> expected = new HashSet<File>();
247         Collections.addAll(expected, relRootDir, relSubDir1, relSubDir2);
248         assertEquals(expected, FileUtil.findDirsUnder(absRootDir, null));
249         expected.clear();
250         File fakeRoot = new File("fakeRoot");
251         Collections.addAll(expected,
252                     new File(fakeRoot, relRootDir.getPath()),
253                     new File(fakeRoot, relSubDir1.getPath()),
254                     new File(fakeRoot, relSubDir2.getPath()));
255         assertEquals("Failed to apply a new relative parent", expected,
256                     FileUtil.findDirsUnder(absRootDir, fakeRoot));
257         assertEquals("found something when passing null as a root dir", 0,
258                     FileUtil.findDirsUnder(null, null).size());
259         try {
260             FileUtil.findDirsUnder(aFile, null);
261             fail("should have thrown an excpetion when passing in something that's not a dir");
262         } catch (IllegalArgumentException e) {
263             assertTrue(true);
264         }
265     }
266 
267     /** Test method for {@link FileUtil#createTempFileForRemote(String, File)}. */
268     @Test
testCreateTempFileForRemote()269     public void testCreateTempFileForRemote() throws IOException {
270         String remoteFilePath = "path/userdata.img";
271         File tmpFile = FileUtil.createTempFileForRemote(remoteFilePath, null);
272         try {
273             assertTrue(tmpFile.getAbsolutePath().contains("userdata"));
274             assertTrue(tmpFile.getAbsolutePath().endsWith(".img"));
275         } finally {
276             FileUtil.deleteFile(tmpFile);
277         }
278     }
279 
280     /** Test method for {@link FileUtil#createTempFileForRemote(String, File)} for a nested path. */
281     @Test
testCreateTempFileForRemote_nested()282     public void testCreateTempFileForRemote_nested() throws IOException {
283         String remoteFilePath = "path/2path/userdata.img";
284         File tmpFile = FileUtil.createTempFileForRemote(remoteFilePath, null);
285         try {
286             assertTrue(tmpFile.getAbsolutePath().contains("userdata"));
287             assertTrue(tmpFile.getAbsolutePath().endsWith(".img"));
288         } finally {
289             FileUtil.deleteFile(tmpFile);
290         }
291     }
292 
293     /** Test {@link FileUtil#createTempFileForRemote(String, File)} for file with no extension */
294     @Test
testCreateTempFileForRemote_noext()295     public void testCreateTempFileForRemote_noext() throws IOException {
296         String remoteFilePath = "path/2path/userddddmg";
297         File tmpFile = FileUtil.createTempFileForRemote(remoteFilePath, null);
298         try {
299             assertTrue(tmpFile.getAbsolutePath().contains("userddddmg"));
300         } finally {
301             FileUtil.deleteFile(tmpFile);
302         }
303     }
304 
305     /** Test {@link FileUtil#createTempFileForRemote(String, File)} for a too small prefix. */
306     @Test
testCreateTempFileForRemote_short()307     public void testCreateTempFileForRemote_short() throws IOException {
308         String remoteFilePath = "path/2path/us.img";
309         File tmpFile = FileUtil.createTempFileForRemote(remoteFilePath, null);
310         try {
311             assertTrue(tmpFile.getAbsolutePath().contains("usXXX"));
312             assertTrue(tmpFile.getAbsolutePath().endsWith(".img"));
313         } finally {
314             FileUtil.deleteFile(tmpFile);
315         }
316     }
317 
318     /** Test {@link FileUtil#createTempFileForRemote(String, File)} for remoteFile in root path. */
319     @Test
testCreateTempFileForRemote_singleFile()320     public void testCreateTempFileForRemote_singleFile() throws IOException {
321         String remoteFilePath = "userdata.img";
322         File tmpFile = FileUtil.createTempFileForRemote(remoteFilePath, null);
323         try {
324             assertTrue(tmpFile.getAbsolutePath().contains("userdata"));
325             assertTrue(tmpFile.getAbsolutePath().endsWith(".img"));
326         } finally {
327             FileUtil.deleteFile(tmpFile);
328         }
329     }
330 
331     /**
332      * Verify {@link FileUtil#calculateMd5(File)} works.
333      *
334      * @throws IOException
335      */
336     @Test
testCalculateMd5()337     public void testCalculateMd5() throws IOException {
338         final String source = "testtesttesttesttest";
339         final String md5 = "f317f682fafe0309c6a423af0b4efa59";
340         File tmpFile = FileUtil.createTempFile("testCalculateMd5", ".txt");
341         try {
342             FileUtil.writeToFile(source, tmpFile);
343             String actualMd5 = FileUtil.calculateMd5(tmpFile);
344             assertEquals(md5, actualMd5);
345         } finally {
346             FileUtil.deleteFile(tmpFile);
347         }
348     }
349 
350     /**
351      * Verify {@link FileUtil#calculateBase64Md5(File)} works.
352      *
353      * @throws IOException
354      */
355     @Test
testCalculateBase64Md5()356     public void testCalculateBase64Md5() throws IOException {
357         final String source = "testtesttesttesttest";
358         final String base64Md5 = "8xf2gvr+AwnGpCOvC076WQ==";
359         File tmpFile = FileUtil.createTempFile("testCalculateMd5", ".txt");
360         try {
361             FileUtil.writeToFile(source, tmpFile);
362             String actualBase64Md5 = FileUtil.calculateBase64Md5(tmpFile);
363             assertEquals(base64Md5, actualBase64Md5);
364         } finally {
365             FileUtil.deleteFile(tmpFile);
366         }
367     }
368 
369     /** Test that {@link FileUtil#recursiveSymlink(File, File)} properly simlink files. */
370     @Test
testRecursiveSymlink()371     public void testRecursiveSymlink() throws IOException {
372         File dir1 = null;
373         File dest = null;
374         try {
375             dir1 = FileUtil.createTempDir("orig-dir");
376             File subdir1 = FileUtil.createTempDir("sub-dir", dir1);
377             File testFile = FileUtil.createTempFile("test", "file", subdir1);
378             dest = FileUtil.createTempDir("dest-dir");
379             FileUtil.recursiveSymlink(dir1, dest);
380             // check that file is in dest dir
381             assertNotNull(FileUtil.findFile(dest, testFile.getName()));
382         } finally {
383             FileUtil.recursiveDelete(dir1);
384             FileUtil.recursiveDelete(dest);
385         }
386     }
387 
388     /**
389      * Test that when a symlink dir is encountered by {@link FileUtil#recursiveDelete(File)}. The
390      * link itself is deleted but not followed.
391      */
392     @Test
testSymlinkDeletion()393     public void testSymlinkDeletion() throws IOException {
394         File dir1 = null;
395         File dest = null;
396         try {
397             dir1 = FileUtil.createTempDir("orig-dir");
398             File subdir1 = FileUtil.createTempDir("sub-dir", dir1);
399             File testFile = FileUtil.createTempFile("test", "file", subdir1);
400             dest = FileUtil.createTempDir("dest-dir");
401             dest.delete();
402             FileUtil.symlinkFile(dir1, dest);
403             // Check that file is in dest dir
404             assertNotNull(FileUtil.findFile(dest, testFile.getName()));
405             // Now delete the symlink
406             FileUtil.recursiveDelete(dest);
407             assertFalse(dest.exists());
408             // Ensure the orignal directory was not deleted (symlink was not followed).
409             assertTrue(subdir1.exists());
410         } finally {
411             FileUtil.recursiveDelete(dir1);
412             FileUtil.recursiveDelete(dest);
413         }
414     }
415 
416     // Assertions
assertUnixPerms(File file, String expPerms, String altExpPerms)417     private String assertUnixPerms(File file, String expPerms, String altExpPerms) {
418         String perms = ls(file.getPath());
419         assertTrue(
420                 String.format(
421                         "Expected file %s perms to be '%s' but they were '%s'.",
422                         file, expPerms, perms),
423                 perms.startsWith(expPerms) || perms.startsWith(altExpPerms));
424         return perms;
425     }
426 
427     // Helpers
ls(String path)428     private String ls(String path) {
429         CommandResult result =
430                 RunUtil.getDefault().runTimedCmdRetry(10 * 1000, 0, 3, "ls", "-ld", path);
431         return result.getStdout();
432     }
433 
createTempDir(String prefix)434     private File createTempDir(String prefix) throws IOException {
435         return createTempDir(prefix, null);
436     }
437 
createTempDir(String prefix, File parentDir)438     private File createTempDir(String prefix, File parentDir) throws IOException {
439         File tempDir = FileUtil.createTempDir(prefix, parentDir);
440         mTempFiles.add(tempDir);
441         return tempDir;
442     }
443 
createTempFile(String prefix, String suffix)444     private File createTempFile(String prefix, String suffix) throws IOException {
445         File tempFile = FileUtil.createTempFile(prefix, suffix);
446         mTempFiles.add(tempFile);
447         return tempFile;
448     }
449 
createTempFile(String prefix, String suffix, File parentDir)450     private File createTempFile(String prefix, String suffix, File parentDir) throws IOException {
451         File tempFile = FileUtil.createTempFile(prefix, suffix, parentDir);
452         mTempFiles.add(tempFile);
453         return tempFile;
454     }
455 }
456