/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.blob;
import com.foundationdb.Transaction;
import com.foundationdb.async.Function;
import com.foundationdb.directory.DirectorySubspace;
import com.foundationdb.directory.PathUtil;
import com.foundationdb.server.test.it.FDBITBase;
import com.foundationdb.subspace.Subspace;
import com.foundationdb.tuple.ByteArrayUtil;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class BlobAsyncIT extends FDBITBase
{
@Test
public void writeReadEmpty() {
writeAndRead(0);
}
@Test
public void writeReadTiny() {
writeAndRead(100);
}
@Test
public void writeReadSmall() {
writeAndRead(1000);
}
@Test
public void writeReadMedium() {
writeAndRead(10000);
}
@Test
public void writeReadLarge() {
writeAndRead(100000);
}
@Test
public void writeReadHuge() {
writeAndRead(1000000);
}
@Test
public void append() {
final byte[] testBytes1 = generateBytes(100);
final byte[] testBytes2 = generateBytes(100);
final byte[] testBytesBoth = ByteArrayUtil.join(testBytes1, testBytes2);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
BlobAsync blob = new BlobAsync(getDir(tr));
// Append to non-existent
blob.append(tr, testBytes1).get();
byte[] readBytes = blob.read(tr).get();
assertArrayEquals(testBytes1, readBytes);
// Append to pre-existing
blob.append(tr, testBytes2).get();
readBytes = blob.read(tr).get();
assertArrayEquals(testBytesBoth, readBytes);
return null;
}
});
}
@Test
public void truncate() {
final byte[] testBytes = generateBytes(100);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
BlobAsync blob = new BlobAsync(getDir(tr));
blob.write(tr, 0, testBytes).get();
blob.truncate(tr, 50).get();
byte[] readBytes = blob.read(tr).get();
assertArrayEquals(Arrays.copyOf(testBytes, 50), readBytes);
blob.truncate(tr, 0).get();
readBytes = blob.read(tr).get();
assertNull(readBytes);
return null;
}
});
}
@Test
public void writePartial() {
final byte[] testBytes = generateBytes(100);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
BlobAsync blob = new BlobAsync(getDir(tr));
blob.write(tr, 0, testBytes).get();
byte[] subBytes = new byte[10];
for(int i = 0; i < subBytes.length; ++i) {
subBytes[i] = (byte)(0x42 + i);
}
blob.write(tr, 42, subBytes).get();
byte[] newBytes = Arrays.copyOf(testBytes, testBytes.length);
System.arraycopy(subBytes, 0, newBytes, 42, subBytes.length);
byte[] readBytes = blob.read(tr).get();
assertArrayEquals(newBytes, readBytes);
return null;
}
});
}
@Test
public void readPartial() {
final byte[] testBytes = generateBytes(100);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
BlobAsync blob = new BlobAsync(getDir(tr));
blob.write(tr, 0, testBytes).get();
byte[] subBytes = Arrays.copyOfRange(testBytes, 42, 52);
byte[] readBytes = blob.read(tr, 42, 10).get();
assertArrayEquals(subBytes, readBytes);
return null;
}
});
}
@Test
public void scanBounds() {
final byte[] testBytes = generateBytes(4096);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
DirectorySubspace dir = getDir(tr);
byte[] beforePrefix = ByteArrayUtil.join(dir.pack(), new byte[] { 0x41 });
byte[] blobPrefix = ByteArrayUtil.join(dir.pack(), new byte[] { 0x42 });
byte[] afterPrefix = ByteArrayUtil.join(dir.pack(), new byte[] { 0x43 });
tr.set(beforePrefix, beforePrefix);
tr.set(afterPrefix, afterPrefix);
BlobAsync blob = new BlobAsync(new Subspace(blobPrefix));
blob.write(tr, 0, testBytes).get();
byte[] readBytes = blob.read(tr).get();
assertArrayEquals(testBytes, readBytes);
return null;
}
});
}
private void writeAndRead(final int len) {
final byte[] testBytes = generateBytes(len);
fdbHolder().getTransactionContext().run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
BlobAsync blob = new BlobAsync(getDir(tr));
blob.write(tr, 0, testBytes).get();
assertEquals(Long.valueOf(len), blob.getSize(tr).get());
byte[] readBytes = blob.read(tr).get();
if(len == 0) {
assertNull(readBytes);
} else {
assertArrayEquals(testBytes, readBytes);
}
return null;
}
});
}
private DirectorySubspace getDir(Transaction tr) {
DirectorySubspace dir = fdbHolder().getRootDirectory().createOrOpen(tr, PathUtil.from("blob_test")).get();
tr.clear(dir.range());
return dir;
}
private static byte[] generateBytes(int len) {
byte[] bytes = new byte[len];
for(int i = 0; i < len; ++i) {
bytes[i] = (byte)(i % 10);
}
return bytes;
}
}