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