23
23
24
24
import java .io .ByteArrayInputStream ;
25
25
import java .io .ByteArrayOutputStream ;
26
- import java .io .IOException ;
27
26
import java .io .InputStream ;
27
+ import java .io .IOException ;
28
28
import java .io .OutputStream ;
29
+ import java .nio .ByteBuffer ;
29
30
import java .nio .channels .Channels ;
30
31
import java .nio .channels .FileChannel ;
31
32
import java .nio .channels .IllegalBlockingModeException ;
51
52
import jdk .test .lib .RandomFactory ;
52
53
53
54
import static java .lang .String .format ;
55
+ import static java .nio .file .StandardOpenOption .*;
54
56
55
57
import static org .testng .Assert .assertEquals ;
56
58
import static org .testng .Assert .assertThrows ;
62
64
* @build jdk.test.lib.RandomFactory
63
65
* @run testng/othervm/timeout=180 TransferTo
64
66
* @bug 8265891
65
- * @summary tests whether sun.nio.ChannelInputStream.transferTo conforms to the
66
- * InputStream.transferTo contract defined in the javadoc
67
+ * @summary Tests whether sun.nio.ChannelInputStream.transferTo conforms to the
68
+ * InputStream.transferTo specification
67
69
* @key randomness
68
70
*/
69
71
public class TransferTo {
@@ -72,27 +74,30 @@ public class TransferTo {
72
74
73
75
private static final int ITERATIONS = 10 ;
74
76
75
- private static final int NUM_WRITES = 3 * 1024 ;
76
- private static final int BYTES_PER_WRITE = 1024 * 1024 ;
77
- private static final long BYTES_WRITTEN = (long ) NUM_WRITES * BYTES_PER_WRITE ;
77
+ private static final int NUM_WRITES = 3 * 1024 ;
78
+ private static final int BYTES_PER_WRITE = 1024 * 1024 ;
79
+ private static final long BYTES_WRITTEN = (long ) NUM_WRITES * BYTES_PER_WRITE ;
78
80
79
81
private static final Random RND = RandomFactory .getRandom ();
80
82
81
83
private static final Path CWD = Path .of ("." );
82
84
83
85
/*
84
- * Provides test scenarios, i. e. combinations of input and output streams to be tested.
86
+ * Provides test scenarios, i.e., combinations of input and output streams
87
+ * to be tested.
85
88
*/
86
89
@ DataProvider
87
90
public static Object [][] streamCombinations () throws Exception {
88
91
return new Object [][] {
89
92
// tests FileChannel.transferTo(FileChannel) optimized case
90
93
{ fileChannelInput (), fileChannelOutput () },
91
94
92
- // tests FileChannel.transferTo(SelectableChannelOutput) optimized case
95
+ // tests FileChannel.transferTo(SelectableChannelOutput)
96
+ // optimized case
93
97
{ fileChannelInput (), selectableChannelOutput () },
94
98
95
- // tests FileChannel.transferTo(WritableChannelOutput) optimized case
99
+ // tests FileChannel.transferTo(WritableChannelOutput)
100
+ // optimized case
96
101
{ fileChannelInput (), writableByteChannelOutput () },
97
102
98
103
// tests InputStream.transferTo(OutputStream) default case
@@ -101,7 +106,8 @@ public static Object[][] streamCombinations() throws Exception {
101
106
}
102
107
103
108
/*
104
- * Testing API compliance: Input stream must throw NullPointerException when parameter "out" is null.
109
+ * Testing API compliance: input stream must throw NullPointerException
110
+ * when parameter "out" is null.
105
111
*/
106
112
@ Test (dataProvider = "streamCombinations" )
107
113
public void testNullPointerException (InputStreamProvider inputStreamProvider ,
@@ -117,7 +123,8 @@ public void testNullPointerException(InputStreamProvider inputStreamProvider,
117
123
}
118
124
119
125
/*
120
- * Testing API compliance: Complete content of input stream must be transferred to output stream.
126
+ * Testing API compliance: complete content of input stream must be
127
+ * transferred to output stream.
121
128
*/
122
129
@ Test (dataProvider = "streamCombinations" )
123
130
public void testStreamContents (InputStreamProvider inputStreamProvider ,
@@ -128,10 +135,12 @@ public void testStreamContents(InputStreamProvider inputStreamProvider,
128
135
// tests input stream with a length between 1k and 4k
129
136
checkTransferredContents (inputStreamProvider , outputStreamProvider , createRandomBytes (1024 , 4096 ));
130
137
131
- // tests input stream with several data chunks, as 16k is more than a single chunk can hold
138
+ // tests input stream with several data chunks, as 16k is more than a
139
+ // single chunk can hold
132
140
checkTransferredContents (inputStreamProvider , outputStreamProvider , createRandomBytes (16384 , 16384 ));
133
141
134
- // tests randomly chosen starting positions within source and target stream
142
+ // tests randomly chosen starting positions within source and
143
+ // target stream
135
144
for (int i = 0 ; i < ITERATIONS ; i ++) {
136
145
byte [] inBytes = createRandomBytes (MIN_SIZE , MAX_SIZE_INCR );
137
146
int posIn = RND .nextInt (inBytes .length );
@@ -147,34 +156,60 @@ public void testStreamContents(InputStreamProvider inputStreamProvider,
147
156
}
148
157
149
158
/*
150
- * Special test for file-to-file transfer of more than two GB.
151
- * This test covers multiple iterations of FileChannel.transerTo(FileChannel),
152
- * which ChannelInputStream.transferTo() only applies in this particular case,
153
- * and cannot get tested using a single byte[] due to size limitation of arrays.
159
+ * Special test for file-to-file transfer of more than 2 GB. This test
160
+ * covers multiple iterations of FileChannel.transerTo(FileChannel),
161
+ * which ChannelInputStream.transferTo() only applies in this particular
162
+ * case, and cannot get tested using a single byte[] due to size limitation
163
+ * of arrays.
154
164
*/
155
165
@ Test
156
166
public void testMoreThanTwoGB () throws IOException {
157
- Path sourceFile = Files .createTempFile (CWD , "test2GBSource" , null );
167
+ // prepare two temporary files to be compared at the end of the test
168
+ // set the source file name
169
+ String sourceName = String .format ("test3GBSource%s.tmp" ,
170
+ String .valueOf (RND .nextInt (Integer .MAX_VALUE )));
171
+ Path sourceFile = CWD .resolve (sourceName );
172
+
158
173
try {
159
- // preparing two temporary files which will be compared at the end of the test
160
- Path targetFile = Files .createTempFile (CWD , "test2GBtarget" , null );
174
+ // set the target file name
175
+ String targetName = String .format ("test3GBTarget%s.tmp" ,
176
+ String .valueOf (RND .nextInt (Integer .MAX_VALUE )));
177
+ Path targetFile = CWD .resolve (targetName );
178
+
161
179
try {
162
- // writing 3 GB of random bytes into source file
163
- for (int i = 0 ; i < NUM_WRITES ; i ++)
164
- Files .write (sourceFile , createRandomBytes (BYTES_PER_WRITE , 0 ), StandardOpenOption .APPEND );
180
+ // calculate initial position to be just short of 2GB
181
+ final long initPos = 2047 *BYTES_PER_WRITE ;
182
+
183
+ // create the source file with a hint to be sparse
184
+ try (FileChannel fc = FileChannel .open (sourceFile , CREATE_NEW , SPARSE , WRITE , APPEND );) {
185
+ // set initial position to avoid writing nearly 2GB
186
+ fc .position (initPos );
187
+
188
+ // fill the remainder of the file with random bytes
189
+ int nw = (int )(NUM_WRITES - initPos /BYTES_PER_WRITE );
190
+ for (int i = 0 ; i < nw ; i ++) {
191
+ byte [] rndBytes = createRandomBytes (BYTES_PER_WRITE , 0 );
192
+ ByteBuffer src = ByteBuffer .wrap (rndBytes );
193
+ fc .write (src );
194
+ }
195
+ }
165
196
166
- // performing actual transfer, effectively by multiple invocations of Filechannel.transferTo(FileChannel)
167
- long count ;
168
- try (InputStream inputStream = Channels .newInputStream (FileChannel .open (sourceFile ));
169
- OutputStream outputStream = Channels
170
- .newOutputStream (FileChannel .open (targetFile , StandardOpenOption .WRITE ))) {
171
- count = inputStream .transferTo (outputStream );
197
+ // create the target file with a hint to be sparse
198
+ try (FileChannel fc = FileChannel .open (targetFile , CREATE_NEW , WRITE , SPARSE );) {
172
199
}
173
200
174
- // comparing reported transferred bytes, must be 3 GB
175
- assertEquals (count , BYTES_WRITTEN );
201
+ // perform actual transfer, effectively by multiple invocations
202
+ // of Filechannel.transferTo(FileChannel)
203
+ try (InputStream inputStream = Channels .newInputStream (FileChannel .open (sourceFile ));
204
+ OutputStream outputStream = Channels .newOutputStream (FileChannel .open (targetFile , WRITE ))) {
205
+ long count = inputStream .transferTo (outputStream );
206
+
207
+ // compare reported transferred bytes, must be 3 GB
208
+ // less the value of the initial position
209
+ assertEquals (count , BYTES_WRITTEN - initPos );
210
+ }
176
211
177
- // comparing content of both files, failing in case of any difference
212
+ // compare content of both files, failing if different
178
213
assertEquals (Files .mismatch (sourceFile , targetFile ), -1 );
179
214
180
215
} finally {
@@ -186,28 +221,33 @@ public void testMoreThanTwoGB() throws IOException {
186
221
}
187
222
188
223
/*
189
- * Special test whether selectable channel based transfer throws blocking mode exception.
224
+ * Special test of whether selectable channel based transfer throws blocking
225
+ * mode exception.
190
226
*/
191
227
@ Test
192
228
public void testIllegalBlockingMode () throws IOException {
193
229
Pipe pipe = Pipe .open ();
194
230
try {
195
- // testing arbitrary input (here: empty file) to non-blocking selectable output
231
+ // testing arbitrary input (here: empty file) to non-blocking
232
+ // selectable output
196
233
try (FileChannel fc = FileChannel .open (Files .createTempFile (CWD , "testIllegalBlockingMode" , null ));
197
- InputStream is = Channels .newInputStream (fc );
198
- SelectableChannel sc = pipe .sink ().configureBlocking (false );
199
- OutputStream os = Channels .newOutputStream ((WritableByteChannel ) sc )) {
234
+ InputStream is = Channels .newInputStream (fc );
235
+ SelectableChannel sc = pipe .sink ().configureBlocking (false );
236
+ OutputStream os = Channels .newOutputStream ((WritableByteChannel ) sc )) {
200
237
201
- // IllegalBlockingMode must be thrown when trying to perform a transfer
238
+ // IllegalBlockingMode must be thrown when trying to perform
239
+ // a transfer
202
240
assertThrows (IllegalBlockingModeException .class , () -> is .transferTo (os ));
203
241
}
204
242
205
- // testing non-blocking selectable input to arbitrary output (here: byte array)
243
+ // testing non-blocking selectable input to arbitrary output
244
+ // (here: byte array)
206
245
try (SelectableChannel sc = pipe .source ().configureBlocking (false );
207
- InputStream is = Channels .newInputStream ((ReadableByteChannel ) sc );
208
- OutputStream os = new ByteArrayOutputStream ()) {
246
+ InputStream is = Channels .newInputStream ((ReadableByteChannel ) sc );
247
+ OutputStream os = new ByteArrayOutputStream ()) {
209
248
210
- // IllegalBlockingMode must be thrown when trying to perform a transfer
249
+ // IllegalBlockingMode must be thrown when trying to perform
250
+ // a transfer
211
251
assertThrows (IllegalBlockingModeException .class , () -> is .transferTo (os ));
212
252
}
213
253
} finally {
@@ -217,26 +257,26 @@ public void testIllegalBlockingMode() throws IOException {
217
257
}
218
258
219
259
/*
220
- * Asserts that the transferred content is correct, i. e. compares the actually transferred bytes
221
- * to the expected assumption . The position of the input and output stream before the transfer is
222
- * the start of stream (BOF).
260
+ * Asserts that the transferred content is correct, i.e., compares the bytes
261
+ * actually transferred to those expected. The position of the input and
262
+ * output streams before the transfer are zero (BOF).
223
263
*/
224
264
private static void checkTransferredContents (InputStreamProvider inputStreamProvider ,
225
265
OutputStreamProvider outputStreamProvider , byte [] inBytes ) throws Exception {
226
266
checkTransferredContents (inputStreamProvider , outputStreamProvider , inBytes , 0 , 0 );
227
267
}
228
268
229
269
/*
230
- * Asserts that the transferred content is correct, i. e. compares the actually transferred bytes
231
- * to the expected assumption . The position of the input and output stream before the transfer is
232
- * provided by the caller.
270
+ * Asserts that the transferred content is correct, i. e. compares the bytes
271
+ * actually transferred to those expected. The positions of the input and
272
+ * output streams before the transfer are provided by the caller.
233
273
*/
234
274
private static void checkTransferredContents (InputStreamProvider inputStreamProvider ,
235
275
OutputStreamProvider outputStreamProvider , byte [] inBytes , int posIn , int posOut ) throws Exception {
236
276
AtomicReference <Supplier <byte []>> recorder = new AtomicReference <>();
237
277
try (InputStream in = inputStreamProvider .input (inBytes );
238
- OutputStream out = outputStreamProvider .output (recorder ::set )) {
239
- // skip bytes till starting position
278
+ OutputStream out = outputStreamProvider .output (recorder ::set )) {
279
+ // skip bytes until starting position
240
280
in .skipNBytes (posIn );
241
281
out .write (new byte [posOut ]);
242
282
@@ -252,7 +292,8 @@ private static void checkTransferredContents(InputStreamProvider inputStreamProv
252
292
}
253
293
254
294
/*
255
- * Creates an array of random size (between min and min + maxRandomAdditive) filled with random bytes
295
+ * Creates an array of random size (between min and min + maxRandomAdditive)
296
+ * filled with random bytes
256
297
*/
257
298
private static byte [] createRandomBytes (int min , int maxRandomAdditive ) {
258
299
byte [] bytes = new byte [min + (maxRandomAdditive == 0 ? 0 : RND .nextInt (maxRandomAdditive ))];
@@ -298,7 +339,8 @@ public InputStream input(byte... bytes) throws Exception {
298
339
}
299
340
300
341
/*
301
- * Creates a provider for an input stream which wraps a readable byte channel but is not a file channel
342
+ * Creates a provider for an input stream which wraps a readable byte
343
+ * channel but is not a file channel
302
344
*/
303
345
private static InputStreamProvider readableByteChannelInput () {
304
346
return new InputStreamProvider () {
@@ -316,7 +358,7 @@ private static OutputStreamProvider fileChannelOutput() {
316
358
return new OutputStreamProvider () {
317
359
public OutputStream output (Consumer <Supplier <byte []>> spy ) throws Exception {
318
360
Path path = Files .createTempFile (CWD , "fileChannelOutput" , null );
319
- FileChannel fileChannel = FileChannel .open (path , StandardOpenOption . WRITE );
361
+ FileChannel fileChannel = FileChannel .open (path , WRITE );
320
362
spy .accept (() -> {
321
363
try {
322
364
return Files .readAllBytes (path );
0 commit comments