1/*
2 * Copyright (C) 2008 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 android.os;
18
19import static android.text.format.DateUtils.DAY_IN_MILLIS;
20import static android.text.format.DateUtils.HOUR_IN_MILLIS;
21import static android.text.format.DateUtils.WEEK_IN_MILLIS;
22
23import android.content.Context;
24import android.provider.DocumentsContract.Document;
25import android.test.AndroidTestCase;
26import android.test.suitebuilder.annotation.MediumTest;
27
28import com.google.android.collect.Sets;
29
30import libcore.io.IoUtils;
31
32import java.io.ByteArrayInputStream;
33import java.io.File;
34import java.io.FileOutputStream;
35import java.io.FileWriter;
36import java.util.Arrays;
37import java.util.HashSet;
38
39@MediumTest
40public class FileUtilsTest extends AndroidTestCase {
41    private static final String TEST_DATA =
42            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
43
44    private File mDir;
45    private File mTestFile;
46    private File mCopyFile;
47    private File mTarget;
48
49
50    @Override
51    protected void setUp() throws Exception {
52        super.setUp();
53        mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
54        mTestFile = new File(mDir, "test.file");
55        mCopyFile = new File(mDir, "copy.file");
56
57        mTarget = getContext().getFilesDir();
58        FileUtils.deleteContents(mTarget);
59    }
60
61    @Override
62    protected void tearDown() throws Exception {
63        IoUtils.deleteContents(mDir);
64        FileUtils.deleteContents(mTarget);
65    }
66
67    // TODO: test setPermissions(), getPermissions()
68
69    public void testCopyFile() throws Exception {
70        stageFile(mTestFile, TEST_DATA);
71        assertFalse(mCopyFile.exists());
72        FileUtils.copyFile(mTestFile, mCopyFile);
73        assertTrue(mCopyFile.exists());
74        assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
75    }
76
77    public void testCopyToFile() throws Exception {
78        final String s = "Foo Bar";
79        assertFalse(mCopyFile.exists());
80        FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile);
81        assertTrue(mCopyFile.exists());
82        assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
83    }
84
85    public void testIsFilenameSafe() throws Exception {
86        assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
87        assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
88        assertFalse(FileUtils.isFilenameSafe(new File("foo*bar")));
89        assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
90    }
91
92    public void testReadTextFile() throws Exception {
93        stageFile(mTestFile, TEST_DATA);
94
95        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
96
97        assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null));
98        assertEquals("ABCDE<>", FileUtils.readTextFile(mTestFile, 5, "<>"));
99        assertEquals(TEST_DATA.substring(0, 51) + "<>",
100                FileUtils.readTextFile(mTestFile, 51, "<>"));
101        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 52, "<>"));
102        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 100, "<>"));
103
104        assertEquals("vwxyz", FileUtils.readTextFile(mTestFile, -5, null));
105        assertEquals("<>vwxyz", FileUtils.readTextFile(mTestFile, -5, "<>"));
106        assertEquals("<>" + TEST_DATA.substring(1, 52),
107                FileUtils.readTextFile(mTestFile, -51, "<>"));
108        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -52, "<>"));
109        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
110    }
111
112    public void testReadTextFileWithZeroLengthFile() throws Exception {
113        stageFile(mTestFile, TEST_DATA);
114        new FileOutputStream(mTestFile).close();  // Zero out the file
115        assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
116        assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
117        assertEquals("", FileUtils.readTextFile(mTestFile, 10, "<>"));
118        assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>"));
119        assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
120    }
121
122    public void testContains() throws Exception {
123        assertTrue(FileUtils.contains(new File("/"), new File("/moo.txt")));
124        assertTrue(FileUtils.contains(new File("/"), new File("/")));
125
126        assertTrue(FileUtils.contains(new File("/sdcard"), new File("/sdcard")));
127        assertTrue(FileUtils.contains(new File("/sdcard/"), new File("/sdcard/")));
128
129        assertTrue(FileUtils.contains(new File("/sdcard"), new File("/sdcard/moo.txt")));
130        assertTrue(FileUtils.contains(new File("/sdcard/"), new File("/sdcard/moo.txt")));
131
132        assertFalse(FileUtils.contains(new File("/sdcard"), new File("/moo.txt")));
133        assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/moo.txt")));
134
135        assertFalse(FileUtils.contains(new File("/sdcard"), new File("/sdcard.txt")));
136        assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/sdcard.txt")));
137    }
138
139    public void testDeleteOlderEmptyDir() throws Exception {
140        FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
141        assertDirContents();
142    }
143
144    public void testDeleteOlderTypical() throws Exception {
145        touch("file1", HOUR_IN_MILLIS);
146        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
147        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
148        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
149        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
150        assertTrue(FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS));
151        assertDirContents("file1", "file2", "file3");
152    }
153
154    public void testDeleteOlderInFuture() throws Exception {
155        touch("file1", -HOUR_IN_MILLIS);
156        touch("file2", HOUR_IN_MILLIS);
157        touch("file3", WEEK_IN_MILLIS);
158        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
159        assertDirContents("file1", "file2");
160
161        touch("file1", -HOUR_IN_MILLIS);
162        touch("file2", HOUR_IN_MILLIS);
163        touch("file3", WEEK_IN_MILLIS);
164        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
165        assertDirContents("file1", "file2");
166    }
167
168    public void testDeleteOlderOnlyAge() throws Exception {
169        touch("file1", HOUR_IN_MILLIS);
170        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
171        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
172        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
173        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
174        assertTrue(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
175        assertFalse(FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS));
176        assertDirContents("file1");
177    }
178
179    public void testDeleteOlderOnlyCount() throws Exception {
180        touch("file1", HOUR_IN_MILLIS);
181        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
182        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
183        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
184        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
185        assertTrue(FileUtils.deleteOlderFiles(mDir, 2, 0));
186        assertFalse(FileUtils.deleteOlderFiles(mDir, 2, 0));
187        assertDirContents("file1", "file2");
188    }
189
190    public void testValidExtFilename() throws Exception {
191        assertTrue(FileUtils.isValidExtFilename("a"));
192        assertTrue(FileUtils.isValidExtFilename("foo.bar"));
193        assertTrue(FileUtils.isValidExtFilename("foo bar.baz"));
194        assertTrue(FileUtils.isValidExtFilename("foo.bar.baz"));
195        assertTrue(FileUtils.isValidExtFilename(".bar"));
196        assertTrue(FileUtils.isValidExtFilename("foo~!@#$%^&*()_[]{}+bar"));
197
198        assertFalse(FileUtils.isValidExtFilename(null));
199        assertFalse(FileUtils.isValidExtFilename("."));
200        assertFalse(FileUtils.isValidExtFilename("../foo"));
201        assertFalse(FileUtils.isValidExtFilename("/foo"));
202
203        assertEquals(".._foo", FileUtils.buildValidExtFilename("../foo"));
204        assertEquals("_foo", FileUtils.buildValidExtFilename("/foo"));
205        assertEquals("foo_bar", FileUtils.buildValidExtFilename("foo\0bar"));
206        assertEquals(".foo", FileUtils.buildValidExtFilename(".foo"));
207        assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
208    }
209
210    public void testValidFatFilename() throws Exception {
211        assertTrue(FileUtils.isValidFatFilename("a"));
212        assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
213        assertTrue(FileUtils.isValidFatFilename("foo.bar.baz"));
214        assertTrue(FileUtils.isValidFatFilename(".bar"));
215        assertTrue(FileUtils.isValidFatFilename("foo.bar"));
216        assertTrue(FileUtils.isValidFatFilename("foo bar"));
217        assertTrue(FileUtils.isValidFatFilename("foo+bar"));
218        assertTrue(FileUtils.isValidFatFilename("foo,bar"));
219
220        assertFalse(FileUtils.isValidFatFilename("foo*bar"));
221        assertFalse(FileUtils.isValidFatFilename("foo?bar"));
222        assertFalse(FileUtils.isValidFatFilename("foo<bar"));
223        assertFalse(FileUtils.isValidFatFilename(null));
224        assertFalse(FileUtils.isValidFatFilename("."));
225        assertFalse(FileUtils.isValidFatFilename("../foo"));
226        assertFalse(FileUtils.isValidFatFilename("/foo"));
227
228        assertEquals(".._foo", FileUtils.buildValidFatFilename("../foo"));
229        assertEquals("_foo", FileUtils.buildValidFatFilename("/foo"));
230        assertEquals(".foo", FileUtils.buildValidFatFilename(".foo"));
231        assertEquals("foo.bar", FileUtils.buildValidFatFilename("foo.bar"));
232        assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
233    }
234
235    public void testTrimFilename() throws Exception {
236        assertEquals("short.txt", FileUtils.trimFilename("short.txt", 16));
237        assertEquals("extrem...eme.txt", FileUtils.trimFilename("extremelylongfilename.txt", 16));
238
239        final String unicode = "a\u03C0\u03C0\u03C0\u03C0z";
240        assertEquals("a\u03C0\u03C0\u03C0\u03C0z", FileUtils.trimFilename(unicode, 10));
241        assertEquals("a\u03C0...\u03C0z", FileUtils.trimFilename(unicode, 9));
242        assertEquals("a...\u03C0z", FileUtils.trimFilename(unicode, 8));
243        assertEquals("a...\u03C0z", FileUtils.trimFilename(unicode, 7));
244        assertEquals("a...z", FileUtils.trimFilename(unicode, 6));
245    }
246
247    public void testBuildUniqueFile_normal() throws Exception {
248        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
249        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
250        assertNameEquals("test.jpeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
251        assertNameEquals("TEst.JPeg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
252        assertNameEquals("test.png.jpg",
253                FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
254        assertNameEquals("test.png.jpg",
255                FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.png"));
256
257        assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test"));
258        assertNameEquals("test.flac", FileUtils.buildUniqueFile(mTarget, "audio/flac", "test.flac"));
259        assertNameEquals("test.flac",
260                FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test"));
261        assertNameEquals("test.flac",
262                FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
263    }
264
265    public void testBuildUniqueFile_unknown() throws Exception {
266        assertNameEquals("test",
267                FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
268        assertNameEquals("test.jpg",
269                FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
270        assertNameEquals(".test",
271                FileUtils.buildUniqueFile(mTarget, "application/octet-stream", ".test"));
272
273        assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test"));
274        assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
275    }
276
277    public void testBuildUniqueFile_dir() throws Exception {
278        assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
279        new File(mTarget, "test").mkdir();
280        assertNameEquals("test (1)",
281                FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
282
283        assertNameEquals("test.jpg",
284                FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
285        new File(mTarget, "test.jpg").mkdir();
286        assertNameEquals("test.jpg (1)",
287                FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
288    }
289
290    public void testBuildUniqueFile_increment() throws Exception {
291        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
292        new File(mTarget, "test.jpg").createNewFile();
293        assertNameEquals("test (1).jpg",
294                FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
295        new File(mTarget, "test (1).jpg").createNewFile();
296        assertNameEquals("test (2).jpg",
297                FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
298    }
299
300    public void testBuildUniqueFile_mimeless() throws Exception {
301        assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
302        new File(mTarget, "test.jpg").createNewFile();
303        assertNameEquals("test (1).jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
304
305        assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, "test"));
306        new File(mTarget, "test").createNewFile();
307        assertNameEquals("test (1)", FileUtils.buildUniqueFile(mTarget, "test"));
308
309        assertNameEquals("test.foo.bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
310        new File(mTarget, "test.foo.bar").createNewFile();
311        assertNameEquals("test.foo (1).bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
312    }
313
314    private static void assertNameEquals(String expected, File actual) {
315        assertEquals(expected, actual.getName());
316    }
317
318    private void touch(String name, long age) throws Exception {
319        final File file = new File(mDir, name);
320        file.createNewFile();
321        file.setLastModified(System.currentTimeMillis() - age);
322    }
323
324    private void stageFile(File file, String data) throws Exception {
325        FileWriter writer = new FileWriter(file);
326        try {
327            writer.write(data, 0, data.length());
328        } finally {
329            writer.close();
330        }
331    }
332
333    private void assertDirContents(String... expected) {
334        final HashSet<String> expectedSet = Sets.newHashSet(expected);
335        String[] actual = mDir.list();
336        if (actual == null) actual = new String[0];
337
338        assertEquals(
339                "Expected " + Arrays.toString(expected) + " but actual " + Arrays.toString(actual),
340                expected.length, actual.length);
341        for (String actualFile : actual) {
342            assertTrue("Unexpected actual file " + actualFile, expectedSet.contains(actualFile));
343        }
344    }
345}
346