package org.embulk.standards; import com.google.common.collect.ImmutableList; import org.embulk.EmbulkTestRuntime; import org.embulk.config.ConfigSource; import org.embulk.config.TaskSource; import org.embulk.spi.DataException; import org.embulk.spi.FileInput; import org.embulk.spi.ParserPlugin; import org.embulk.spi.Schema; import org.embulk.spi.TestPageBuilderReader.MockPageOutput; import org.embulk.spi.util.InputStreamFileInput; import org.embulk.spi.util.Pages; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.msgpack.value.Value; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.msgpack.value.ValueFactory.newArray; import static org.msgpack.value.ValueFactory.newBoolean; import static org.msgpack.value.ValueFactory.newInteger; import static org.msgpack.value.ValueFactory.newMap; import static org.msgpack.value.ValueFactory.newString; public class TestJsonParserPlugin { @Rule public EmbulkTestRuntime runtime = new EmbulkTestRuntime(); private ConfigSource config; private JsonParserPlugin plugin; private MockPageOutput output; @Before public void createResource() { config = config(); plugin = new JsonParserPlugin(); output = new MockPageOutput(); } @Test public void readNormalJson() throws Exception { transaction(config, fileInput( "{\"_c0\":true,\"_c1\":10,\"_c2\":\"embulk\",\"_c3\":{\"k\":\"v\"}}", "{}", "{\n" + "\"_c0\":false,\n" + "\"_c1\":-10,\n" + "\"_c2\":\"エンバルク\",\n" + "\"_c3\":[\"e0\",\"e1\"]\n" + "}", "[1, 2, 3]", // this line should be skipped. "\"embulk\"", // this line should be skipped. "10", // this line should be skipped. "true", // this line should be skipped. "false", // this line should be skipped. "null" // this line should be skipped. )); List<Object[]> records = Pages.toObjects(plugin.newSchema(), output.pages); assertEquals(3, records.size()); Object[] record; Map<Value, Value> map; { // "{\"_c0\":true,\"_c1\":10,\"_c2\":\"embulk\",\"_c3\":{\"k\":\"v\"}}" record = records.get(0); assertEquals(1, record.length); map = ((Value)record[0]).asMapValue().map(); assertEquals(newBoolean(true), map.get(newString("_c0"))); assertEquals(newInteger(10L), map.get(newString("_c1"))); assertEquals(newString("embulk"), map.get(newString("_c2"))); assertEquals(newMap(newString("k"), newString("v")), map.get(newString("_c3"))); } { // "{}" record = records.get(1); assertEquals(1, record.length); assertTrue(((Value)record[0]).asMapValue().map().isEmpty()); } { record = records.get(2); assertEquals(1, record.length); map = ((Value)record[0]).asMapValue().map(); assertEquals(newBoolean(false), map.get(newString("_c0"))); assertEquals(newInteger(-10L), map.get(newString("_c1"))); assertEquals(newString("エンバルク"), map.get(newString("_c2"))); assertEquals(newArray(newString("e0"), newString("e1")), map.get(newString("_c3"))); } } @Test public void useStopOnInvalidRecord() throws Exception { ConfigSource config = this.config.deepCopy().set("stop_on_invalid_record", true); try { transaction(config, fileInput( "[1, 2, 3]" // throw DataException )); fail(); } catch (Throwable t) { assertTrue(t instanceof DataException); } } @Test public void readBrokenJson() { try { transaction(config, fileInput( "{\"_c0\":true,\"_c1\":10," // throw DataException )); fail(); } catch (Throwable t) { assertTrue(t instanceof DataException); } } private ConfigSource config() { return runtime.getExec().newConfigSource(); } private void transaction(ConfigSource config, final FileInput input) { plugin.transaction(config, new ParserPlugin.Control() { @Override public void run(TaskSource taskSource, Schema schema) { plugin.run(taskSource, schema, input, output); } }); } private FileInput fileInput(String... lines) throws Exception { StringBuilder sb = new StringBuilder(); for (String line : lines) { sb.append(line).append("\n"); } ByteArrayInputStream in = new ByteArrayInputStream(sb.toString().getBytes("UTF-8")); return new InputStreamFileInput(runtime.getBufferAllocator(), provider(in)); } private InputStreamFileInput.IteratorProvider provider(InputStream... inputStreams) throws IOException { return new InputStreamFileInput.IteratorProvider( ImmutableList.copyOf(inputStreams)); } }