/* (c) 2014 LinkedIn Corp. All rights reserved.
*
* 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.
*/
package com.linkedin.cubert.operator;
import java.io.IOException;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.TupleFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.linkedin.cubert.block.BlockSchema;
import com.linkedin.cubert.block.ColumnType;
import com.linkedin.cubert.block.DataType;
import com.linkedin.cubert.functions.FunctionTree;
import com.linkedin.cubert.operator.PreconditionException;
public class TestFunctionTree
{
private final ObjectMapper mapper = new ObjectMapper();
private static final ColumnType INT_TYPE = new ColumnType(null, DataType.INT);
private static final ColumnType LONG_TYPE = new ColumnType(null, DataType.LONG);
private static final ColumnType FLOAT_TYPE = new ColumnType(null, DataType.FLOAT);
private static final ColumnType DOUBLE_TYPE = new ColumnType(null, DataType.DOUBLE);
JsonNode toJson(String str)
{
str = str.replaceAll("'", "\"");
try
{
return mapper.readValue(str, JsonNode.class);
}
catch (JsonParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (JsonMappingException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
JsonNode toJson(String template, Object... args)
{
return toJson(String.format(template, args));
}
@BeforeClass
public void setUp() throws JsonGenerationException,
JsonMappingException,
IOException
{
}
void doTest(BlockSchema inputSchema,
JsonNode json,
Object[][] data,
ColumnType expectedType,
Object[] expectedResults) throws PreconditionException,
IOException
{
FunctionTree tree = new FunctionTree(inputSchema);
tree.addFunctionTree(json);
ColumnType outType = tree.getType(0);
Assert.assertEquals(outType.getType(), expectedType.getType());
int ntuples = data.length;
int nfields = data[0].length;
Tuple tuple = TupleFactory.getInstance().newTuple(nfields);
for (int i = 0; i < ntuples; i++)
{
for (int j = 0; j < nfields; j++)
tuple.set(j, data[i][j]);
tree.attachTuple(tuple);
Object out = tree.evalTree(0);
Assert.assertEquals(out, expectedResults[i]);
}
}
void test(BlockSchema inputSchema,
JsonNode json,
Object[][] data,
ColumnType expectedType,
Object[] expectedResults)
{
try
{
doTest(inputSchema, json, data, expectedType, expectedResults);
}
catch (Exception e)
{
e.printStackTrace();
Assert.assertFalse(true);
}
}
void testFail(BlockSchema inputSchema,
JsonNode json,
Object[][] data,
ColumnType expectedType,
Object[] expectedResults)
{
try
{
doTest(inputSchema, json, data, expectedType, expectedResults);
Assert.assertFalse(true);
}
catch (PreconditionException e)
{
return;
}
catch (Exception e)
{
Assert.assertFalse(true);
}
}
@Test
void testInputProjection()
{
String template = "{'function': 'INPUT_PROJECTION', 'arguments': [%s]}";
JsonNode json;
BlockSchema schema = new BlockSchema("INT col1");
Object[][] data = new Object[][] { { 0 }, { null } };
Object[] expected = new Object[] { 0, null };
json = toJson(template, "'col1'");
test(schema, json, data, INT_TYPE, expected);
json = toJson(template, "0");
test(schema, json, data, INT_TYPE, expected);
json = toJson(template, "'col2'");
testFail(schema, json, data, INT_TYPE, expected);
json = toJson(template, "1");
testFail(schema, json, data, INT_TYPE, expected);
}
@Test
void testArithmetic()
{
String template =
"{'function': '%s', 'arguments': ["
+ "{'function': 'INPUT_PROJECTION', 'arguments': ['%s']},"
+ "{'function': 'INPUT_PROJECTION', 'arguments': ['%s']}]}";
JsonNode json;
BlockSchema schema = new BlockSchema("INT col1, INT col2");
Object[][] data =
new Object[][] { { 10, 20 }, { 10, null }, { null, 20 }, { null, null } };
// when both types are same
json = toJson(template, "ADD", "col1", "col2");
test(schema, json, data, INT_TYPE, new Object[] { 30, null, null, null });
// when the two types are not same
schema = new BlockSchema("int col1, long col2");
data =
new Object[][] { { 10, 20L }, { 10, null }, { null, 20L }, { null, null } };
json = toJson(template, "ADD", "col1", "col2");
test(schema, json, data, LONG_TYPE, new Object[] { 30L, null, null, null });
// when the two types are incompatible
schema = new BlockSchema("int col1, string col2");
testFail(schema, json, data, null, null);
// when the actual data is of less wider type
schema = new BlockSchema("double col1, double col2");
data = new Object[][] { { 10, 20L }, { 10L, 20f }, { 10f, 20d }, { 10d, 20 } };
json = toJson(template, "ADD", "col1", "col2");
test(schema, json, data, DOUBLE_TYPE, new Object[] { 30d, 30d, 30d, 30d });
}
}