blob: fdc0628597277117c226c44982d2b5e976e2aa4a [file] [log] [blame]
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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 com.baidu.hugegraph.computer.core.store;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.UUID;
import org.apache.commons.io.FileUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import com.baidu.hugegraph.computer.core.config.ComputerOptions;
import com.baidu.hugegraph.computer.core.config.Config;
import com.baidu.hugegraph.computer.core.io.UnsafeBytesInput;
import com.baidu.hugegraph.computer.core.io.UnsafeBytesOutput;
import com.baidu.hugegraph.computer.core.store.file.seqfile.ValueFileInput;
import com.baidu.hugegraph.computer.core.store.file.seqfile.ValueFileOutput;
import com.baidu.hugegraph.computer.suite.unit.UnitTestBase;
import com.baidu.hugegraph.testutil.Assert;
import com.baidu.hugegraph.testutil.Whitebox;
import com.baidu.hugegraph.util.E;
public class ValueFileTest {
private static Config CONFIG;
@BeforeClass
public static void init() {
CONFIG = UnitTestBase.updateWithRequiredOptions(
ComputerOptions.VALUE_FILE_MAX_SEGMENT_SIZE, "32"
);
}
@Test
public void testConstructor() throws IOException {
int bufferCapacity = 13;
File dir = createTempDir();
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferCapacity)) {
Assert.assertEquals(0, output.position());
}
Assert.assertThrows(IllegalArgumentException.class, () -> {
new ValueFileOutput(CONFIG, dir, 1);
}, e -> {
Assert.assertContains("bufferCapacity must be >= 8",
e.getMessage());
});
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferCapacity)) {
Assert.assertEquals(0, input.position());
}
Assert.assertThrows(IllegalArgumentException.class, () -> {
new ValueFileInput(CONFIG, dir, 1);
}, e -> {
Assert.assertContains("The parameter bufferSize must be >= 8",
e.getMessage());
});
Config config = UnitTestBase.updateWithRequiredOptions(
ComputerOptions.VALUE_FILE_MAX_SEGMENT_SIZE,
String.valueOf(Integer.MAX_VALUE)
);
try (ValueFileOutput output = new ValueFileOutput(config, dir)) {
Assert.assertEquals(0, output.position());
}
try (ValueFileInput input = new ValueFileInput(config, dir)) {
Assert.assertEquals(0, input.position());
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testByteArray() throws IOException {
File dir = createTempDir();
final int bufferSize = 13;
byte[] bytes;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
// The remaining capacity of the buffer can hold
bytes = orderBytesBySize(5);
output.write(bytes);
/*
* The remaining capacity of the buffer can't hold and write
* size is smaller than buffer capacity
*/
bytes = orderBytesBySize(11);
output.write(bytes);
/*
* Write size is greater than buffer capacity and remaining of
* segment can hold write size
*/
bytes = orderBytesBySize(14);
output.write(bytes);
/*
* Write size is greater than buffer capacity and remaining of
* segment can't hold write size
*/
bytes = orderBytesBySize(80);
output.write(bytes);
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
assertBytes(5, input.readBytes(5));
assertBytes(11, input.readBytes(11));
assertBytes(14, input.readBytes(14));
assertBytes(80, input.readBytes(80));
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testWriteInt() throws IOException {
File dir = createTempDir();
final int bufferSize = 10;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
output.writeInt(i);
}
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
Assert.assertEquals(i, input.readInt());
}
Assert.assertThrows(IOException.class, input::readInt, e -> {
Assert.assertContains("overflows buffer",
e.getMessage());
});
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testWriteIntWithPosition() throws IOException {
File dir = createTempDir();
final int bufferSize = 20;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
// Write position in buffer
if (i == 2) {
output.writeFixedInt(4, 100);
}
output.writeInt(i);
}
output.writeFixedInt(Integer.BYTES * 10, 100);
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
if (i == 1 || i == 10) {
Assert.assertEquals(100, input.readInt());
continue;
}
Assert.assertEquals(i, input.readInt());
}
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testSeek() throws IOException {
File dir = createTempDir();
final int bufferSize = 15;
try {
// Test ValueFileInput
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
output.writeInt(i);
}
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
Long fileLength = Whitebox.getInternalState(input,
"fileLength");
Assert.assertEquals(200L, fileLength);
long position;
// Position in buffer
position = 13L;
assertSeek(fileLength, input, position);
// Position not in buffer but in current segment
position = 28L;
assertSeek(fileLength, input, position);
// Position not in buffer and not in current segment
position = 200L;
assertSeek(fileLength, input, position);
position = 3L;
assertSeek(fileLength, input, position);
position = 3L;
assertSeek(fileLength, input, position);
position = 68L;
assertSeek(fileLength, input, position);
position = 0L;
assertSeek(fileLength, input, position);
// Position out of bound
position = 300L;
long finalPosition = position;
Assert.assertThrows(EOFException.class, () -> {
input.seek(finalPosition);
}, e -> {
Assert.assertContains("reach the end of file",
e.getMessage());
});
input.seek(16 * Integer.BYTES);
Assert.assertEquals(16, input.readInt());
// Random seek and assert data
Random random = new Random();
for (int i = 0; i < 10; i++) {
int num = random.nextInt(49);
input.seek(num * Integer.BYTES);
Assert.assertEquals(num, input.readInt());
}
}
// Test ValueFileOutput
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
// 200, 100, 2, 3, 4.....
for (int i = 0; i < 50; i++) {
// Position in buffer
if (i == 2) {
output.seek(4);
output.writeInt(100);
}
// Position not int buffer
if (i == 10) {
long oldPosition = output.position();
output.seek(0);
output.writeInt(200);
output.seek(oldPosition);
}
output.writeInt(i);
}
Assert.assertThrows(EOFException.class, () -> {
output.seek(500L);
}, e -> {
Assert.assertContains("reach the end of file",
e.getMessage());
});
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
if (i == 0) {
Assert.assertEquals(200, input.readInt());
continue;
}
if (i == 1) {
Assert.assertEquals(100, input.readInt());
continue;
}
Assert.assertEquals(i, input.readInt());
}
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testSkip() throws IOException {
File dir = createTempDir();
final int bufferSize = 15;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
// 100, 1, 2, 200, 4, 5 ......
for (int i = 0; i < 50; i++) {
output.writeInt(i);
}
output.seek(0);
output.writeInt(100);
output.skip(8);
output.writeInt(200);
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
Assert.assertEquals(100, input.readInt());
input.skip(8);
Assert.assertEquals(200, input.readInt());
input.seek(20 * Integer.BYTES);
Assert.assertEquals(20, input.readInt());
input.skip(10 * Integer.BYTES);
Assert.assertEquals(31, input.readInt());
Assert.assertThrows(IllegalArgumentException.class, () -> {
input.skip(500L);
}, e -> {
Assert.assertContains("because don't have enough data",
e.getMessage());
});
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testDuplicate() throws IOException {
File dir = createTempDir();
final int bufferSize = 15;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
for (int i = 0; i < 50; i++) {
output.writeInt(i);
}
}
try (ValueFileInput input = new ValueFileInput(CONFIG, dir,
bufferSize)) {
input.seek(20 * Integer.BYTES);
Assert.assertEquals(20, input.readInt());
UnsafeBytesInput other = input.duplicate();
Assert.assertEquals(21, other.readInt());
Assert.assertEquals(21, input.readInt());
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
@Test
public void testCompare() throws IOException {
File dir = createTempDir();
final int bufferSize = 15;
try {
try (ValueFileOutput output = new ValueFileOutput(CONFIG, dir,
bufferSize)) {
String data = "HugeGraph is a fast-speed and highly-scalable " +
"graph database";
output.write(data.getBytes());
}
try (ValueFileInput input1 = new ValueFileInput(CONFIG, dir,
bufferSize);
ValueFileInput input2 = new ValueFileInput(CONFIG, dir,
bufferSize)) {
int result;
// All in buffer
result = input1.compare(0, 3, input2, 1, 3);
Assert.assertLt(0, result);
result = input1.compare(0, 3, input2, 0, 5);
Assert.assertLt(0, result);
result = input1.compare(1, 3, input2, 1, 3);
Assert.assertEquals(0, result);
// The input1 in buffer, input2 not in buffer
result = input1.compare(0, 3, input2, 21, 3);
Assert.assertLt(0, result);
result = input1.compare(1, 3, input2, 23, 3);
Assert.assertGt(0, result);
result = input1.compare(0, 3, input2, 23, 5);
Assert.assertLt(0, result);
result = input1.compare(3, 1, input2, 23, 1);
Assert.assertEquals(0, result);
// The input1 not in buffer, input2 in buffer
input2.seek(20);
result = input1.compare(23, 5, input2, 23, 5);
Assert.assertEquals(0, result);
result = input1.compare(23, 12, input2, 23, 5);
Assert.assertGt(0, result);
// All not in buffer
input2.seek(0);
result = input1.compare(23, 5, input2, 23, 5);
Assert.assertEquals(0, result);
result = input1.compare(23, 12, input2, 23, 5);
Assert.assertGt(0, result);
// Compare with different class
UnsafeBytesOutput output = new UnsafeBytesOutput(20);
output.writeBytes("banana");
UnsafeBytesInput input = new UnsafeBytesInput(output.buffer());
output.close();
result = input1.compare(0, 2, input, 0, 4);
Assert.assertLt(0, result);
result = input1.compare(1, 5, input, 0, 4);
Assert.assertGt(0, result);
}
} finally {
FileUtils.deleteQuietly(dir);
}
}
private static byte[] orderBytesBySize(int size) {
E.checkArgument(size <= Byte.MAX_VALUE,
"Size must be <= %s but get '%s'",
Byte.MAX_VALUE, size);
byte[] bytes = new byte[size];
for (int i = 0; i < size; i++) {
bytes[i] = (byte) i;
}
return bytes;
}
private static void assertBytes(int expectSize, byte[] bytes) {
E.checkArgument(expectSize <= Byte.MAX_VALUE,
"Size must be <= %s but get '%s'",
Byte.MAX_VALUE, expectSize);
for (int i = 0; i < expectSize; i++) {
Assert.assertEquals(i, bytes[i]);
}
}
private static File createTempDir() {
File dir = new File(UUID.randomUUID().toString());
dir.mkdirs();
return dir;
}
private static void assertSeek(long fileLength, ValueFileInput input,
long position) throws IOException {
input.seek(position);
Assert.assertEquals(position, input.position());
Assert.assertEquals(fileLength - position, input.available());
}
}