/*
* Copyright 2016 The Simple File Server Authors
*
* 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 org.sfs.util;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.file.AsyncFile;
import io.vertx.core.file.OpenOptions;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.RunTestOnContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sfs.SfsVertx;
import org.sfs.SfsVertxImpl;
import org.sfs.TestSubscriber;
import org.sfs.io.BufferWriteEndableWriteStream;
import org.sfs.io.DigestEndableWriteStream;
import org.sfs.io.NullEndableWriteStream;
import org.sfs.io.PipedEndableWriteStream;
import org.sfs.io.PipedReadStream;
import org.sfs.thread.NamedCapacityFixedThreadPool;
import rx.Observable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.concurrent.ExecutorService;
import static com.google.common.hash.Hashing.sha512;
import static com.google.common.io.Files.hash;
import static io.vertx.core.buffer.Buffer.buffer;
import static java.nio.file.Files.createTempFile;
import static java.nio.file.Files.deleteIfExists;
import static java.nio.file.Files.newOutputStream;
import static org.sfs.io.AsyncIO.pump;
import static org.sfs.rx.Defer.aVoid;
import static org.sfs.rx.RxHelper.combineSinglesDelayError;
import static org.sfs.util.MessageDigestFactory.SHA512;
import static org.sfs.util.PrngRandom.getCurrentInstance;
import static org.sfs.util.VertxAssert.assertArrayEquals;
@RunWith(VertxUnitRunner.class)
public class PipedStreamTest {
@Rule
public final RunTestOnContext rule = new RunTestOnContext();
private ExecutorService backgroundPool;
private ExecutorService ioPool;
@Before
public void start() {
ioPool = NamedCapacityFixedThreadPool.newInstance(200, "sfs-io-pool");
backgroundPool = NamedCapacityFixedThreadPool.newInstance(200, "sfs-blocking-action-pool");
}
@After
public void stop(TestContext context) {
if (ioPool != null) {
ioPool.shutdown();
}
if (backgroundPool != null) {
backgroundPool.shutdown();
}
}
@Test
public void testImmediateEnd(TestContext context) {
SfsVertx sfsVertx = new SfsVertxImpl(rule.vertx(), backgroundPool, ioPool);
Buffer testBuffer = buffer("test");
Async async = context.async();
aVoid()
.flatMap(aVoid -> {
PipedReadStream pipedReadStream = new PipedReadStream();
PipedEndableWriteStream pipedEndableWriteStream = new PipedEndableWriteStream(pipedReadStream);
BufferWriteEndableWriteStream bufferWriteStream = new BufferWriteEndableWriteStream();
pipedEndableWriteStream.end(testBuffer);
return pump(pipedReadStream, bufferWriteStream)
.map(aVoid1 -> {
assertArrayEquals(context, testBuffer.getBytes(), bufferWriteStream.toBuffer().getBytes());
return (Void) null;
});
})
.subscribe(new TestSubscriber(context, async));
}
@Test
public void testDelayedEnd(TestContext context) {
SfsVertx vertx = new SfsVertxImpl(rule.vertx(), backgroundPool, ioPool);
Buffer testBuffer = buffer("test");
Async async = context.async();
aVoid()
.flatMap(aVoid -> {
PipedReadStream pipedReadStream = new PipedReadStream();
PipedEndableWriteStream pipedEndableWriteStream = new PipedEndableWriteStream(pipedReadStream);
BufferWriteEndableWriteStream bufferWriteStream = new BufferWriteEndableWriteStream();
pipedEndableWriteStream.write(testBuffer);
return pump(pipedReadStream, bufferWriteStream)
.doOnSubscribe(() -> vertx.setTimer(500, event -> pipedEndableWriteStream.end()))
.map(aVoid1 -> {
assertArrayEquals(context, testBuffer.getBytes(), bufferWriteStream.toBuffer().getBytes());
return (Void) null;
});
})
.subscribe(new TestSubscriber(context, async));
}
@Test
public void testImmediatePumpFile(TestContext context) throws IOException {
SfsVertx vertx = new SfsVertxImpl(rule.vertx(), backgroundPool, ioPool);
byte[] bytes = new byte[256];
getCurrentInstance().nextBytesBlocking(bytes);
Path path = createTempFile("", "");
try (OutputStream outputStream = newOutputStream(path)) {
for (int i = 0; i < 10000; i++) {
outputStream.write(bytes);
}
}
final byte[] sha512 = hash(path.toFile(), sha512()).asBytes();
Async async = context.async();
aVoid()
.flatMap(aVoid -> {
AsyncFile asyncFile = vertx.fileSystem().openBlocking(path.toString(), new OpenOptions());
PipedReadStream pipedReadStream = new PipedReadStream();
PipedEndableWriteStream pipedEndableWriteStream = new PipedEndableWriteStream(pipedReadStream);
Observable<Void> producer = pump(asyncFile, pipedEndableWriteStream);
DigestEndableWriteStream digestWriteStream = new DigestEndableWriteStream(new NullEndableWriteStream(), SHA512);
Observable<Void> consumer = pump(pipedReadStream, digestWriteStream);
return combineSinglesDelayError(producer, consumer, (aVoid1, aVoid2) -> {
assertArrayEquals(context, sha512, digestWriteStream.getDigest(SHA512).get());
return (Void) null;
});
})
.doOnTerminate(() -> {
try {
deleteIfExists(path);
} catch (IOException e) {
e.printStackTrace();
}
})
.subscribe(new TestSubscriber(context, async));
}
}