test/jdk/java/nio/channels/FileChannel/Transfers.java
changeset 47216 71c04702a3d5
parent 44857 9b65ef2c7ae9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/FileChannel/Transfers.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @summary Comprehensive test for FileChannel.transfer{From,To}
+ * @bug 4708120
+ * @author Mark Reinhold
+ * @run main/timeout=300 Transfers
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+
+
+public class Transfers {
+
+    static PrintStream out = System.out;
+
+    private static class Failure
+        extends RuntimeException
+    {
+
+        Failure(Exception x) {
+            super(x);
+        }
+
+        Failure(String s) {
+            super(s);
+        }
+
+    }
+
+
+    // -- Writing and reading random bytes --
+
+    private static void writeBytes(byte[] ba, FileChannel fc,
+                                   int off, int len)
+        throws IOException
+    {
+        fc.position(off);
+        if (fc.write(ByteBuffer.wrap(ba, 0, len)) != len)
+            throw new IOException("Incomplete write");
+        fc.position(0);
+    }
+
+    private static void writeRandomBytes(long seed,
+                                         FileChannel fc, int off, int len)
+        throws IOException
+    {
+        Random r = new Random(seed);
+        byte[] ba = new byte[len];
+        r.nextBytes(ba);
+        writeBytes(ba, fc, off, len);
+    }
+
+    private static void writeZeroBytes(FileChannel fc, int off, int len)
+        throws IOException
+    {
+        byte[] ba = new byte[len];
+        writeBytes(ba, fc, off, len);
+    }
+
+    private static void checkBytes(FileChannel fc, int off, int len,
+                                   byte[] bytes)
+        throws IOException
+    {
+        ByteBuffer bb = ByteBuffer.allocate(len);
+        fc.position(off);
+        if (fc.read(bb) != len)
+            throw new IOException("Incomplete read");
+        bb.flip();
+        ByteBuffer bab = ByteBuffer.wrap(bytes, 0, len);
+        if (!bb.equals(bab))
+            throw new Failure("Wrong data written");
+    }
+
+    private static void checkRandomBytes(FileChannel fc, int off, int len,
+                                         long seed)
+        throws IOException
+    {
+        byte[] ba = new byte[len];
+        Random r = new Random(seed);
+        r.nextBytes(ba);
+        checkBytes(fc, off, len, ba);
+    }
+
+    private static void checkZeroBytes(FileChannel fc, int off, int len)
+        throws IOException
+    {
+        byte[] ba = new byte[len];
+        checkBytes(fc, off, len, ba);
+    }
+
+    // For debugging
+    //
+    private static void dump(FileChannel fc)
+        throws IOException
+    {
+        int sz = (int)fc.size();
+        ByteBuffer bb = ByteBuffer.allocate(sz);
+        fc.position(0);
+        if (fc.read(bb) != sz)
+            throw new IOException("Incomplete read");
+        bb.flip();
+        byte prev = -1;
+        int r = 0;                      // Repeats
+        int n = 0;
+        while (bb.hasRemaining() && (n < 32)) {
+            byte b = bb.get();
+            if (b == prev) {
+                r++;
+                continue;
+            }
+            if (r > 0) {
+                int c = prev & 0xff;
+                if (c < 0x10)
+                    out.print('0');
+                out.print(Integer.toHexString(c));
+                if (r > 1) {
+                    out.print("[");
+                    out.print(r);
+                    out.print("]");
+                }
+                n++;
+            }
+            prev = b;
+            r = 1;
+        }
+        if (r > 0) {
+            int c = prev & 0xff;
+            if (c < 0x10)
+                out.print('0');
+            out.print(Integer.toHexString(c));
+            if (r > 1) {
+                out.print("[");
+                out.print(r);
+                out.print("]");
+            }
+            n++;
+        }
+        if (bb.hasRemaining())
+            out.print("...");
+        out.println();
+    }
+
+
+
+    static File sourceFile;
+    static File targetFile;
+
+    // -- Self-verifying sources and targets --
+
+    static abstract class Source {
+
+        protected final int size;
+        protected final long seed;
+        private final String name;
+
+        Source(int size, long seed, String name) {
+            this.size = size;
+            this.seed = seed;
+            this.name = name;
+        }
+
+        String name() {
+            return name;
+        }
+
+        abstract ReadableByteChannel channel();
+
+        abstract void verify() throws IOException;
+
+    }
+
+    static class FileSource
+        extends Source
+    {
+        private final File fn;
+        private final RandomAccessFile raf;
+        private final FileChannel fc;
+
+        FileSource(int size, long seed) throws IOException {
+            super(size, seed, "FileChannel");
+            fn = sourceFile;
+            raf = new RandomAccessFile(fn, "rw");
+            fc = raf.getChannel();
+            fc.position(0);
+            writeRandomBytes(seed, fc, 0, size);
+        }
+
+        ReadableByteChannel channel() {
+            return fc;
+        }
+
+        void verify() throws IOException {
+            if (fc.position() != size)
+                throw new Failure("Wrong position: "
+                                  + fc.position() + " (expected " + size +
+                                  ")");
+            checkRandomBytes(fc, 0, size, seed);
+            fc.close();
+            raf.close();                // Bug in 1.4.0
+        }
+
+    }
+
+    static class UserSource
+        extends Source
+    {
+        private ReadableByteChannel ch;
+        private final ByteBuffer src;
+
+        UserSource(int size, long seed) {
+            super(size, seed, "UserChannel");
+
+            final byte[] bytes = new byte[size + 1];
+            Random r = new Random(seed);
+            r.nextBytes(bytes);
+            src = ByteBuffer.wrap(bytes);
+
+            ch = new ReadableByteChannel() {
+                    public int read(ByteBuffer dst) {
+                        if (!src.hasRemaining())
+                            return -1;
+                        int nr = Math.min(src.remaining(), dst.remaining());
+                        ByteBuffer s = src.duplicate();
+                        s.limit(s.position() + nr);
+                        dst.put(s);
+                        src.position(src.position() + nr);
+                        return nr;
+                    }
+                    public boolean isOpen() {
+                        return true;
+                    }
+                    public void close() { }
+                };
+        }
+
+        ReadableByteChannel channel() {
+            return ch;
+        }
+
+        void verify() {
+            if (src.remaining() != 1)
+                throw new Failure("Source has " + src.remaining()
+                                  + " bytes remaining (expected 1)");
+        }
+
+    }
+
+    static abstract class Target {
+
+        protected final int size;
+        protected final long seed;
+        private final String name;
+
+        Target(int size, long seed, String name) {
+            this.size = size;
+            this.seed = seed;
+            this.name = name;
+        }
+
+        String name() {
+            return name;
+        }
+
+        abstract WritableByteChannel channel();
+
+        abstract void verify() throws IOException;
+
+    }
+
+    static class FileTarget
+        extends Target
+    {
+        private final File fn;
+        private final RandomAccessFile raf;
+        private final FileChannel fc;
+
+        FileTarget(int size, long seed) throws IOException {
+            super(size, seed, "FileChannel");
+            fn = targetFile;
+            raf = new RandomAccessFile(fn, "rw");
+            fc = raf.getChannel();
+            fc.position(0);
+        }
+
+        WritableByteChannel channel() {
+            return fc;
+        }
+
+        void verify() throws IOException {
+            if (fc.position() != size)
+                throw new Failure("Wrong position: "
+                                  + fc.position() + " (expected " + size + ")");
+            checkRandomBytes(fc, 0, size, seed);
+            fc.close();
+            raf.close();                // Bug in 1.4.0
+        }
+
+    }
+
+    static class UserTarget
+        extends Target
+    {
+        private WritableByteChannel ch;
+        private final ByteBuffer dst;
+
+        UserTarget(int size, long seed) {
+            super(size, seed, "UserChannel");
+            dst = ByteBuffer.wrap(new byte[size + 1]);
+
+            ch = new WritableByteChannel() {
+                    public int write(ByteBuffer src) {
+                        int nr = Math.min(src.remaining(), dst.remaining());
+                        ByteBuffer s = src.duplicate();
+                        s.limit(s.position() + nr);
+                        dst.put(s);
+                        src.position(src.position() + nr);
+                        return nr;
+                    }
+                    public boolean isOpen() {
+                        return true;
+                    }
+                    public void close() { }
+                };
+        }
+
+        WritableByteChannel channel() {
+            return ch;
+        }
+
+        void verify() {
+            if (dst.remaining() != 1)
+                throw new Failure("Destination has " + dst.remaining()
+                                  + " bytes remaining (expected 1)");
+            byte[] ba = new byte[size];
+            Random r = new Random(seed);
+            r.nextBytes(ba);
+            dst.flip();
+            ByteBuffer rbb = ByteBuffer.wrap(ba, 0, size);
+            if (!dst.equals(rbb))
+                throw new Failure("Wrong data written");
+        }
+
+    }
+
+
+    // Generates a sequence of ints of the form 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+    // 15, 16, 17, 31, 32, 33, ..., 2^i-1, 2^i, 2^i+1, ..., max.
+
+    static class IntGenerator {
+
+        private int max;
+        private int cur = -1;
+        private int p2 = 8;
+
+        IntGenerator(int max) {
+            this.max = max;
+        }
+
+        boolean hasNext() {
+            return cur < max;
+        }
+
+        int next() {
+            if (cur >= max)
+                throw new IllegalStateException();
+            if (cur < 6) {
+                cur++;
+                return cur;
+            }
+            if (cur == p2 + 1) {
+                p2 <<= 1;
+                cur = p2 - 1;
+                return cur;
+            }
+            cur++;
+            return cur;
+        }
+
+    }
+
+
+    // -- Tests --
+
+    private static final int MAX_XFER_SIZE = 1 << 14;
+    private static final int MAX_FILE_SIZE = MAX_XFER_SIZE << 1;
+
+    private static boolean debug = false;
+    private static boolean verbose = false;
+
+    static void show(String dir, String channelName, int off, int len) {
+        if (!verbose)
+            return;
+        out.println(dir + " " + channelName +
+                    ": offset " + off + ", length " + len);
+    }
+
+    static void testTo(long seed, FileChannel fc, int off, int len, Target tgt)
+        throws IOException
+    {
+        show("To", tgt.name(), off, len);
+
+        // Clear source, then randomize just the source region
+        writeZeroBytes(fc, 0, MAX_FILE_SIZE);
+        writeRandomBytes(seed, fc, off, len);
+
+        // Randomize position
+        int pos = (int)seed & 0xfff;
+        fc.position(pos);
+
+        int n = (int)fc.transferTo(off, len, tgt.channel());
+        if (n != len)
+            throw new Failure("Incorrect transfer length: " + n
+                              + " (expected " + len + ")");
+
+        // Check that source wasn't changed
+        if (fc.position() != pos)
+            throw new Failure("Position changed");
+        if (debug)
+            dump(fc);
+        checkRandomBytes(fc, off, len, seed);
+        writeZeroBytes(fc, off, len);
+        checkZeroBytes(fc, 0, MAX_FILE_SIZE);
+
+        // Check that target was updated correctly
+        tgt.verify();
+    }
+
+    static void testFrom(long seed, Source src, FileChannel fc, int off, int len)
+        throws IOException
+    {
+        show("From", src.name(), off, len);
+
+        // Clear target
+        writeZeroBytes(fc, 0, MAX_FILE_SIZE);
+
+        // Randomize position
+        int pos = (int)seed & 0xfff;
+        fc.position(pos);
+
+        int n = (int)fc.transferFrom(src.channel(), off, len);
+        if (n != len)
+            throw new Failure("Incorrect transfer length: " + n
+                              + " (expected " + len + ")");
+
+        // Check that source didn't change, and was read correctly
+        src.verify();
+
+        // Check that target was updated correctly
+        if (fc.position() != pos)
+            throw new Failure("Position changed");
+        if (debug)
+            dump(fc);
+        checkRandomBytes(fc, off, len, seed);
+        writeZeroBytes(fc, off, len);
+        checkZeroBytes(fc, 0, MAX_FILE_SIZE);
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        if (args.length > 0) {
+            if (args[0].indexOf('v') >= 0)
+                verbose = true;
+            if (args[0].indexOf('d') >= 0)
+                debug = verbose = true;
+        }
+
+        File testDir = new File(System.getProperty("test.dir", "."));
+
+        sourceFile = File.createTempFile("xfer.src.", "", testDir);
+        sourceFile.deleteOnExit();
+        targetFile = File.createTempFile("xfer.tgt.", "", testDir);
+        targetFile.deleteOnExit();
+
+        File fn = File.createTempFile("xfer.fch.", "", testDir);
+        fn.deleteOnExit();
+
+        Random rnd = new Random();
+        int failures = 0;
+
+        try (FileChannel fc = new RandomAccessFile(fn, "rw").getChannel()) {
+            for (boolean to = false;; to = true) {
+                for (boolean user = false;; user = true) {
+                    if (!verbose)
+                        out.print((to ? "To " : "From ") +
+                                  (user ? "user channel" : "file channel")
+                                  + ":");
+                    IntGenerator offGen = new IntGenerator(MAX_XFER_SIZE + 2);
+                    while (offGen.hasNext()) {
+                        int off = offGen.next();
+                        if (!verbose) out.print(" " + off);
+                        IntGenerator lenGen = new IntGenerator(MAX_XFER_SIZE + 2);
+                        while (lenGen.hasNext()) {
+                            int len = lenGen.next();
+                            long s = rnd.nextLong();
+                            String chName = null;
+                            try {
+                                if (to) {
+                                    Target tgt;
+                                    if (user)
+                                        tgt = new UserTarget(len, s);
+                                    else
+                                        tgt = new FileTarget(len, s);
+                                    chName = tgt.name();
+                                    testTo(s, fc, off, len, tgt);
+                                }
+                                else {
+                                    Source src;
+                                    if (user)
+                                        src = new UserSource(len, s);
+                                    else
+                                        src = new FileSource(len, s);
+                                    chName = src.name();
+                                    testFrom(s, src, fc, off, len);
+                                }
+                            } catch (Failure x) {
+                                out.println();
+                                out.println("FAILURE: " + chName
+                                            + ", offset " + off
+                                            + ", length " + len);
+                                x.printStackTrace(out);
+                                failures++;
+                            }
+                        }
+                    }
+                    if (!verbose)
+                        out.println();
+                    if (user)
+                        break;
+                }
+                if (to)
+                    break;
+            }
+        }
+
+        sourceFile.delete();
+        targetFile.delete();
+        fn.delete();
+
+        if (failures > 0) {
+            out.println();
+            throw new RuntimeException("Some tests failed");
+        }
+
+        out.println("Test succeeded.");
+    }
+}