FileInputStreamTest.java revision 39b97ad7dd0e91440c67772f84208cd783c98ce0
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
17package libcore.java.io;
18
19import java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.List;
27
28import android.system.ErrnoException;
29import android.system.Os;
30import android.system.OsConstants;
31import android.system.StructStatVfs;
32import junit.framework.TestCase;
33
34import libcore.io.IoUtils;
35import libcore.io.Libcore;
36
37public final class FileInputStreamTest extends TestCase {
38    private static final int TOTAL_SIZE = 1024;
39    private static final int SKIP_SIZE = 100;
40
41    private static class DataFeeder extends Thread {
42        private FileDescriptor mOutFd;
43
44        public DataFeeder(FileDescriptor fd) {
45            mOutFd = fd;
46        }
47
48        @Override
49        public void run() {
50            try {
51                FileOutputStream fos = new FileOutputStream(mOutFd);
52                try {
53                    byte[] buffer = new byte[TOTAL_SIZE];
54                    for (int i = 0; i < buffer.length; ++i) {
55                        buffer[i] = (byte) i;
56                    }
57                    fos.write(buffer);
58                } finally {
59                    IoUtils.closeQuietly(fos);
60                    IoUtils.close(mOutFd);
61                }
62            } catch (IOException e) {
63                throw new RuntimeException(e);
64            }
65        }
66    }
67
68    private void verifyData(FileInputStream is, int start, int count) throws IOException {
69        byte buffer[] = new byte[count];
70        assertEquals(count, is.read(buffer));
71        for (int i = 0; i < count; ++i) {
72            assertEquals((byte) (i + start), buffer[i]);
73        }
74    }
75
76    public void testSkipInPipes() throws Exception {
77        FileDescriptor[] pipe = Libcore.os.pipe2(0);
78        DataFeeder feeder = new DataFeeder(pipe[1]);
79        try {
80            feeder.start();
81            FileInputStream fis = new FileInputStream(pipe[0]);
82            fis.skip(SKIP_SIZE);
83            verifyData(fis, SKIP_SIZE, TOTAL_SIZE - SKIP_SIZE);
84            assertEquals(-1, fis.read());
85            feeder.join(1000);
86            assertFalse(feeder.isAlive());
87        } finally {
88            IoUtils.closeQuietly(pipe[0]);
89        }
90    }
91
92    public void testDirectories() throws Exception {
93        try {
94            new FileInputStream(".");
95            fail();
96        } catch (FileNotFoundException expected) {
97        }
98    }
99
100    private File makeFile() throws Exception {
101        File tmp = File.createTempFile("FileOutputStreamTest", "tmp");
102        FileOutputStream fos = new FileOutputStream(tmp);
103        fos.write(1);
104        fos.write(1);
105        fos.close();
106        return tmp;
107    }
108
109    public void testFileDescriptorOwnership() throws Exception {
110        File tmp = makeFile();
111
112        FileInputStream fis1 = new FileInputStream(tmp);
113        FileInputStream fis2 = new FileInputStream(fis1.getFD());
114
115        // Close the second FileDescriptor and check we can't use it...
116        fis2.close();
117
118        try {
119            fis2.available();
120            fail();
121        } catch (IOException expected) {
122        }
123        try {
124            fis2.read();
125            fail();
126        } catch (IOException expected) {
127        }
128        try {
129            fis2.read(new byte[1], 0, 1);
130            fail();
131        } catch (IOException expected) {
132        }
133        try {
134            fis2.skip(1);
135            fail();
136        } catch (IOException expected) {
137        }
138        // ...but that we can still use the first.
139        assertTrue(fis1.getFD().valid());
140        assertFalse(fis1.read() == -1);
141
142        // Close the first FileDescriptor and check we can't use it...
143        fis1.close();
144        try {
145            fis1.available();
146            fail();
147        } catch (IOException expected) {
148        }
149        try {
150            fis1.read();
151            fail();
152        } catch (IOException expected) {
153        }
154        try {
155            fis1.read(new byte[1], 0, 1);
156            fail();
157        } catch (IOException expected) {
158        }
159        try {
160            fis1.skip(1);
161            fail();
162        } catch (IOException expected) {
163        }
164
165        // FD is no longer owned by any stream, should be invalidated.
166        assertFalse(fis1.getFD().valid());
167    }
168
169    public void testClose() throws Exception {
170        File tmp = makeFile();
171        FileInputStream fis = new FileInputStream(tmp);
172
173        // Closing an already-closed stream is a no-op...
174        fis.close();
175        fis.close();
176
177        // But any explicit activity is an error.
178        try {
179            fis.available();
180            fail();
181        } catch (IOException expected) {
182        }
183        try {
184            fis.read();
185            fail();
186        } catch (IOException expected) {
187        }
188        try {
189            fis.read(new byte[1], 0, 1);
190            fail();
191        } catch (IOException expected) {
192        }
193        try {
194            fis.skip(1);
195            fail();
196        } catch (IOException expected) {
197        }
198        // Including 0-byte skips...
199        try {
200            fis.skip(0);
201            fail();
202        } catch (IOException expected) {
203        }
204        // ...but not 0-byte reads...
205        fis.read(new byte[0], 0, 0);
206    }
207
208    // http://b/26117827
209    public void testReadProcVersion() throws IOException {
210        File file = new File("/proc/version");
211        FileInputStream input = new FileInputStream(file);
212        assertTrue(input.available() == 0);
213    }
214
215    // http://b/25695227
216    public void testFdLeakWhenOpeningDirectory() throws Exception {
217        File phile = IoUtils.createTemporaryDirectory("test_bug_25695227");
218
219        try {
220            new FileInputStream(phile);
221            fail();
222        } catch (FileNotFoundException expected) {
223        }
224
225        assertTrue(getOpenFdsForPrefix("test_bug_25695227").isEmpty());
226    }
227
228    // http://b/28192631
229    public void testSkipOnLargeFiles() throws Exception {
230        File largeFile = File.createTempFile("FileInputStreamTest_testSkipOnLargeFiles", "");
231
232        // Required space is 3.1 GB: 3GB for file plus 100M headroom.
233        final long requiredFreeSpaceBytes = 3172L * 1024 * 1024;
234
235        // If system doesn't have enough space free for this test, skip it.
236        final StructStatVfs statVfs = Os.statvfs(largeFile.getPath());
237        final long freeSpaceAvailableBytes = statVfs.f_bsize * statVfs.f_bavail;
238        if (freeSpaceAvailableBytes < requiredFreeSpaceBytes) {
239            return;
240        }
241
242        try {
243            FileOutputStream fos = new FileOutputStream(largeFile);
244            try {
245                byte[] buffer = new byte[1024 * 1024]; // 1 MB
246                for (int i = 0; i < 3 * 1024; i++) { // 3 GB
247                    fos.write(buffer);
248                }
249            } finally {
250                fos.close();
251            }
252
253            FileInputStream fis = new FileInputStream(largeFile);
254            long lastByte = 3 * 1024 * 1024 * 1024L - 1;
255            assertEquals(0, Libcore.os.lseek(fis.getFD(), 0, OsConstants.SEEK_CUR));
256            assertEquals(lastByte, fis.skip(lastByte));
257        } finally {
258            // Proactively cleanup - it's a pretty large file.
259            assertTrue(largeFile.delete());
260        }
261    }
262
263    private static List<Integer> getOpenFdsForPrefix(String path) throws Exception {
264        File[] fds = new File("/proc/self/fd").listFiles();
265        List<Integer> list = new ArrayList<>();
266        for (File fd : fds) {
267            try {
268                File fdPath = new File(android.system.Os.readlink(fd.getAbsolutePath()));
269                if (fdPath.getName().startsWith(path)) {
270                    list.add(Integer.valueOf(fd.getName()));
271                }
272            } catch (ErrnoException e) {
273                if (e.errno != OsConstants.ENOENT) {
274                    throw e.rethrowAsIOException();
275                }
276            }
277        }
278
279        return list;
280    }
281}
282