| /* |
| * Copyright (C) 2016 Square, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package okio; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.Future; |
| import java.util.zip.Deflater; |
| import java.util.zip.GZIPInputStream; |
| import java.util.zip.GZIPOutputStream; |
| import org.junit.Test; |
| |
| import static okio.TestUtil.SEGMENT_SIZE; |
| import static okio.TestUtil.randomSource; |
| import static org.junit.Assert.assertEquals; |
| |
| /** Slow running tests that run a large amount of data through a stream. */ |
| public final class LargeStreamsTest { |
| /** 4 GiB plus 1 byte. This is greater than what can be expressed in an unsigned int. */ |
| public static final long FOUR_GIB_PLUS_ONE = 0x100000001L; |
| |
| /** SHA-256 of {@code TestUtil.randomSource(FOUR_GIB_PLUS_ONE)}. */ |
| public static final ByteString SHA256_RANDOM_FOUR_GIB_PLUS_1 = ByteString.decodeHex( |
| "9654947a655c5efc445502fd1bf11117d894b7812b7974fde8ca4a02c5066315"); |
| |
| @Test public void test() throws Exception { |
| Pipe pipe = new Pipe(1024 * 1024); |
| |
| Future<Long> future = readAllAndCloseAsync(randomSource(FOUR_GIB_PLUS_ONE), pipe.sink()); |
| |
| HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); |
| readAllAndClose(pipe.source(), hashingSink); |
| |
| assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); |
| assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); |
| } |
| |
| /** Note that this test hangs on Android. */ |
| @Test public void gzipSource() throws Exception { |
| Pipe pipe = new Pipe(1024 * 1024); |
| |
| OutputStream gzipOut = new GZIPOutputStream(Okio.buffer(pipe.sink()).outputStream()) { |
| { |
| // Disable compression to speed up a slow test. Improved from 141s to 33s on one machine. |
| def.setLevel(Deflater.NO_COMPRESSION); |
| } |
| }; |
| Future<Long> future = readAllAndCloseAsync( |
| randomSource(FOUR_GIB_PLUS_ONE), Okio.sink(gzipOut)); |
| |
| HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); |
| GzipSource gzipSource = new GzipSource(pipe.source()); |
| readAllAndClose(gzipSource, hashingSink); |
| |
| assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); |
| assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); |
| } |
| |
| /** Note that this test hangs on Android. */ |
| @Test public void gzipSink() throws Exception { |
| Pipe pipe = new Pipe(1024 * 1024); |
| |
| GzipSink gzipSink = new GzipSink(pipe.sink()); |
| |
| // Disable compression to speed up a slow test. Improved from 141s to 35s on one machine. |
| gzipSink.deflater().setLevel(Deflater.NO_COMPRESSION); |
| Future<Long> future = readAllAndCloseAsync(randomSource(FOUR_GIB_PLUS_ONE), gzipSink); |
| |
| HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); |
| GZIPInputStream gzipIn = new GZIPInputStream(Okio.buffer(pipe.source()).inputStream()); |
| readAllAndClose(Okio.source(gzipIn), hashingSink); |
| |
| assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); |
| assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); |
| } |
| |
| /** Reads all bytes from {@code source} and writes them to {@code sink}. */ |
| private Long readAllAndClose(Source source, Sink sink) throws IOException { |
| long result = 0L; |
| Buffer buffer = new Buffer(); |
| for (long count; (count = source.read(buffer, SEGMENT_SIZE)) != -1L; result += count) { |
| sink.write(buffer, count); |
| } |
| source.close(); |
| sink.close(); |
| return result; |
| } |
| |
| /** Calls {@link #readAllAndClose} on a background thread. */ |
| private Future<Long> readAllAndCloseAsync(final Source source, final Sink sink) { |
| ExecutorService executor = Executors.newSingleThreadExecutor(); |
| try { |
| return executor.submit(new Callable<Long>() { |
| @Override public Long call() throws Exception { |
| return readAllAndClose(source, sink); |
| } |
| }); |
| } finally { |
| executor.shutdown(); |
| } |
| } |
| } |