/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.runtime.stage.input;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptID;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.task.TaskAttemptContextImpl;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import com.asakusafw.runtime.directio.hadoop.BlockInfo;
import com.asakusafw.runtime.directio.hadoop.BlockMap;
import com.asakusafw.runtime.io.ModelOutput;
import com.asakusafw.runtime.stage.temporary.TemporaryFile;
import com.asakusafw.runtime.stage.temporary.TemporaryStorage;
import com.asakusafw.runtime.util.hadoop.ConfigurationProvider;
import com.asakusafw.runtime.windows.WindowsSupport;
/**
* Test for {@link TemporaryInputFormat}.
*/
public class TemporaryInputFormatTest {
/**
* Windows platform support.
*/
@ClassRule
public static final WindowsSupport WINDOWS_SUPPORT = new WindowsSupport();
/**
* Temporary folder for testing.
*/
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
/**
* simple case for computing splits.
*/
@Test
public void splits_simple() {
BlockMap blocks = blocks("testing", m(10));
List<FileSplit> splits = TemporaryInputFormat.computeSplits(new Path("testing"), blocks, m(64));
assertThat(splits, hasSize(1));
FileSplit s0 = find(splits, 0);
assertThat(s0.getLength(), is(m(10)));
}
/**
* computing splits with already aligned blocks.
*/
@Test
public void splits_aligned() {
BlockMap blocks = blocks("testing", TemporaryFile.BLOCK_SIZE, TemporaryFile.BLOCK_SIZE);
List<FileSplit> splits = TemporaryInputFormat.computeSplits(new Path("testing"), blocks, m(64));
assertThat(splits, hasSize(2));
FileSplit s0 = find(splits, 0);
assertThat(s0.getLength(), is((long) TemporaryFile.BLOCK_SIZE));
FileSplit s1 = find(splits, TemporaryFile.BLOCK_SIZE);
assertThat(s1.getLength(), is((long) TemporaryFile.BLOCK_SIZE));
}
/**
* computing splits without unaligned blocks.
*/
@Test
public void splits_unaligned() {
BlockMap blocks = blocks("testing", TemporaryFile.BLOCK_SIZE - 10, TemporaryFile.BLOCK_SIZE);
List<FileSplit> splits = TemporaryInputFormat.computeSplits(new Path("testing"), blocks, m(128));
assertThat(splits, hasSize(2));
FileSplit s0 = find(splits, 0);
assertThat(s0.getLength(), is((long) TemporaryFile.BLOCK_SIZE));
FileSplit s1 = find(splits, TemporaryFile.BLOCK_SIZE);
assertThat(s1.getLength(), is((long) TemporaryFile.BLOCK_SIZE - 10));
}
/**
* computing splits with aligned blocks plus.
*/
@Test
public void splits_aligned_rest() {
BlockMap blocks = blocks("testing", TemporaryFile.BLOCK_SIZE, TemporaryFile.BLOCK_SIZE + 10);
List<FileSplit> splits = TemporaryInputFormat.computeSplits(new Path("testing"), blocks, m(64));
assertThat(splits, hasSize(2));
FileSplit s0 = find(splits, 0);
assertThat(s0.getLength(), is((long) TemporaryFile.BLOCK_SIZE));
FileSplit s1 = find(splits, TemporaryFile.BLOCK_SIZE);
assertThat(s1.getLength(), is((long) TemporaryFile.BLOCK_SIZE + 10));
}
/**
* computing splits with forcibly splitting.
*/
@Test
public void splits_force() {
BlockMap blocks = blocks("testing", TemporaryFile.BLOCK_SIZE * 10);
List<FileSplit> splits = TemporaryInputFormat.computeSplits(
new Path("testing"), blocks, TemporaryFile.BLOCK_SIZE + 1);
assertThat(splits, hasSize(5));
FileSplit s0 = find(splits, TemporaryFile.BLOCK_SIZE * 0);
assertThat(s0.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 2));
FileSplit s1 = find(splits, TemporaryFile.BLOCK_SIZE * 2);
assertThat(s1.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 2));
FileSplit s2 = find(splits, TemporaryFile.BLOCK_SIZE * 4);
assertThat(s2.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 2));
FileSplit s3 = find(splits, TemporaryFile.BLOCK_SIZE * 6);
assertThat(s3.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 2));
FileSplit s4 = find(splits, TemporaryFile.BLOCK_SIZE * 8);
assertThat(s4.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 2));
}
/**
* computing splits w/ suppress.
*/
@Test
public void splits_suppress() {
BlockMap blocks = blocks("testing", TemporaryFile.BLOCK_SIZE * 10);
List<FileSplit> splits = TemporaryInputFormat.computeSplits(new Path("testing"), blocks, 0);
assertThat(splits, hasSize(1));
FileSplit s0 = find(splits, 0);
assertThat(s0.getLength(), is((long) TemporaryFile.BLOCK_SIZE * 10));
}
/**
* Simple case for record readers.
* @throws Exception if failed
*/
@Test
public void reader_simple() throws Exception {
Configuration conf = new ConfigurationProvider().newInstance();
FileStatus stat = write(conf, 1);
try (RecordReader<NullWritable, Text> reader = TemporaryInputFormat.createRecordReader()) {
reader.initialize(
new FileSplit(stat.getPath(), 0, stat.getLen(), null),
new TaskAttemptContextImpl(conf, new TaskAttemptID()));
assertThat(reader.nextKeyValue(), is(true));
assertThat(reader.getCurrentValue(), is(new Text("Hello, world!")));
assertThat(reader.nextKeyValue(), is(false));
assertThat((double) reader.getProgress(), closeTo(1.0, 0.01));
}
}
private FileStatus write(Configuration conf, int count) throws IOException {
Path path = new Path(folder.newFile().toURI());
try (ModelOutput<Text> output = TemporaryStorage.openOutput(conf, Text.class, path)) {
Text buffer = new Text("Hello, world!");
for (int i = 0; i < count; i++) {
output.write(buffer);
}
}
return path.getFileSystem(conf).getFileStatus(path);
}
private long m(long value) {
return value * 1024 * 1024;
}
private FileSplit find(List<FileSplit> splits, long start) {
for (FileSplit split : splits) {
if (split.getStart() == start) {
return split;
}
}
throw new AssertionError(start);
}
private BlockMap blocks(String path, long... blockSizes) {
List<BlockInfo> blockList = new ArrayList<>();
long totalSize = 0;
for (long blockSize : blockSizes) {
long next = totalSize + blockSize;
blockList.add(new BlockInfo(totalSize, next, null));
totalSize = next;
}
return BlockMap.create(path, totalSize, blockList, false);
}
}