1/*
2 * Copyright (C) 2009 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
17package libcore.java.io;
18
19import java.io.BufferedReader;
20import java.io.File;
21import java.io.FileFilter;
22import java.io.FilenameFilter;
23import java.io.InputStreamReader;
24import java.io.IOException;
25import java.util.UUID;
26import libcore.io.Libcore;
27
28public class FileTest extends junit.framework.TestCase {
29    private static File createTemporaryDirectory() throws Exception {
30        String base = System.getProperty("java.io.tmpdir");
31        File directory = new File(base, UUID.randomUUID().toString());
32        assertTrue(directory.mkdirs());
33        return directory;
34    }
35
36    private static String longString(int n) {
37        StringBuilder result = new StringBuilder();
38        for (int i = 0; i < n; ++i) {
39            result.append('x');
40        }
41        return result.toString();
42    }
43
44    private static File createDeepStructure(File base) throws Exception {
45        // ext has a limit of around 256 characters for each path entry.
46        // 128 characters should be safe for everything but FAT.
47        String longString = longString(128);
48        // Keep creating subdirectories until the path length is greater than 1KiB.
49        // Ubuntu 8.04's kernel is happy up to about 4KiB.
50        File f = base;
51        for (int i = 0; f.toString().length() <= 1024; ++i) {
52            f = new File(f, longString);
53            assertTrue(f.mkdir());
54        }
55        return f;
56    }
57
58    // Rather than test all methods, assume that if createTempFile creates a long path and
59    // exists can see it, the code for coping with long paths (shared by all methods) works.
60    public void test_longPath() throws Exception {
61        File base = createTemporaryDirectory();
62        assertTrue(createDeepStructure(base).exists());
63    }
64
65    // readlink(2) is a special case,.
66    public void test_longReadlink() throws Exception {
67        File base = createTemporaryDirectory();
68        File target = createDeepStructure(base);
69        File source = new File(base, "source");
70        assertFalse(source.exists());
71        assertTrue(target.exists());
72        assertTrue(target.getCanonicalPath().length() > 1024);
73        ln_s(target, source);
74        assertTrue(source.exists());
75        assertEquals(target.getCanonicalPath(), source.getCanonicalPath());
76    }
77
78    // TODO: File.list is a special case too, but I haven't fixed it yet, and the new code,
79    // like the old code, will die of a native buffer overrun if we exercise it.
80
81    public void test_emptyFilename() throws Exception {
82        // The behavior of the empty filename is an odd mixture.
83        File f = new File("");
84        // Mostly it behaves like an invalid path...
85        assertFalse(f.canExecute());
86        assertFalse(f.canRead());
87        assertFalse(f.canWrite());
88        try {
89            f.createNewFile();
90            fail("expected IOException");
91        } catch (IOException expected) {
92        }
93        assertFalse(f.delete());
94        f.deleteOnExit();
95        assertFalse(f.exists());
96        assertEquals("", f.getName());
97        assertEquals(null, f.getParent());
98        assertEquals(null, f.getParentFile());
99        assertEquals("", f.getPath());
100        assertFalse(f.isAbsolute());
101        assertFalse(f.isDirectory());
102        assertFalse(f.isFile());
103        assertFalse(f.isHidden());
104        assertEquals(0, f.lastModified());
105        assertEquals(0, f.length());
106        assertEquals(null, f.list());
107        assertEquals(null, f.list(null));
108        assertEquals(null, f.listFiles());
109        assertEquals(null, f.listFiles((FileFilter) null));
110        assertEquals(null, f.listFiles((FilenameFilter) null));
111        assertFalse(f.mkdir());
112        assertFalse(f.mkdirs());
113        assertFalse(f.renameTo(f));
114        assertFalse(f.setLastModified(123));
115        assertFalse(f.setExecutable(true));
116        assertFalse(f.setReadOnly());
117        assertFalse(f.setReadable(true));
118        assertFalse(f.setWritable(true));
119        // ...but sometimes it behaves like "user.dir".
120        String cwd = System.getProperty("user.dir");
121        assertEquals(new File(cwd), f.getAbsoluteFile());
122        assertEquals(cwd, f.getAbsolutePath());
123        // TODO: how do we test these without hard-coding assumptions about where our temporary
124        // directory is? (In practice, on Android, our temporary directory is accessed through
125        // a symbolic link, so the canonical file/path will be different.)
126        //assertEquals(new File(cwd), f.getCanonicalFile());
127        //assertEquals(cwd, f.getCanonicalPath());
128    }
129
130    // http://b/2486943 - between eclair and froyo, we added a call to
131    // isAbsolute from the File constructor, potentially breaking subclasses.
132    public void test_subclassing() throws Exception {
133        class MyFile extends File {
134            private String field;
135            MyFile(String s) {
136                super(s);
137                field = "";
138            }
139            @Override public boolean isAbsolute() {
140                field.length();
141                return super.isAbsolute();
142            }
143        }
144        new MyFile("");
145    }
146
147    // http://b/3047893 - getCanonicalPath wasn't actually resolving symbolic links.
148    public void test_getCanonicalPath() throws Exception {
149        // This assumes you can create symbolic links in the temporary directory. This isn't
150        // true on Android if you're using /sdcard. It will work in /data/local though.
151        File base = createTemporaryDirectory();
152        File target = new File(base, "target");
153        target.createNewFile(); // The RI won't follow a dangling symlink, which seems like a bug!
154        File linkName = new File(base, "link");
155        ln_s(target, linkName);
156        assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
157
158        // .../subdir/shorter -> .../target (using a link to ../target).
159        File subdir = new File(base, "subdir");
160        assertTrue(subdir.mkdir());
161        linkName = new File(subdir, "shorter");
162        ln_s("../target", linkName.toString());
163        assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
164
165        // .../l -> .../subdir/longer (using a relative link to subdir/longer).
166        linkName = new File(base, "l");
167        ln_s("subdir/longer", linkName.toString());
168        File longer = new File(base, "subdir/longer");
169        longer.createNewFile(); // The RI won't follow a dangling symlink, which seems like a bug!
170        assertEquals(longer.getCanonicalPath(), linkName.getCanonicalPath());
171
172        // .../double -> .../target (via a link into subdir and a link back out).
173        linkName = new File(base, "double");
174        ln_s("subdir/shorter", linkName.toString());
175        assertEquals(target.getCanonicalPath(), linkName.getCanonicalPath());
176    }
177
178    private static void ln_s(File target, File linkName) throws Exception {
179        ln_s(target.toString(), linkName.toString());
180    }
181
182    private static void ln_s(String target, String linkName) throws Exception {
183        Libcore.os.symlink(target, linkName);
184    }
185
186    public void test_createNewFile() throws Exception {
187        File f = File.createTempFile("FileTest", "tmp");
188        assertFalse(f.createNewFile()); // EEXIST -> false
189        assertFalse(f.getParentFile().createNewFile()); // EEXIST -> false, even if S_ISDIR
190        try {
191            new File(f, "poop").createNewFile(); // ENOTDIR -> throw
192            fail();
193        } catch (IOException expected) {
194        }
195        try {
196            new File("").createNewFile(); // ENOENT -> throw
197            fail();
198        } catch (IOException expected) {
199        }
200    }
201
202    public void test_rename() throws Exception {
203        File f = File.createTempFile("FileTest", "tmp");
204        assertFalse(f.renameTo(new File("")));
205        assertFalse(new File("").renameTo(f));
206        assertFalse(f.renameTo(new File(".")));
207        assertTrue(f.renameTo(f));
208    }
209
210    public void test_getAbsolutePath() throws Exception {
211        String originalUserDir = System.getProperty("user.dir");
212        try {
213            File f = new File("poop");
214            System.setProperty("user.dir", "/a");
215            assertEquals("/a/poop", f.getAbsolutePath());
216            System.setProperty("user.dir", "/b");
217            assertEquals("/b/poop", f.getAbsolutePath());
218        } finally {
219            System.setProperty("user.dir", originalUserDir);
220        }
221    }
222
223    public void test_getSpace() throws Exception {
224        assertTrue(new File("/").getFreeSpace() >= 0);
225        assertTrue(new File("/").getTotalSpace() >= 0);
226        assertTrue(new File("/").getUsableSpace() >= 0);
227    }
228
229    public void test_mkdirs() throws Exception {
230        // Set up a directory to test in.
231        File base = createTemporaryDirectory();
232
233        // mkdirs returns true only if it _creates_ a directory.
234        // So we get false for a directory that already exists...
235        assertTrue(base.exists());
236        assertFalse(base.mkdirs());
237        // But true if we had to create something.
238        File a = new File(base, "a");
239        assertFalse(a.exists());
240        assertTrue(a.mkdirs());
241        assertTrue(a.exists());
242
243        // Test the recursive case where we need to create multiple parents.
244        File b = new File(a, "b");
245        File c = new File(b, "c");
246        File d = new File(c, "d");
247        assertTrue(a.exists());
248        assertFalse(b.exists());
249        assertFalse(c.exists());
250        assertFalse(d.exists());
251        assertTrue(d.mkdirs());
252        assertTrue(a.exists());
253        assertTrue(b.exists());
254        assertTrue(c.exists());
255        assertTrue(d.exists());
256
257        // Test the case where the 'directory' exists as a file.
258        File existsAsFile = new File(base, "existsAsFile");
259        existsAsFile.createNewFile();
260        assertTrue(existsAsFile.exists());
261        assertFalse(existsAsFile.mkdirs());
262
263        // Test the case where the parent exists as a file.
264        File badParent = new File(existsAsFile, "sub");
265        assertTrue(existsAsFile.exists());
266        assertFalse(badParent.exists());
267        assertFalse(badParent.mkdirs());
268    }
269}
270