/**
* 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.tajo.parser.sql;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.tajo.algebra.Expr;
import org.apache.tajo.annotation.Nullable;
import org.apache.tajo.conf.TajoConf;
import org.apache.tajo.exception.SQLSyntaxError;
import org.apache.tajo.storage.StorageUtil;
import org.apache.tajo.util.FileUtil;
import org.apache.tajo.util.JavaResourceUtil;
import org.apache.tajo.util.Pair;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;
import static org.junit.Assert.*;
/**
* This class verifies SQLAnalyzer.
*/
public class TestSQLAnalyzer {
@Rule public TestName name = new TestName();
final static SQLAnalyzer analyzer = new SQLAnalyzer();
public static Expr parseQuery(String sql) throws SQLSyntaxError {
return analyzer.parse(sql);
}
public Collection<File> getResourceFiles(String subdir) throws URISyntaxException, IOException {
URL uri = ClassLoader.getSystemResource("queries/TestSQLAnalyzer");
Path positiveQueryDir = StorageUtil.concatPath(new Path(uri.toURI()), subdir);
FileSystem fs = positiveQueryDir.getFileSystem(new TajoConf());
if (!fs.exists(positiveQueryDir)) {
throw new IOException("Cannot find " + positiveQueryDir);
}
// get only files
Collection<FileStatus> files = filter(Lists.newArrayList(fs.listStatus(positiveQueryDir)),
new Predicate<FileStatus>() {
@Override
public boolean apply(@Nullable FileStatus input) {
// TODO: This should be removed at TAJO-1891
if (input.getPath().getName().indexOf("add_partition") > -1) {
return false;
} else {
return input.isFile();
}
}
}
);
// transform FileStatus into File
return transform(files, new Function<FileStatus, File>() {
@Override
public File apply(@Nullable FileStatus fileStatus) {
return new File(URI.create(fileStatus.getPath().toString()));
}
});
}
/**
* Return a pair of file name and SQL query
*
* @return a pair of file name and SQL query
* @throws IOException
* @throws URISyntaxException
*/
public Collection<Pair<String, String>> getFileContents(String subdir) throws IOException, URISyntaxException {
return transform(getResourceFiles(subdir), new Function<File, Pair<String, String>>() {
@Override
public Pair<String, String> apply(@Nullable File file) {
try {
return new Pair<>(file.getName(), FileUtil.readTextFile(file));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
/**
* In order to add more unit tests, please add SQL files into resources/results/TestSQLAnalyzer/positive.
* This test just checkes if SQL statements are parsed successfully.
*
* @throws IOException
* @throws URISyntaxException
*/
@Test
public void testPositiveTests() throws IOException, URISyntaxException {
for (Pair<String, String> pair : getFileContents("positive")) {
try {
assertNotNull(parseQuery(pair.getSecond()));
System.out.println(pair.getFirst() + " test passed...");
} catch (Throwable t) {
fail("Parsing '" + pair.getFirst() + "' failed, its cause: " + t.getMessage());
}
}
System.out.flush();
}
/**
* In order to add more unit tests, please add SQL files into resources/queries/TestSQLAnalyzer/errors.
* This test just checkes if the error messages caused by SQL parsing errors
*
* @throws IOException
* @throws URISyntaxException
*/
@Test
public void testErrorMessages() throws IOException, URISyntaxException {
for (Pair<String, String> pair : getFileContents("errors")) {
String fileName = pair.getFirst().split("\\.")[0];
String expectedResult = "";
try {
expectedResult =
JavaResourceUtil.readTextFromResource("results/TestSQLAnalyzer/errors/" + fileName + ".result");
} catch (FileNotFoundException fnfe) {
}
try {
Expr expr = parseQuery(pair.getSecond());
System.out.println(expr);
fail(pair.getFirst() + " must be failed.");
} catch (SQLSyntaxError e) {
assertEquals("Error message is different from " + fileName + ".result", expectedResult, e.getMessage());
}
System.out.println(pair.getFirst() + " test passed...");
}
System.out.flush();
}
/**
* In order to add more unit tests, add a SQL file including a SQL statement
* into the directory resources/queries/TestSQLAnalyzer
* and its generated algebraic expression formatted by json into resources/results/TestSQLAnalyzer.
* Each result file name must be the same to its SQL file.
*
* @throws IOException
* @throws URISyntaxException
*/
@Test
public void testGeneratedAlgebras() throws IOException, URISyntaxException {
for (Pair<String, String> pair : getFileContents(".")) {
Expr expr = null;
try {
expr = parseQuery(pair.getSecond());
} catch (SQLSyntaxError t) {
fail(t.getMessage());
}
String expectedResult = null;
String fileName = null;
try {
fileName = pair.getFirst().split("\\.")[0];
expectedResult = JavaResourceUtil.readTextFromResource("results/TestSQLAnalyzer/" + fileName + ".result");
} catch (FileNotFoundException ioe) {
expectedResult = "";
} catch (Throwable t) {
fail(t.getMessage());
}
assertEquals(pair.getFirst() + " is different from " + fileName + ".result",
expectedResult.trim(), expr.toJson().trim());
System.out.println(pair.getFirst() + " test passed..");
}
System.out.flush();
}
private static Expr parseExpr(String sql) {
ANTLRInputStream input = new ANTLRInputStream(sql);
SQLLexer lexer = new SQLLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
SQLParser parser = new SQLParser(tokens);
parser.setErrorHandler(new BailErrorStrategy());
parser.setBuildParseTree(true);
SQLAnalyzer visitor = new SQLAnalyzer();
SQLParser.Value_expressionContext context = parser.value_expression();
return visitor.visitValue_expression(context);
}
/**
* In order to add more unit tests, add text files including SQL expressions
* into the directory resources/queries/TestSQLAnalyzer/exprs.
*
* @throws IOException
* @throws URISyntaxException
*/
@Test
public void testExprs() throws IOException, URISyntaxException {
for (Pair<String, String> pair : getFileContents("exprs")) {
testExprs(pair.getFirst(), pair.getSecond());
System.out.println(pair.getFirst() + " test passed..");
}
System.out.flush();
}
private void testExprs(String file, String fileContents) {
for (String line : fileContents.split("\n")) {
try {
assertNotNull(parseExpr(line));
} catch (Throwable t) {
fail(line + " in " + file + " failed..");
}
}
}
}