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.test.AndroidTestCase;
25import android.test.suitebuilder.annotation.MediumTest;
26
27import com.google.android.collect.Sets;
28
29import java.io.ByteArrayInputStream;
30import java.io.File;
31import java.io.FileOutputStream;
32import java.io.FileWriter;
33import java.util.Arrays;
34import java.util.HashSet;
35
36import libcore.io.IoUtils;
37
38@MediumTest
39public class FileUtilsTest extends AndroidTestCase {
40    private static final String TEST_DATA =
41            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
42
43    private File mDir;
44    private File mTestFile;
45    private File mCopyFile;
46
47    @Override
48    protected void setUp() throws Exception {
49        super.setUp();
50        mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
51        mTestFile = new File(mDir, "test.file");
52        mCopyFile = new File(mDir, "copy.file");
53    }
54
55    @Override
56    protected void tearDown() throws Exception {
57        IoUtils.deleteContents(mDir);
58    }
59
60    // TODO: test setPermissions(), getPermissions()
61
62    public void testCopyFile() throws Exception {
63        stageFile(mTestFile, TEST_DATA);
64        assertFalse(mCopyFile.exists());
65        FileUtils.copyFile(mTestFile, mCopyFile);
66        assertTrue(mCopyFile.exists());
67        assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
68    }
69
70    public void testCopyToFile() throws Exception {
71        final String s = "Foo Bar";
72        assertFalse(mCopyFile.exists());
73        FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile);
74        assertTrue(mCopyFile.exists());
75        assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
76    }
77
78    public void testIsFilenameSafe() throws Exception {
79        assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
80        assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
81        assertFalse(FileUtils.isFilenameSafe(new File("foo*bar")));
82        assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
83    }
84
85    public void testReadTextFile() throws Exception {
86        stageFile(mTestFile, TEST_DATA);
87
88        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
89
90        assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null));
91        assertEquals("ABCDE<>", FileUtils.readTextFile(mTestFile, 5, "<>"));
92        assertEquals(TEST_DATA.substring(0, 51) + "<>",
93                FileUtils.readTextFile(mTestFile, 51, "<>"));
94        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 52, "<>"));
95        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 100, "<>"));
96
97        assertEquals("vwxyz", FileUtils.readTextFile(mTestFile, -5, null));
98        assertEquals("<>vwxyz", FileUtils.readTextFile(mTestFile, -5, "<>"));
99        assertEquals("<>" + TEST_DATA.substring(1, 52),
100                FileUtils.readTextFile(mTestFile, -51, "<>"));
101        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -52, "<>"));
102        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
103    }
104
105    public void testReadTextFileWithZeroLengthFile() throws Exception {
106        stageFile(mTestFile, TEST_DATA);
107        new FileOutputStream(mTestFile).close();  // Zero out the file
108        assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
109        assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
110        assertEquals("", FileUtils.readTextFile(mTestFile, 10, "<>"));
111        assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>"));
112        assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
113    }
114
115    public void testDeleteOlderEmptyDir() throws Exception {
116        FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
117        assertDirContents();
118    }
119
120    public void testDeleteOlderTypical() throws Exception {
121        touch("file1", HOUR_IN_MILLIS);
122        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
123        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
124        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
125        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
126        FileUtils.deleteOlderFiles(mDir, 3, DAY_IN_MILLIS);
127        assertDirContents("file1", "file2", "file3");
128    }
129
130    public void testDeleteOlderInFuture() throws Exception {
131        touch("file1", -HOUR_IN_MILLIS);
132        touch("file2", HOUR_IN_MILLIS);
133        touch("file3", WEEK_IN_MILLIS);
134        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
135        assertDirContents("file1", "file2");
136
137        touch("file1", -HOUR_IN_MILLIS);
138        touch("file2", HOUR_IN_MILLIS);
139        touch("file3", WEEK_IN_MILLIS);
140        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
141        assertDirContents("file1", "file2");
142    }
143
144    public void testDeleteOlderOnlyAge() 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        FileUtils.deleteOlderFiles(mDir, 0, DAY_IN_MILLIS);
151        assertDirContents("file1");
152    }
153
154    public void testDeleteOlderOnlyCount() throws Exception {
155        touch("file1", HOUR_IN_MILLIS);
156        touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
157        touch("file3", 2 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
158        touch("file4", 3 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
159        touch("file5", 4 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
160        FileUtils.deleteOlderFiles(mDir, 2, 0);
161        assertDirContents("file1", "file2");
162    }
163
164    private void touch(String name, long age) throws Exception {
165        final File file = new File(mDir, name);
166        file.createNewFile();
167        file.setLastModified(System.currentTimeMillis() - age);
168    }
169
170    private void stageFile(File file, String data) throws Exception {
171        FileWriter writer = new FileWriter(file);
172        try {
173            writer.write(data, 0, data.length());
174        } finally {
175            writer.close();
176        }
177    }
178
179    private void assertDirContents(String... expected) {
180        final HashSet<String> expectedSet = Sets.newHashSet(expected);
181        String[] actual = mDir.list();
182        if (actual == null) actual = new String[0];
183
184        assertEquals(
185                "Expected " + Arrays.toString(expected) + " but actual " + Arrays.toString(actual),
186                expected.length, actual.length);
187        for (String actualFile : actual) {
188            assertTrue("Unexpected actual file " + actualFile, expectedSet.contains(actualFile));
189        }
190    }
191}
192