1/*
2 * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 4313887
26 * @summary Unit test for java.nio.file.Files.newByteChannel
27 * @library ..
28 * @modules jdk.unsupported
29 */
30
31import java.nio.ByteBuffer;
32import java.nio.file.*;
33import static java.nio.file.StandardOpenOption.*;
34import static com.sun.nio.file.ExtendedOpenOption.*;
35import java.nio.file.attribute.FileAttribute;
36import java.nio.channels.*;
37import java.io.IOException;
38import java.util.*;
39
40public class SBC {
41
42    static boolean supportsLinks;
43
44    public static void main(String[] args) throws Exception {
45        Path dir = TestUtil.createTemporaryDirectory();
46        try {
47            supportsLinks = TestUtil.supportsLinks(dir);
48
49            // open options
50            createTests(dir);
51            appendTests(dir);
52            truncateExistingTests(dir);
53            noFollowLinksTests(dir);
54
55            // SeekableByteChannel methods
56            sizeTruncatePositionTests(dir);
57
58            // platform specific
59            if (System.getProperty("os.name").startsWith("Windows"))
60                dosSharingOptionTests(dir);
61
62            // misc. tests
63            badCombinations(dir);
64            unsupportedOptions(dir);
65            nullTests(dir);
66
67        } finally {
68            TestUtil.removeAll(dir);
69        }
70    }
71
72    // test CREATE and CREATE_NEW options
73    static void createTests(Path dir) throws Exception {
74        Path file = dir.resolve("foo");
75
76        // CREATE
77        try {
78            // create file (no existing file)
79            Files.newByteChannel(file, CREATE, WRITE).close();
80            if (Files.notExists(file))
81                throw new RuntimeException("File not created");
82
83            // create file (existing file)
84            Files.newByteChannel(file, CREATE, WRITE).close();
85
86            // create file where existing file is a sym link
87            if (supportsLinks) {
88                Path link = Files.createSymbolicLink(dir.resolve("link"), file);
89                try {
90                    // file already exists
91                    Files.newByteChannel(link, CREATE, WRITE).close();
92
93                    // file does not exist
94                    Files.delete(file);
95                    Files.newByteChannel(link, CREATE, WRITE).close();
96                    if (Files.notExists(file))
97                        throw new RuntimeException("File not created");
98
99                } finally {
100                    TestUtil.deleteUnchecked(link);
101                }
102            }
103
104        } finally {
105            TestUtil.deleteUnchecked(file);
106        }
107
108        // CREATE_NEW
109        try {
110            // create file
111            Files.newByteChannel(file, CREATE_NEW, WRITE).close();
112            if (Files.notExists(file))
113                throw new RuntimeException("File not created");
114
115            // create should fail
116            try {
117                SeekableByteChannel sbc =
118                    Files.newByteChannel(file, CREATE_NEW, WRITE);
119                sbc.close();
120                throw new RuntimeException("FileAlreadyExistsException not thrown");
121            } catch (FileAlreadyExistsException x) { }
122
123            // create should fail
124            if (supportsLinks) {
125                Path link = dir.resolve("link");
126                Path target = dir.resolve("thisDoesNotExist");
127                Files.createSymbolicLink(link, target);
128                try {
129
130                    try {
131                        SeekableByteChannel sbc =
132                            Files.newByteChannel(file, CREATE_NEW, WRITE);
133                        sbc.close();
134                        throw new RuntimeException("FileAlreadyExistsException not thrown");
135                    } catch (FileAlreadyExistsException x) { }
136
137                } finally {
138                    TestUtil.deleteUnchecked(link);
139                }
140            }
141
142
143        } finally {
144            TestUtil.deleteUnchecked(file);
145        }
146
147        // CREATE_NEW + SPARSE
148        try {
149            try (SeekableByteChannel sbc = Files.newByteChannel(file, CREATE_NEW, WRITE, SPARSE)) {
150                final long hole = 2L * 1024L * 1024L * 1024L;
151                sbc.position(hole);
152                write(sbc, "hello");
153                long size = sbc.size();
154                if (size != (hole + 5))
155                    throw new RuntimeException("Unexpected size");
156            }
157        } finally {
158            TestUtil.deleteUnchecked(file);
159        }
160    }
161
162    // test APPEND option
163    static void appendTests(Path dir) throws Exception {
164        Path file = dir.resolve("foo");
165        try {
166            // "hello there" should be written to file
167            try (SeekableByteChannel sbc = Files.newByteChannel(file, CREATE_NEW, WRITE, APPEND)) {
168                write(sbc, "hello ");
169                sbc.position(0L);
170                write(sbc, "there");
171            }
172
173            // check file
174            try (Scanner s = new Scanner(file)) {
175                String line = s.nextLine();
176                if (!line.equals("hello there"))
177                    throw new RuntimeException("Unexpected file contents");
178            }
179
180            // check that read is not allowed
181            try (SeekableByteChannel sbc = Files.newByteChannel(file, APPEND)) {
182                sbc.read(ByteBuffer.allocate(100));
183            } catch (NonReadableChannelException x) {
184            }
185        } finally {
186            // clean-up
187            TestUtil.deleteUnchecked(file);
188        }
189    }
190
191    // test TRUNCATE_EXISTING option
192    static void truncateExistingTests(Path dir) throws Exception {
193        Path file = dir.resolve("foo");
194        try {
195            try (SeekableByteChannel sbc = Files.newByteChannel(file, CREATE_NEW, WRITE)) {
196                write(sbc, "Have a nice day!");
197            }
198
199            // re-open with truncate option
200            // write short message and check
201            try (SeekableByteChannel sbc = Files.newByteChannel(file, WRITE, TRUNCATE_EXISTING)) {
202                write(sbc, "Hello there!");
203            }
204            try (Scanner s = new Scanner(file)) {
205                String line = s.nextLine();
206                if (!line.equals("Hello there!"))
207                    throw new RuntimeException("Unexpected file contents");
208            }
209
210            // re-open with create + truncate option
211            // check file is of size 0L
212            try (SeekableByteChannel sbc = Files.newByteChannel(file, WRITE, CREATE, TRUNCATE_EXISTING)) {
213                long size = ((FileChannel)sbc).size();
214                if (size != 0L)
215                    throw new RuntimeException("File not truncated");
216            }
217
218        } finally {
219            // clean-up
220            TestUtil.deleteUnchecked(file);
221        }
222
223    }
224
225    // test NOFOLLOW_LINKS option
226    static void noFollowLinksTests(Path dir) throws Exception {
227        if (!supportsLinks)
228            return;
229        Path file = Files.createFile(dir.resolve("foo"));
230        try {
231            // ln -s foo link
232            Path link = dir.resolve("link");
233            Files.createSymbolicLink(link, file);
234
235            // open with NOFOLLOW_LINKS option
236            try {
237                Files.newByteChannel(link, READ, LinkOption.NOFOLLOW_LINKS);
238                throw new RuntimeException();
239            } catch (IOException | UnsupportedOperationException x) {
240            } finally {
241                TestUtil.deleteUnchecked(link);
242            }
243
244        } finally {
245            // clean-up
246            TestUtil.deleteUnchecked(file);
247        }
248    }
249
250    // test size/truncate/position methods
251    static void sizeTruncatePositionTests(Path dir) throws Exception {
252        Path file = dir.resolve("foo");
253        try {
254            try (SeekableByteChannel sbc = Files.newByteChannel(file, CREATE_NEW, READ, WRITE)) {
255                if (sbc.size() != 0L)
256                    throw new RuntimeException("Unexpected size");
257
258                // check size
259                write(sbc, "hello");
260                if (sbc.size() != 5L)
261                    throw new RuntimeException("Unexpected size");
262
263                // truncate (size and position should change)
264                sbc.truncate(4L);
265                if (sbc.size() != 4L)
266                    throw new RuntimeException("Unexpected size");
267                if (sbc.position() != 4L)
268                    throw new RuntimeException("Unexpected position");
269
270                // truncate (position should not change)
271                sbc.position(2L).truncate(3L);
272                if (sbc.size() != 3L)
273                    throw new RuntimeException("Unexpected size");
274                if (sbc.position() != 2L)
275                    throw new RuntimeException("Unexpected position");
276            }
277        } finally {
278            TestUtil.deleteUnchecked(file);
279        }
280    }
281
282    // Windows specific options for the use by applications that really want
283    // to use legacy DOS sharing options
284    static void dosSharingOptionTests(Path dir) throws Exception {
285        Path file = Files.createFile(dir.resolve("foo"));
286        try {
287            // no sharing
288            try (SeekableByteChannel ch = Files.newByteChannel(file, READ, NOSHARE_READ,
289                                                               NOSHARE_WRITE, NOSHARE_DELETE))
290            {
291                try {
292                    Files.newByteChannel(file, READ);
293                    throw new RuntimeException("Sharing violation expected");
294                } catch (IOException ignore) { }
295                try {
296                    Files.newByteChannel(file, WRITE);
297                    throw new RuntimeException("Sharing violation expected");
298                } catch (IOException ignore) { }
299                try {
300                    Files.delete(file);
301                    throw new RuntimeException("Sharing violation expected");
302                } catch (IOException ignore) { }
303            }
304
305            // read allowed
306            try (SeekableByteChannel ch = Files.newByteChannel(file, READ, NOSHARE_WRITE, NOSHARE_DELETE)) {
307                Files.newByteChannel(file, READ).close();
308                try {
309                    Files.newByteChannel(file, WRITE);
310                    throw new RuntimeException("Sharing violation expected");
311                } catch (IOException ignore) { }
312                try {
313                    Files.delete(file);
314                    throw new RuntimeException("Sharing violation expected");
315                } catch (IOException ignore) { }
316            }
317
318            // write allowed
319            try (SeekableByteChannel ch = Files.newByteChannel(file, READ, NOSHARE_READ, NOSHARE_DELETE)) {
320                try {
321                    Files.newByteChannel(file, READ);
322                    throw new RuntimeException("Sharing violation expected");
323                } catch (IOException ignore) { }
324                Files.newByteChannel(file, WRITE).close();
325                try {
326                    Files.delete(file);
327                    throw new RuntimeException("Sharing violation expected");
328                } catch (IOException ignore) { }
329            }
330
331            // delete allowed
332            try (SeekableByteChannel ch = Files.newByteChannel(file, READ, NOSHARE_READ, NOSHARE_WRITE)) {
333                try {
334                    Files.newByteChannel(file, READ);
335                    throw new RuntimeException("Sharing violation expected");
336                } catch (IOException ignore) { }
337                try {
338                    Files.newByteChannel(file, WRITE);
339                    throw new RuntimeException("Sharing violation expected");
340                } catch (IOException ignore) { }
341                Files.delete(file);
342            }
343
344        } finally {
345            TestUtil.deleteUnchecked(file);
346        }
347    }
348
349    // invalid combinations of options
350    static void badCombinations(Path dir) throws Exception {
351        Path file = dir.resolve("bad");
352
353        try {
354            Files.newByteChannel(file, READ, APPEND);
355            throw new RuntimeException("IllegalArgumentException expected");
356        } catch (IllegalArgumentException x) { }
357
358        try {
359            Files.newByteChannel(file, WRITE, APPEND, TRUNCATE_EXISTING);
360            throw new RuntimeException("IllegalArgumentException expected");
361        } catch (IllegalArgumentException x) { }
362    }
363
364    // unsupported operations
365    static void unsupportedOptions(Path dir) throws Exception {
366        Path file = dir.resolve("bad");
367
368        OpenOption badOption = new OpenOption() { };
369        try {
370            Files.newByteChannel(file, badOption);
371            throw new RuntimeException("UnsupportedOperationException expected");
372        } catch (UnsupportedOperationException e) { }
373        try {
374            Files.newByteChannel(file, READ, WRITE, badOption);
375            throw new RuntimeException("UnsupportedOperationException expected");
376        } catch (UnsupportedOperationException e) { }
377    }
378
379    // null handling
380    static void nullTests(Path dir) throws Exception {
381        Path file = dir.resolve("foo");
382
383        try {
384            OpenOption[] opts = { READ, null };
385            Files.newByteChannel((Path)null, opts);
386            throw new RuntimeException("NullPointerException expected");
387        } catch (NullPointerException x) { }
388
389        try {
390            Files.newByteChannel(file, (OpenOption[])null);
391            throw new RuntimeException("NullPointerException expected");
392        } catch (NullPointerException x) { }
393
394        try {
395            OpenOption[] opts = { READ, null };
396            Files.newByteChannel(file, opts);
397            throw new RuntimeException("NullPointerException expected");
398        } catch (NullPointerException x) { }
399
400        try {
401            Files.newByteChannel(file, (Set<OpenOption>)null);
402            throw new RuntimeException("NullPointerException expected");
403        } catch (NullPointerException x) { }
404
405        try {
406            Set<OpenOption> opts = new HashSet<>();
407            opts.add(READ);
408            opts.add(null);
409            Files.newByteChannel(file, opts);
410            throw new RuntimeException("NullPointerException expected");
411        } catch (NullPointerException x) { }
412
413        try {
414            EnumSet<StandardOpenOption> opts = EnumSet.of(READ);
415            Files.newByteChannel(file, opts, (FileAttribute[])null);
416            throw new RuntimeException("NullPointerException expected");
417        } catch (NullPointerException x) { }
418
419        try {
420            EnumSet<StandardOpenOption> opts = EnumSet.of(READ);
421            FileAttribute[] attrs = { null };
422            Files.newByteChannel(file, opts, attrs);
423            throw new RuntimeException("NullPointerException expected");
424        } catch (NullPointerException x) { }
425    }
426
427    static void write(WritableByteChannel wbc, String msg) throws IOException {
428        ByteBuffer buf = ByteBuffer.wrap(msg.getBytes());
429        while (buf.hasRemaining())
430            wbc.write(buf);
431    }
432}
433