blob: c0e01b1b159453e50ade580d412122e532085680 [file] [log] [blame]
/*
* 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.flink.types.parser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.flink.configuration.ConfigConstants;
import org.apache.flink.util.TestLogger;
import org.junit.Test;
/**
*
*/
public abstract class ParserTestBase<T> extends TestLogger {
public abstract String[] getValidTestValues();
public abstract T[] getValidTestResults();
public abstract String[] getInvalidTestValues();
public abstract boolean allowsEmptyField();
public abstract FieldParser<T> getParser();
public abstract Class<T> getTypeClass();
@Test
public void testTest() {
assertNotNull(getParser());
assertNotNull(getTypeClass());
assertNotNull(getValidTestValues());
assertNotNull(getValidTestResults());
assertNotNull(getInvalidTestValues());
assertTrue(getValidTestValues().length == getValidTestResults().length);
}
@Test
public void testGetValue() {
try {
FieldParser<?> parser = getParser();
Object created = parser.createValue();
assertNotNull("Null type created", created);
assertTrue("Wrong type created", getTypeClass().isAssignableFrom(created.getClass()));
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testValidStringInIsolation() {
try {
String[] testValues = getValidTestValues();
T[] results = getValidTestResults();
for (int i = 0; i < testValues.length; i++) {
FieldParser<T> parser1 = getParser();
FieldParser<T> parser2 = getParser();
FieldParser<T> parser3 = getParser();
byte[] bytes1 = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
byte[] bytes2 = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
byte[] bytes3 = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
int numRead1 = parser1.parseField(bytes1, 0, bytes1.length, new byte[] {'|'}, parser1.createValue());
int numRead2 = parser2.parseField(bytes2, 0, bytes2.length, new byte[] {'&', '&'}, parser2.createValue());
int numRead3 = parser3.parseField(bytes3, 0, bytes3.length, new byte[] {'9','9','9'}, parser3.createValue());
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", numRead1 != -1);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", numRead2 != -1);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", numRead3 != -1);
assertEquals("Invalid number of bytes read returned.", bytes1.length, numRead1);
assertEquals("Invalid number of bytes read returned.", bytes2.length, numRead2);
assertEquals("Invalid number of bytes read returned.", bytes3.length, numRead3);
T result1 = parser1.getLastResult();
T result2 = parser2.getLastResult();
T result3 = parser3.getLastResult();
assertEquals("Parser parsed wrong. "+testValues[i], results[i], result1);
assertEquals("Parser parsed wrong. "+testValues[i], results[i], result2);
assertEquals("Parser parsed wrong. "+testValues[i], results[i], result3);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testValidStringInIsolationWithEndDelimiter() {
try {
String[] testValues = getValidTestValues();
T[] results = getValidTestResults();
for (int i = 0; i < testValues.length; i++) {
FieldParser<T> parser1 = getParser();
FieldParser<T> parser2 = getParser();
String testVal1 = testValues[i] + "|";
String testVal2 = testValues[i] + "&&&&";
byte[] bytes1 = testVal1.getBytes(ConfigConstants.DEFAULT_CHARSET);
byte[] bytes2 = testVal2.getBytes(ConfigConstants.DEFAULT_CHARSET);
int numRead1 = parser1.parseField(bytes1, 0, bytes1.length, new byte[] {'|'}, parser1.createValue());
int numRead2 = parser2.parseField(bytes2, 0, bytes2.length, new byte[] {'&', '&','&', '&'}, parser2.createValue());
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", numRead1 != -1);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", numRead2 != -1);
assertEquals("Invalid number of bytes read returned.", bytes1.length, numRead1);
assertEquals("Invalid number of bytes read returned.", bytes2.length, numRead2);
T result1 = parser1.getLastResult();
T result2 = parser2.getLastResult();
assertEquals("Parser parsed wrong.", results[i], result1);
assertEquals("Parser parsed wrong.", results[i], result2);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testConcatenated() {
try {
String[] testValues = getValidTestValues();
T[] results = getValidTestResults();
byte[] allBytesWithDelimiter = concatenate(testValues, new char[] {'|'}, true);
byte[] allBytesNoDelimiterEnd = concatenate(testValues, new char[] {','}, false);
FieldParser<T> parser1 = getParser();
FieldParser<T> parser2 = getParser();
T val1 = parser1.createValue();
T val2 = parser2.createValue();
int pos1 = 0;
int pos2 = 0;
for (int i = 0; i < results.length; i++) {
pos1 = parser1.parseField(allBytesWithDelimiter, pos1, allBytesWithDelimiter.length, new byte[] {'|'}, val1);
pos2 = parser2.parseField(allBytesNoDelimiterEnd, pos2, allBytesNoDelimiterEnd.length, new byte[] {','}, val2);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", pos1 != -1);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", pos2 != -1);
T result1 = parser1.getLastResult();
T result2 = parser2.getLastResult();
assertEquals("Parser parsed wrong.", results[i], result1);
assertEquals("Parser parsed wrong.", results[i], result2);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testConcatenatedMultiCharDelimiter() {
try {
String[] testValues = getValidTestValues();
T[] results = getValidTestResults();
byte[] allBytesWithDelimiter = concatenate(testValues, new char[] {'&','&', '&', '&'}, true);
byte[] allBytesNoDelimiterEnd = concatenate(testValues, new char[] {'9', '9', '9'}, false);
FieldParser<T> parser1 = getParser();
FieldParser<T> parser2 = getParser();
T val1 = parser1.createValue();
T val2 = parser2.createValue();
int pos1 = 0;
int pos2 = 0;
for (int i = 0; i < results.length; i++) {
pos1 = parser1.parseField(allBytesWithDelimiter, pos1, allBytesWithDelimiter.length, new byte[] {'&','&','&','&'}, val1);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", pos1 != -1);
T result1 = parser1.getLastResult();
assertEquals("Parser parsed wrong.", results[i], result1);
pos2 = parser2.parseField(allBytesNoDelimiterEnd, pos2, allBytesNoDelimiterEnd.length, new byte[] {'9','9','9'}, val2);
assertTrue("Parser declared the valid value " + testValues[i] + " as invalid.", pos2 != -1);
T result2 = parser2.getLastResult();
assertEquals("Parser parsed wrong.", results[i], result2);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testInValidStringInIsolation() {
try {
String[] testValues = getInvalidTestValues();
for (int i = 0; i < testValues.length; i++) {
FieldParser<T> parser = getParser();
byte[] bytes = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
int numRead = parser.parseField(bytes, 0, bytes.length, new byte[]{'|'}, parser.createValue());
assertTrue("Parser accepted the invalid value " + testValues[i] + ".", numRead == -1);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testInValidStringsMixedIn() {
try {
String[] validValues = getValidTestValues();
T[] validResults = getValidTestResults();
String[] invalidTestValues = getInvalidTestValues();
FieldParser<T> parser = getParser();
T value = parser.createValue();
for (String invalid : invalidTestValues) {
// place an invalid string in the middle
String[] testLine = new String[validValues.length + 1];
int splitPoint = validValues.length / 2;
System.arraycopy(validValues, 0, testLine, 0, splitPoint);
testLine[splitPoint] = invalid;
System.arraycopy(validValues, splitPoint, testLine, splitPoint + 1, validValues.length - splitPoint);
byte[] bytes = concatenate(testLine, new char[] {'%'}, true);
// read the valid parts
int pos = 0;
for (int i = 0; i < splitPoint; i++) {
pos = parser.parseField(bytes, pos, bytes.length, new byte[] {'%'}, value);
assertTrue("Parser declared the valid value " + validValues[i] + " as invalid.", pos != -1);
T result = parser.getLastResult();
assertEquals("Parser parsed wrong.", validResults[i], result);
}
// fail on the invalid part
pos = parser.parseField(bytes, pos, bytes.length, new byte[] {'%'}, value);
assertTrue("Parser accepted the invalid value " + invalid + ".", pos == -1);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
@SuppressWarnings("unchecked")
public void testStaticParseMethod() {
try {
Method parseMethod = null;
try {
parseMethod = getParser().getClass().getMethod("parseField", byte[].class, int.class, int.class, char.class);
}
catch (NoSuchMethodException e) {
return;
}
String[] testValues = getValidTestValues();
T[] results = getValidTestResults();
for (int i = 0; i < testValues.length; i++) {
byte[] bytes = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
T result;
try {
result = (T) parseMethod.invoke(null, bytes, 0, bytes.length, '|');
}
catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
fail("Error while parsing: " + e.getTargetException().getMessage());
return;
}
assertEquals("Parser parsed wrong.", results[i], result);
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
@Test
public void testStaticParseMethodWithInvalidValues() {
try {
Method parseMethod = null;
try {
parseMethod = getParser().getClass().getMethod("parseField", byte[].class, int.class, int.class, char.class);
}
catch (NoSuchMethodException e) {
return;
}
String[] testValues = getInvalidTestValues();
for (int i = 0; i < testValues.length; i++) {
byte[] bytes = testValues[i].getBytes(ConfigConstants.DEFAULT_CHARSET);
try {
parseMethod.invoke(null, bytes, 0, bytes.length, '|');
fail("Static parse method accepted invalid value");
}
catch (InvocationTargetException e) {
// that's it!
}
}
}
catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
private static byte[] concatenate(String[] values, char[] delimiter, boolean delimiterAtEnd) {
int len = 0;
for (String s : values) {
len += s.length() + delimiter.length;
}
if (!delimiterAtEnd) {
len-=delimiter.length;
}
int currPos = 0;
byte[] result = new byte[len];
for (int i = 0; i < values.length; i++) {
String s = values[i];
byte[] bytes = s.getBytes(ConfigConstants.DEFAULT_CHARSET);
int numBytes = bytes.length;
System.arraycopy(bytes, 0, result, currPos, numBytes);
currPos += numBytes;
if (delimiterAtEnd || i < values.length-1) {
for(int j = 0; j < delimiter.length; j++)
result[currPos++] = (byte) delimiter[j];
}
}
return result;
}
@Test
public void testTrailingEmptyField() {
try {
FieldParser<T> parser = getParser();
byte[] bytes = "||".getBytes(ConfigConstants.DEFAULT_CHARSET);
for (int i = 0; i < 2; i++) {
// test empty field with trailing delimiter when i = 0,
// test empty field with out trailing delimiter when i= 1.
int numRead = parser.parseField(bytes, i, bytes.length, new byte[]{'|'}, parser.createValue());
assertEquals(FieldParser.ParseErrorState.EMPTY_COLUMN, parser.getErrorState());
if (this.allowsEmptyField()) {
assertTrue("Parser declared the empty string as invalid.", numRead != -1);
assertEquals("Invalid number of bytes read returned.", i + 1, numRead);
} else {
assertTrue("Parser accepted the empty string.", numRead == -1);
}
parser.resetParserState();
}
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
fail("Test erroneous: " + e.getMessage());
}
}
}