/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package libcore.tzdata.update; import junit.framework.TestCase; import android.system.Os; import android.system.OsConstants; import android.system.StructStat; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import libcore.io.IoUtils; import libcore.io.Libcore; /** * Tests for {@link FileUtils}. */ public class FileUtilsTest extends TestCase { private List testFiles = new ArrayList<>(); @Override public void tearDown() throws Exception { // Delete in reverse order Collections.reverse(testFiles); for (File tempFile : testFiles) { tempFile.delete(); } super.tearDown(); } public void testCalculateChecksum() throws Exception { final String content = "Content"; File file1 = createTextFile(content); File file2 = createTextFile(content); File file3 = createTextFile(content + "!"); long file1CheckSum = FileUtils.calculateChecksum(file1); long file2CheckSum = FileUtils.calculateChecksum(file2); long file3Checksum = FileUtils.calculateChecksum(file3); assertEquals(file1CheckSum, file2CheckSum); assertTrue(file1CheckSum != file3Checksum); } public void testDeleteRecursive() throws Exception { File dir = createTempDir(); File file1 = createRegularFile(dir, "file1"); File file2 = createRegularFile(dir, "file2"); File symLink1 = createSymlink(file1, dir, "symLink1"); File subDir = createDir(dir, "subDir"); File file3 = createRegularFile(subDir, "subFile1"); File file4 = createRegularFile(subDir, "subFile2"); File symLink2 = createSymlink(file1, dir, "symLink2"); File otherDir = createTempDir(); File otherFile = createRegularFile(otherDir, "kept"); File linkToOtherDir = createSymlink(otherDir, subDir, "linkToOtherDir"); File linkToOtherFile = createSymlink(otherFile, subDir, "linkToOtherFile"); File[] filesToDelete = { dir, file1, file2, symLink1, subDir, file3, file4, symLink2, linkToOtherDir, linkToOtherFile }; File[] filesToKeep = { otherDir, otherFile }; assertFilesExist(filesToDelete); assertFilesExist(filesToKeep); FileUtils.deleteRecursive(dir); assertFilesDoNotExist(filesToDelete); assertFilesExist(filesToKeep); } public void testIsSymlink() throws Exception { File dir = createTempDir(); File subDir = createDir(dir, "subDir"); File fileInSubDir = createRegularFile(subDir, "fileInSubDir"); File normalFile = createRegularFile(dir, "normalFile"); File symlinkToDir = createSymlink(subDir, dir, "symlinkToDir"); File symlinkToFile = createSymlink(fileInSubDir, dir, "symlinkToFile"); File symlinkToFileInSubDir = createSymlink(fileInSubDir, dir, "symlinkToFileInSubDir"); File normalFileViaSymlink = new File(symlinkToDir, "normalFile"); assertFalse(FileUtils.isSymlink(dir)); assertFalse(FileUtils.isSymlink(subDir)); assertFalse(FileUtils.isSymlink(fileInSubDir)); assertFalse(FileUtils.isSymlink(normalFile)); assertTrue(FileUtils.isSymlink(symlinkToDir)); assertTrue(FileUtils.isSymlink(symlinkToFile)); assertTrue(FileUtils.isSymlink(symlinkToFileInSubDir)); assertFalse(FileUtils.isSymlink(normalFileViaSymlink)); } public void testCreateSubFile() throws Exception { File dir1 = createTempDir().getCanonicalFile(); File actualSubFile = FileUtils.createSubFile(dir1, "file"); assertEquals(new File(dir1, "file"), actualSubFile); File existingSubFile = createRegularFile(dir1, "file"); actualSubFile = FileUtils.createSubFile(dir1, "file"); assertEquals(existingSubFile, actualSubFile); File existingSubDir = createDir(dir1, "subdir"); actualSubFile = FileUtils.createSubFile(dir1, "subdir"); assertEquals(existingSubDir, actualSubFile); assertCreateSubFileThrows(dir1, "../file"); assertCreateSubFileThrows(dir1, "../../file"); assertCreateSubFileThrows(dir1, "../otherdir/file"); File dir2 = createTempDir().getCanonicalFile(); createSymlink(dir2, dir1, "symlinkToDir2"); assertCreateSubFileThrows(dir1, "symlinkToDir2"); assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir"); createRegularFile(dir1, "symlinkToDir2/fileInSymlinkedDir"); assertCreateSubFileThrows(dir1, "symlinkToDir2/fileInSymlinkedDir"); } public void testEnsureDirectoryExists() throws Exception { File dir = createTempDir(); File exists = new File(dir, "exists"); assertTrue(exists.mkdir()); assertTrue(exists.setReadable(true /* readable */, true /* ownerOnly */)); assertTrue(exists.setExecutable(true /* readable */, true /* ownerOnly */)); FileUtils.ensureDirectoriesExist(exists, true /* makeWorldReadable */); assertDirExistsAndIsAccessible(exists, false /* requireWorldReadable */); File subDir = new File(dir, "subDir"); assertFalse(subDir.exists()); FileUtils.ensureDirectoriesExist(subDir, true /* makeWorldReadable */); assertDirExistsAndIsAccessible(subDir, true /* requireWorldReadable */); File one = new File(dir, "one"); File two = new File(one, "two"); File three = new File(two, "three"); FileUtils.ensureDirectoriesExist(three, true /* makeWorldReadable */); assertDirExistsAndIsAccessible(one, true /* requireWorldReadable */); assertDirExistsAndIsAccessible(two, true /* requireWorldReadable */); assertDirExistsAndIsAccessible(three, true /* requireWorldReadable */); } public void testEnsureDirectoriesExist_noPermissions() throws Exception { File dir = createTempDir(); assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */); File unreadableSubDir = new File(dir, "unreadableSubDir"); assertTrue(unreadableSubDir.mkdir()); assertTrue(unreadableSubDir.setReadable(false /* readable */, true /* ownerOnly */)); assertTrue(unreadableSubDir.setExecutable(false /* readable */, true /* ownerOnly */)); File toCreate = new File(unreadableSubDir, "toCreate"); try { FileUtils.ensureDirectoriesExist(toCreate, true /* makeWorldReadable */); fail(); } catch (IOException expected) { } assertDirExistsAndIsAccessible(dir, false /* requireWorldReadable */); assertFalse(unreadableSubDir.canRead() && unreadableSubDir.canExecute()); assertFalse(toCreate.exists()); } public void testEnsureFileDoesNotExist() throws Exception { File dir = createTempDir(); FileUtils.ensureFileDoesNotExist(new File(dir, "doesNotExist")); File exists1 = createRegularFile(dir, "exists1"); assertTrue(exists1.exists()); FileUtils.ensureFileDoesNotExist(exists1); assertFalse(exists1.exists()); exists1 = createRegularFile(dir, "exists1"); File symlink = createSymlink(exists1, dir, "symlinkToFile"); assertTrue(symlink.exists()); FileUtils.ensureFileDoesNotExist(symlink); assertFalse(symlink.exists()); assertTrue(exists1.exists()); // Only files and symlinks supported. We do not delete directories. File emptyDir = createTempDir(); try { FileUtils.ensureFileDoesNotExist(emptyDir); fail(); } catch (IOException expected) { } assertTrue(emptyDir.exists()); } // This test does not pass when run as root because root can do anything even if the permissions // don't allow it. public void testEnsureFileDoesNotExist_noPermission() throws Exception { File dir = createTempDir(); File protectedDir = createDir(dir, "protected"); File undeletable = createRegularFile(protectedDir, "undeletable"); assertTrue(protectedDir.setWritable(false)); assertTrue(undeletable.exists()); try { FileUtils.ensureFileDoesNotExist(undeletable); fail(); } catch (IOException expected) { } finally { assertTrue(protectedDir.setWritable(true)); // Reset for clean-up } assertTrue(undeletable.exists()); } public void testCheckFilesExist() throws Exception { File dir = createTempDir(); createRegularFile(dir, "exists1"); File subDir = createDir(dir, "subDir"); createRegularFile(subDir, "exists2"); assertTrue(FileUtils.filesExist(dir, "exists1", "subDir/exists2")); assertFalse(FileUtils.filesExist(dir, "doesNotExist")); assertFalse(FileUtils.filesExist(dir, "subDir/doesNotExist")); } public void testReadLines() throws Exception { File file = createTextFile("One\nTwo\nThree\n"); List lines = FileUtils.readLines(file); assertEquals(3, lines.size()); assertEquals(lines, Arrays.asList("One", "Two", "Three")); } private File createTextFile(String contents) throws IOException { File file = File.createTempFile(getClass().getSimpleName(), ".txt"); try (FileOutputStream fos = new FileOutputStream(file)) { BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(fos, StandardCharsets.UTF_8)); writer.write(contents); writer.close(); } return file; } private File createSymlink(File file, File symlinkDir, String symlinkName) throws Exception { assertTrue(file.exists()); File symlink = new File(symlinkDir, symlinkName); Os.symlink(file.getAbsolutePath(), symlink.getAbsolutePath()); testFiles.add(symlink); return symlink; } private static void assertCreateSubFileThrows(File parentDir, String name) { try { FileUtils.createSubFile(parentDir, name); fail(); } catch (IOException expected) { assertTrue(expected.getMessage().contains("must exist beneath")); } } private static void assertFilesDoNotExist(File... files) { for (File f : files) { assertFalse(f + " unexpectedly exists", f.exists()); } } private static void assertFilesExist(File... files) { for (File f : files) { assertTrue(f + " expected to exist", f.exists()); } } private static void assertDirExistsAndIsAccessible(File dir, boolean requireWorldReadable) throws Exception { assertTrue(dir.exists() && dir.isDirectory() && dir.canRead() && dir.canExecute()); String path = dir.getCanonicalPath(); StructStat sb = Libcore.os.stat(path); int mask = OsConstants.S_IXUSR | OsConstants.S_IRUSR; if (requireWorldReadable) { mask = mask | OsConstants.S_IXGRP | OsConstants.S_IRGRP | OsConstants.S_IXOTH | OsConstants.S_IROTH; } assertTrue("Permission mask required: " + Integer.toOctalString(mask), (sb.st_mode & mask) == mask); } private File createTempDir() { final String tempPrefix = getClass().getSimpleName(); File tempDir = IoUtils.createTemporaryDirectory(tempPrefix); testFiles.add(tempDir); return tempDir; } private File createDir(File parentDir, String name) { File dir = new File(parentDir, name); assertTrue(dir.mkdir()); testFiles.add(dir); return dir; } private File createRegularFile(File dir, String name) throws Exception { File file = new File(dir, name); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write("Hello".getBytes()); } testFiles.add(file); return file; } }