TestIntegration.java
/*
* 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 org.apache.arrow.tools;
import static org.apache.arrow.tools.ArrowFileTestFixtures.validateOutput;
import static org.apache.arrow.tools.ArrowFileTestFixtures.validateVariadicOutput;
import static org.apache.arrow.tools.ArrowFileTestFixtures.write;
import static org.apache.arrow.tools.ArrowFileTestFixtures.writeData;
import static org.apache.arrow.tools.ArrowFileTestFixtures.writeInput;
import static org.apache.arrow.tools.ArrowFileTestFixtures.writeVariableWidthViewInput;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter.NopIndenter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import java.util.Map;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.tools.Integration.Command;
import org.apache.arrow.vector.complex.NonNullableStructVector;
import org.apache.arrow.vector.complex.impl.ComplexWriterImpl;
import org.apache.arrow.vector.complex.writer.BaseWriter.ComplexWriter;
import org.apache.arrow.vector.complex.writer.BaseWriter.StructWriter;
import org.apache.arrow.vector.complex.writer.BigIntWriter;
import org.apache.arrow.vector.complex.writer.Float8Writer;
import org.apache.arrow.vector.complex.writer.IntWriter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class TestIntegration {
@TempDir public File testFolder;
private BufferAllocator allocator;
private ObjectMapper om = new ObjectMapper();
{
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentArraysWith(NopIndenter.instance);
om.setDefaultPrettyPrinter(prettyPrinter);
om.enable(SerializationFeature.INDENT_OUTPUT);
om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
}
static void writeInputFloat(File testInFile, BufferAllocator allocator, double... f)
throws IOException {
try (BufferAllocator vectorAllocator =
allocator.newChildAllocator("original vectors", 0, Integer.MAX_VALUE);
NonNullableStructVector parent = NonNullableStructVector.empty("parent", vectorAllocator)) {
ComplexWriter writer = new ComplexWriterImpl("root", parent);
StructWriter rootWriter = writer.rootAsStruct();
Float8Writer floatWriter = rootWriter.float8("float");
for (int i = 0; i < f.length; i++) {
floatWriter.setPosition(i);
floatWriter.writeFloat8(f[i]);
}
writer.setValueCount(f.length);
write(parent.getChild("root"), testInFile);
}
}
static void writeInput2(File testInFile, BufferAllocator allocator) throws IOException {
int count = ArrowFileTestFixtures.COUNT;
try (BufferAllocator vectorAllocator =
allocator.newChildAllocator("original vectors", 0, Integer.MAX_VALUE);
NonNullableStructVector parent = NonNullableStructVector.empty("parent", vectorAllocator)) {
writeData(count, parent);
ComplexWriter writer = new ComplexWriterImpl("root", parent);
StructWriter rootWriter = writer.rootAsStruct();
IntWriter intWriter = rootWriter.integer("int");
BigIntWriter bigIntWriter = rootWriter.bigInt("bigInt");
intWriter.setPosition(5);
intWriter.writeInt(999);
bigIntWriter.setPosition(4);
bigIntWriter.writeBigInt(777L);
writer.setValueCount(count);
write(parent.getChild("root"), testInFile);
}
}
@BeforeEach
public void init() {
allocator = new RootAllocator(Integer.MAX_VALUE);
}
@AfterEach
public void tearDown() {
allocator.close();
}
@Test
public void testValid() throws Exception {
File testInFile = new File(testFolder, "testIn.arrow");
File testJSONFile = new File(testFolder, "testOut.json");
testJSONFile.delete();
File testOutFile = new File(testFolder, "testOut.arrow");
testOutFile.delete();
// generate an arrow file
writeInput(testInFile, allocator);
Integration integration = new Integration();
// convert it to json
String[] args1 = {
"-arrow",
testInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args1);
// convert back to arrow
String[] args2 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.JSON_TO_ARROW.name()
};
integration.run(args2);
// check it is the same
validateOutput(testOutFile, allocator);
// validate arrow against json
String[] args3 = {
"-arrow",
testInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.VALIDATE.name()
};
integration.run(args3);
}
@Test
public void testJSONRoundTripWithVariableWidth() throws Exception {
URL resource = getClass().getResource("/integration_json_simple.json");
assertNotNull(resource);
File testJSONFile = new File(resource.getFile()).getCanonicalFile();
File testOutFile = new File(testFolder, "testOut.arrow");
File testRoundTripJSONFile = new File(testFolder, "testOut.json");
testOutFile.delete();
testRoundTripJSONFile.delete();
Integration integration = new Integration();
// convert to arrow
String[] args1 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.JSON_TO_ARROW.name()
};
integration.run(args1);
// convert back to json
String[] args2 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testRoundTripJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args2);
BufferedReader orig = readNormalized(testJSONFile);
BufferedReader rt = readNormalized(testRoundTripJSONFile);
String i;
String o;
int j = 0;
while ((i = orig.readLine()) != null && (o = rt.readLine()) != null) {
assertEquals(i, o, "line: " + j);
++j;
}
}
@Test
public void testJSONRoundTripWithStruct() throws Exception {
URL resource = getClass().getResource("/integration_json_struct.json");
assertNotNull(resource);
File testJSONFile = new File(resource.getFile()).getCanonicalFile();
File testOutFile = new File(testFolder, "testOutStruct.arrow");
File testRoundTripJSONFile = new File(testFolder, "testOutStruct.json");
testOutFile.delete();
testRoundTripJSONFile.delete();
Integration integration = new Integration();
// convert to arrow
String[] args1 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.JSON_TO_ARROW.name()
};
integration.run(args1);
// convert back to json
String[] args2 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testRoundTripJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args2);
BufferedReader orig = readNormalized(testJSONFile);
BufferedReader rt = readNormalized(testRoundTripJSONFile);
String i;
String o;
int j = 0;
while ((i = orig.readLine()) != null && (o = rt.readLine()) != null) {
assertEquals(i, o, "line: " + j);
++j;
}
}
private BufferedReader readNormalized(File f) throws IOException {
Map<?, ?> tree = om.readValue(f.getCanonicalFile(), Map.class);
String normalized = om.writeValueAsString(tree);
return new BufferedReader(new StringReader(normalized));
}
/** The test should not be sensitive to small variations in float representation. */
@Test
public void testFloat() throws Exception {
File testValidInFile = new File(testFolder, "testValidFloatIn.arrow");
File testInvalidInFile = new File(testFolder, "testAlsoValidFloatIn.arrow");
File testJSONFile = new File(testFolder, "testValidOut.json");
testJSONFile.delete();
// generate an arrow file
writeInputFloat(testValidInFile, allocator, 912.4140000000002, 912.414);
// generate a different arrow file
writeInputFloat(testInvalidInFile, allocator, 912.414, 912.4140000000002);
Integration integration = new Integration();
// convert the "valid" file to json
String[] args1 = {
"-arrow",
testValidInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args1);
// compare the "invalid" file to the "valid" json
String[] args3 = {
"-arrow",
testInvalidInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.VALIDATE.name()
};
// this should fail
integration.run(args3);
}
@Test
public void testInvalid() throws Exception {
File testValidInFile = new File(testFolder, "testValidIn.arrow");
File testInvalidInFile = new File(testFolder, "testInvalidIn.arrow");
File testJSONFile = new File(testFolder, "testInvalidOut.json");
testJSONFile.delete();
// generate an arrow file
writeInput(testValidInFile, allocator);
// generate a different arrow file
writeInput2(testInvalidInFile, allocator);
Integration integration = new Integration();
// convert the "valid" file to json
String[] args1 = {
"-arrow",
testValidInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args1);
// compare the "invalid" file to the "valid" json
String[] args3 = {
"-arrow",
testInvalidInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.VALIDATE.name()
};
// this should fail
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class,
() -> {
integration.run(args3);
});
assertTrue(e.getMessage().contains("Different values in column"), e.getMessage());
assertTrue(e.getMessage().contains("999"), e.getMessage());
}
@Test
public void testValidateVariableWidthView() throws Exception {
final int valueCount = 256;
final int multiplier = 6;
for (int i = 1; i < multiplier; i++) {
File testInFile = new File(testFolder, "testIn.arrow");
File testJSONFile = new File(testFolder, "testOut.json");
testJSONFile.delete();
File testOutFile = new File(testFolder, "testOut.arrow");
testOutFile.delete();
writeVariableWidthViewInput(testInFile, allocator, multiplier * valueCount);
Integration integration = new Integration();
// convert it to json
String[] args1 = {
"-arrow",
testInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.ARROW_TO_JSON.name()
};
integration.run(args1);
// convert back to arrow
String[] args2 = {
"-arrow",
testOutFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.JSON_TO_ARROW.name()
};
integration.run(args2);
// check it is the same
validateVariadicOutput(testOutFile, allocator, multiplier * valueCount);
// validate arrow against json
String[] args3 = {
"-arrow",
testInFile.getAbsolutePath(),
"-json",
testJSONFile.getAbsolutePath(),
"-command",
Command.VALIDATE.name()
};
integration.run(args3);
}
}
}