--- /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.");
+ }
+}