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.nio.channels;
18
19import java.io.File;
20import java.io.FileDescriptor;
21import java.io.FileInputStream;
22import java.io.FileOutputStream;
23import java.io.IOException;
24import java.io.RandomAccessFile;
25import java.nio.ByteBuffer;
26import java.nio.channels.FileChannel;
27import libcore.io.IoUtils;
28
29public class FileChannelTest extends junit.framework.TestCase {
30    public void testReadOnlyByteArrays() throws Exception {
31        ByteBuffer readOnly = ByteBuffer.allocate(1).asReadOnlyBuffer();
32        File tmp = File.createTempFile("FileChannelTest", "tmp");
33
34        // You can't read into a read-only buffer...
35        FileChannel fc = new FileInputStream(tmp).getChannel();
36        try {
37            fc.read(readOnly);
38            fail();
39        } catch (IllegalArgumentException expected) {
40        }
41        try {
42            fc.read(new ByteBuffer[] { readOnly });
43            fail();
44        } catch (IllegalArgumentException expected) {
45        }
46        try {
47            fc.read(new ByteBuffer[] { readOnly }, 0, 1);
48            fail();
49        } catch (IllegalArgumentException expected) {
50        }
51        try {
52            fc.read(readOnly, 0L);
53            fail();
54        } catch (IllegalArgumentException expected) {
55        }
56        fc.close();
57
58
59        // But you can write from a read-only buffer...
60        fc = new FileOutputStream(tmp).getChannel();
61        fc.write(readOnly);
62        fc.write(new ByteBuffer[] { readOnly });
63        fc.write(new ByteBuffer[] { readOnly }, 0, 1);
64        fc.write(readOnly, 0L);
65        fc.close();
66    }
67
68    public void test_readv() throws Exception {
69        File tmp = File.createTempFile("FileChannelTest", "tmp");
70        FileChannel fc = new FileOutputStream(tmp).getChannel();
71        fc.write(ByteBuffer.wrap("abcdABCD".getBytes("US-ASCII")));
72        fc.close();
73        // Check that both direct and non-direct buffers work.
74        fc = new FileInputStream(tmp).getChannel();
75        ByteBuffer[] buffers = new ByteBuffer[] { ByteBuffer.allocateDirect(4), ByteBuffer.allocate(4) };
76        assertEquals(8, fc.read(buffers));
77        fc.close();
78        assertEquals(8, buffers[0].limit() + buffers[1].limit());
79        byte[] bytes = new byte[4];
80        buffers[0].flip();
81        buffers[0].get(bytes);
82        assertEquals("abcd", new String(bytes, "US-ASCII"));
83        buffers[1].flip();
84        buffers[1].get(bytes);
85        assertEquals("ABCD", new String(bytes, "US-ASCII"));
86    }
87
88    public void test_writev() throws Exception {
89        File tmp = File.createTempFile("FileChannelTest", "tmp");
90        FileChannel fc = new FileOutputStream(tmp).getChannel();
91        // Check that both direct and non-direct buffers work.
92        ByteBuffer[] buffers = new ByteBuffer[] { ByteBuffer.allocateDirect(4), ByteBuffer.allocate(4) };
93        buffers[0].put("abcd".getBytes("US-ASCII")).flip();
94        buffers[1].put("ABCD".getBytes("US-ASCII")).flip();
95        assertEquals(8, fc.write(buffers));
96        fc.close();
97        assertEquals(8, tmp.length());
98        assertEquals("abcdABCD", new String(IoUtils.readFileAsString(tmp.getPath())));
99    }
100
101    public void test_append() throws Exception {
102        File tmp = File.createTempFile("FileChannelTest", "tmp");
103        FileOutputStream fos = new FileOutputStream(tmp, true);
104        FileChannel fc = fos.getChannel();
105
106        fc.write(ByteBuffer.wrap("hello".getBytes("US-ASCII")));
107        fc.position(0);
108        // The RI reports end of file
109        assertEquals(5, fc.position());
110        // ...but writes to the end of the file.
111        fc.write(ByteBuffer.wrap(" world".getBytes("US-ASCII")));
112        fos.close();
113
114        assertEquals("hello world", new String(IoUtils.readFileAsString(tmp.getPath())));
115    }
116
117    public void test_position_writeAddsPadding() throws Exception {
118        byte[] initialBytes = "12345".getBytes("US-ASCII");
119        int initialFileSize = initialBytes.length; // 5
120        FileChannel fc = createFileContainingBytes(initialBytes);
121
122        int positionBeyondSize = 10;
123        fc.position(positionBeyondSize);
124        assertEquals(positionBeyondSize, fc.position());
125        assertEquals(initialFileSize, fc.size());
126
127        byte[] newBytes = "6789A".getBytes("US-ASCII");
128        fc.write(ByteBuffer.wrap(newBytes));
129
130        int expectedNewLength = positionBeyondSize + newBytes.length;
131        assertEquals(expectedNewLength, fc.position());
132        assertEquals(expectedNewLength, fc.size());
133
134        fc.close();
135    }
136
137    public void test_truncate_greaterThanSizeWithPositionChange() throws Exception {
138        byte[] initialBytes = "12345".getBytes("US-ASCII");
139        int initialFileSize = initialBytes.length; // 5
140        FileChannel fc = createFileContainingBytes(initialBytes);
141
142        int initialPosition = 40;
143        fc.position(initialPosition); // Should not affect the file size
144        assertEquals(initialPosition, fc.position());
145        assertEquals(initialFileSize, fc.size());
146
147        // truncateArg < position, truncateArg > initialFileSize: Should not affect the file size
148        // and should move the position.
149        int truncateArg = 10;
150        fc.truncate(truncateArg);  // Should not affect the file size, but should move the position.
151        assertEquals(initialFileSize, fc.size());
152        // The RI does not behave properly here, according to the docs for truncate(), position()
153        // should now be the same as truncateArg.
154        assertEquals(truncateArg, fc.position());
155
156        fc.close();
157    }
158
159    public void test_truncate_greaterThanSizeWithoutPositionChange() throws Exception {
160        byte[] initialBytes = "123456789A".getBytes("US-ASCII");
161        int initialFileSize = initialBytes.length; // 10
162        FileChannel fc = createFileContainingBytes(initialBytes);
163
164        int initialPosition = 5;
165        fc.position(initialPosition);
166        assertEquals(initialPosition, fc.position());
167        assertEquals(initialFileSize, fc.size());
168
169        // truncateArg > position, truncateArg > initialFileSize: Should not affect the file size
170        // and should not move the position.
171        int truncateArg = 15;
172        fc.truncate(truncateArg);
173        assertEquals(initialFileSize, fc.size());
174        assertEquals(initialPosition, fc.position());
175
176        fc.close();
177    }
178
179    public void test_truncate_lessThanSizeWithPositionChange() throws Exception {
180        byte[] initialBytes = "123456789A".getBytes("US-ASCII");
181        int initialFileSize = initialBytes.length; // 10
182        FileChannel fc = createFileContainingBytes(initialBytes);
183
184        int initialPosition = initialFileSize;
185        fc.position(initialPosition);
186        assertEquals(initialPosition, fc.position());
187        assertEquals(initialFileSize, fc.size());
188
189        int truncateArg = 5;
190        // truncateArg < initialPosition, truncateArg < initialFileSize: Should affect the file size
191        // and should move the position.
192        fc.truncate(truncateArg);
193        assertEquals(truncateArg, fc.size());
194        assertEquals(truncateArg, fc.position());
195
196        fc.close();
197    }
198
199    public void test_truncate_lessThanSizeWithoutPositionChange() throws Exception {
200        byte[] initialBytes = "123456789A".getBytes("US-ASCII");
201        int initialFileSize = initialBytes.length; // 10
202        FileChannel fc = createFileContainingBytes(initialBytes);
203
204        int initialPosition = 4;
205        fc.position(initialPosition);
206        assertEquals(initialPosition, fc.position());
207        assertEquals(initialFileSize, fc.size());
208
209        int truncateArg = 5;
210        // truncateArg > initialPosition, truncateArg < initialFileSize: Should affect the file size
211        // and should not move the position.
212        fc.truncate(truncateArg);
213        assertEquals(truncateArg, fc.size());
214        assertEquals(initialPosition, fc.position());
215
216        fc.close();
217    }
218
219    // b/27351214
220    public void test_close_fromFileDescriptor() throws Exception {
221        // Create a valid FileDescriptor
222        File tmp = File.createTempFile("FileChannelTest", "tmp");
223        FileOutputStream fos = new FileOutputStream(tmp);
224        FileDescriptor fd = fos.getFD();
225        assertTrue(fd.valid());
226
227        // Create FileOutputStream from FileDescriptor
228        FileOutputStream fosFromFd = new FileOutputStream(fd);
229        // Create FileChannel from FileOutputStream created from FileDescriptor
230        FileChannel fc = fosFromFd.getChannel();
231
232        // Invalidate FileDescriptor
233        fos.close();
234        assertFalse(fd.valid());
235
236        // Close FileOutputStream and therefore close the FileChannel.
237        // Without the fix for b/27351214 this will throw an exception
238        // due to channel preClosing the file descriptor after it's
239        // closed.
240        fosFromFd.close();
241    }
242
243
244    private static FileChannel createFileContainingBytes(byte[] bytes) throws IOException {
245        File tmp = File.createTempFile("FileChannelTest", "tmp");
246        FileOutputStream fos = new FileOutputStream(tmp, true);
247        FileChannel fc = fos.getChannel();
248        fc.write(ByteBuffer.wrap(bytes));
249        fc.close();
250
251        assertEquals(bytes.length, tmp.length());
252
253        fc = new RandomAccessFile(tmp, "rw").getChannel();
254        assertEquals(bytes.length, fc.size());
255
256        return fc;
257    }
258}
259