[NO ISSUE][TEST] Store a query output for subsequent test calls
- user model changes: no
- storage format changes: no
- interface changes: no
Details:
- With this change, the test framework allow for a query to store
its output for access by subsequent calls. The change also allow
for a query to validate its output against a previous query.
- This change also fixes a bug in Function datasource field
access.
Change-Id: Iab329fbdf4def5b46686a61c9399520ae71518c4
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2235
Sonar-Qube: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: abdullah alamoudi <bamousaa@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushFieldAccessRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushFieldAccessRule.java
index e0fb1db..1766010 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushFieldAccessRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushFieldAccessRule.java
@@ -99,8 +99,8 @@
@SuppressWarnings("unchecked")
private boolean isAccessToIndexedField(AssignOperator assign, IOptimizationContext context)
throws AlgebricksException {
- AbstractFunctionCallExpression accessFun = (AbstractFunctionCallExpression) assign.getExpressions().get(0)
- .getValue();
+ AbstractFunctionCallExpression accessFun =
+ (AbstractFunctionCallExpression) assign.getExpressions().get(0).getValue();
ILogicalExpression e0 = accessFun.getArguments().get(0).getValue();
if (e0.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
return false;
@@ -161,8 +161,7 @@
if (op2.getOperatorTag() != LogicalOperatorTag.SELECT) {
return false;
}
- ILogicalExpression e1 = (ILogicalExpression) access.getAnnotations()
- .get(OperatorAnnotation.FIELD_ACCESS);
+ ILogicalExpression e1 = (ILogicalExpression) access.getAnnotations().get(OperatorAnnotation.FIELD_ACCESS);
if (e1 == null) {
return false;
}
@@ -215,8 +214,7 @@
pushItDown = true;
} else if (op2.getOperatorTag() == LogicalOperatorTag.GROUP) {
GroupByOperator g = (GroupByOperator) op2;
- List<Pair<LogicalVariable, LogicalVariable>> varMappings =
- new ArrayList<>();
+ List<Pair<LogicalVariable, LogicalVariable>> varMappings = new ArrayList<>();
for (Pair<LogicalVariable, Mutable<ILogicalExpression>> p : g.getDecorList()) {
ILogicalExpression e = p.second.getValue();
if (e.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
@@ -249,8 +247,8 @@
}
if (pushItDown) {
if (op2.getOperatorTag() == LogicalOperatorTag.NESTEDTUPLESOURCE) {
- Mutable<ILogicalOperator> childOfSubplan = ((NestedTupleSourceOperator) op2).getDataSourceReference()
- .getValue().getInputs().get(0);
+ Mutable<ILogicalOperator> childOfSubplan =
+ ((NestedTupleSourceOperator) op2).getDataSourceReference().getValue().getInputs().get(0);
pushAccessDown(opRef, op2, childOfSubplan, context, finalAnnot);
return true;
}
@@ -289,8 +287,8 @@
DataSourceScanOperator scan = (DataSourceScanOperator) op2;
int n = scan.getVariables().size();
LogicalVariable scanRecordVar = scan.getVariables().get(n - 1);
- AbstractFunctionCallExpression accessFun = (AbstractFunctionCallExpression) access.getExpressions()
- .get(0).getValue();
+ AbstractFunctionCallExpression accessFun =
+ (AbstractFunctionCallExpression) access.getExpressions().get(0).getValue();
ILogicalExpression e0 = accessFun.getArguments().get(0).getValue();
LogicalExpressionTag tag = e0.getExpressionTag();
if (tag == LogicalExpressionTag.VARIABLE) {
@@ -300,7 +298,8 @@
if (e1.getExpressionTag() == LogicalExpressionTag.CONSTANT) {
IDataSource<DataSourceId> dataSource = (IDataSource<DataSourceId>) scan.getDataSource();
byte dsType = ((DataSource) dataSource).getDatasourceType();
- if (dsType == DataSource.Type.FEED || dsType == DataSource.Type.LOADABLE) {
+ if (dsType == DataSource.Type.FEED || dsType == DataSource.Type.LOADABLE
+ || dsType == DataSource.Type.FUNCTION) {
return false;
}
DataSourceId asid = dataSource.getId();
@@ -375,7 +374,7 @@
fldAccessOpRef.setValue(op2);
List<Mutable<ILogicalOperator>> faInpList = fieldAccessOp.getInputs();
faInpList.clear();
- faInpList.add(new MutableObject<ILogicalOperator>(inputOfOp2.getValue()));
+ faInpList.add(new MutableObject<>(inputOfOp2.getValue()));
inputOfOp2.setValue(fieldAccessOp);
// typing
context.computeAndSetTypeEnvironmentForOperator(fieldAccessOp);
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsFunction.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsFunction.java
index 73b2d0e..1a461a5 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsFunction.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsFunction.java
@@ -45,7 +45,7 @@
INcApplicationContext appCtx = (INcApplicationContext) serviceCtx.getApplicationContext();
DatasetLifecycleManager dsLifecycleMgr = (DatasetLifecycleManager) appCtx.getDatasetLifecycleManager();
DatasetResource dsr = dsLifecycleMgr.getDatasetLifecycle(datasetId);
- return new StorageComponentsReader(dsr);
+ return new StorageComponentsReader(ctx.getJobletContext().getServiceContext().getNodeId(), dsr);
}
}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsReader.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsReader.java
index 4958d14..81f680d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsReader.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/function/StorageComponentsReader.java
@@ -40,7 +40,7 @@
private final Iterator<String> it;
private final CharArrayRecord record;
- public StorageComponentsReader(DatasetResource dsr) throws HyracksDataException {
+ public StorageComponentsReader(String nodeId, DatasetResource dsr) throws HyracksDataException {
components = new ArrayList<>();
if (dsr != null && dsr.isOpen()) {
Map<Long, IndexInfo> indexes = dsr.getIndexes();
@@ -51,7 +51,9 @@
ILSMIndex index = value.getIndex();
String path = value.getLocalResource().getPath();
strBuilder.append('{');
- strBuilder.append("\"path\":\"");
+ strBuilder.append("\"node\":\"");
+ strBuilder.append(nodeId);
+ strBuilder.append("\", \"path\":\"");
strBuilder.append(path);
strBuilder.append("\", \"components\":[");
// syncronize over the opTracker
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index d66c2e9..0b3a135 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -108,13 +108,13 @@
// see
// https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers/417184
private static final long MAX_URL_LENGTH = 2000l;
- private static final Pattern JAVA_BLOCK_COMMENT_PATTERN = Pattern.compile("/\\*.*\\*/",
- Pattern.MULTILINE | Pattern.DOTALL);
- private static final Pattern JAVA_SHELL_SQL_LINE_COMMENT_PATTERN = Pattern.compile("^(//|#|--).*$",
- Pattern.MULTILINE);
+ private static final Pattern JAVA_BLOCK_COMMENT_PATTERN =
+ Pattern.compile("/\\*.*\\*/", Pattern.MULTILINE | Pattern.DOTALL);
+ private static final Pattern JAVA_SHELL_SQL_LINE_COMMENT_PATTERN =
+ Pattern.compile("^(//|#|--).*$", Pattern.MULTILINE);
private static final Pattern REGEX_LINES_PATTERN = Pattern.compile("^(-)?/(.*)/([im]*)$");
- private static final Pattern POLL_TIMEOUT_PATTERN = Pattern.compile("polltimeoutsecs=(\\d+)(\\D|$)",
- Pattern.MULTILINE);
+ private static final Pattern POLL_TIMEOUT_PATTERN =
+ Pattern.compile("polltimeoutsecs=(\\d+)(\\D|$)", Pattern.MULTILINE);
private static final Pattern POLL_DELAY_PATTERN = Pattern.compile("polldelaysecs=(\\d+)(\\D|$)", Pattern.MULTILINE);
private static final Pattern HANDLE_VARIABLE_PATTERN = Pattern.compile("handlevariable=(\\w+)");
private static final Pattern VARIABLE_REF_PATTERN = Pattern.compile("\\$(\\w+)");
@@ -130,7 +130,6 @@
public static final String DIAGNOSE = "diagnose";
private static final String METRICS_QUERY_TYPE = "metrics";
-
private static final HashMap<Integer, ITestServer> runningTestServers = new HashMap<>();
private static Map<String, InetSocketAddress> ncEndPoints;
private static Map<String, InetSocketAddress> replicationAddress;
@@ -188,10 +187,10 @@
public void runScriptAndCompareWithResult(File scriptFile, PrintWriter print, File expectedFile, File actualFile,
ComparisonEnum compare) throws Exception {
System.err.println("Expected results file: " + expectedFile.toString());
- BufferedReader readerExpected = new BufferedReader(
- new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
- BufferedReader readerActual = new BufferedReader(
- new InputStreamReader(new FileInputStream(actualFile), "UTF-8"));
+ BufferedReader readerExpected =
+ new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
+ BufferedReader readerActual =
+ new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8"));
boolean regex = false;
try {
if (ComparisonEnum.BINARY.equals(compare)) {
@@ -374,10 +373,10 @@
public void runScriptAndCompareWithResultRegex(File scriptFile, File expectedFile, File actualFile)
throws Exception {
String lineExpected, lineActual;
- try (BufferedReader readerExpected = new BufferedReader(
- new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
- BufferedReader readerActual = new BufferedReader(
- new InputStreamReader(new FileInputStream(actualFile), "UTF-8"))) {
+ try (BufferedReader readerExpected =
+ new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
+ BufferedReader readerActual =
+ new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8"))) {
StringBuilder actual = new StringBuilder();
while ((lineActual = readerActual.readLine()) != null) {
actual.append(lineActual).append('\n');
@@ -721,8 +720,8 @@
// Insert and Delete statements are executed here
public void executeUpdate(String str, URI uri) throws Exception {
// Create a method instance.
- HttpUriRequest request = RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8))
- .build();
+ HttpUriRequest request =
+ RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8)).build();
// Execute the method.
executeAndCheckHttpRequest(request);
@@ -732,10 +731,10 @@
public InputStream executeAnyAQLAsync(String statement, boolean defer, OutputFormat fmt, URI uri,
Map<String, Object> variableCtx) throws Exception {
// Create a method instance.
- HttpUriRequest request = RequestBuilder.post(uri)
- .addParameter("mode", defer ? "asynchronous-deferred" : "asynchronous")
- .setEntity(new StringEntity(statement, StandardCharsets.UTF_8)).setHeader("Accept", fmt.mimeType())
- .build();
+ HttpUriRequest request =
+ RequestBuilder.post(uri).addParameter("mode", defer ? "asynchronous-deferred" : "asynchronous")
+ .setEntity(new StringEntity(statement, StandardCharsets.UTF_8))
+ .setHeader("Accept", fmt.mimeType()).build();
String handleVar = getHandleVariable(statement);
@@ -761,8 +760,8 @@
// create function statement
public void executeDDL(String str, URI uri) throws Exception {
// Create a method instance.
- HttpUriRequest request = RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8))
- .build();
+ HttpUriRequest request =
+ RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8)).build();
// Execute the method.
executeAndCheckHttpRequest(request);
@@ -772,8 +771,8 @@
// and returns the contents as a string
// This string is later passed to REST API for execution.
public String readTestFile(File testFile) throws Exception {
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(testFile), StandardCharsets.UTF_8));
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(new FileInputStream(testFile), StandardCharsets.UTF_8));
String line;
StringBuilder stringBuilder = new StringBuilder();
String ls = System.getProperty("line.separator");
@@ -803,8 +802,8 @@
private static String getProcessOutput(Process p) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Future<Integer> future = Executors.newSingleThreadExecutor()
- .submit(() -> IOUtils.copy(p.getInputStream(), new OutputStream() {
+ Future<Integer> future =
+ Executors.newSingleThreadExecutor().submit(() -> IOUtils.copy(p.getInputStream(), new OutputStream() {
@Override
public void write(int b) throws IOException {
baos.write(b);
@@ -899,6 +898,32 @@
expectedResultFile, actualResultFile, queryCount, expectedResultFileCtxs.size(),
cUnit.getParameter(), ComparisonEnum.TEXT);
break;
+ case "store":
+ // This is a query that returns the expected output of a subsequent query
+ String key = getKey(statement);
+ if (variableCtx.containsKey(key)) {
+ throw new IllegalStateException("There exist already a stored result with the key: " + key);
+ }
+ actualResultFile = new File(actualPath, testCaseCtx.getTestCase().getFilePath() + File.separatorChar
+ + cUnit.getName() + '.' + ctx.getSeqNum() + ".adm");
+ executeQuery(OutputFormat.forCompilationUnit(cUnit), statement, variableCtx, ctx.getType(), testFile,
+ null, actualResultFile, queryCount, expectedResultFileCtxs.size(), cUnit.getParameter(),
+ ComparisonEnum.TEXT);
+ variableCtx.put(key, actualResultFile);
+ break;
+ case "validate":
+ // This is a query that validates the output against a previously executed query
+ key = getKey(statement);
+ expectedResultFile = (File) variableCtx.remove(key);
+ if (expectedResultFile == null) {
+ throw new IllegalStateException("There is no stored result with the key: " + key);
+ }
+ actualResultFile = new File(actualPath, testCaseCtx.getTestCase().getFilePath() + File.separatorChar
+ + cUnit.getName() + '.' + ctx.getSeqNum() + ".adm");
+ executeQuery(OutputFormat.forCompilationUnit(cUnit), statement, variableCtx, ctx.getType(), testFile,
+ expectedResultFile, actualResultFile, queryCount, expectedResultFileCtxs.size(),
+ cUnit.getParameter(), ComparisonEnum.TEXT);
+ break;
case "txnqbc": // qbc represents query before crash
InputStream resultStream = executeQuery(statement, OutputFormat.forCompilationUnit(cUnit),
getEndpoint(Servlets.AQL_QUERY), cUnit.getParameter());
@@ -963,7 +988,7 @@
case "delete":
expectedResultFile =
(queryCount.intValue() < 0 || queryCount.intValue() >= expectedResultFileCtxs.size()) ? null
- : expectedResultFileCtxs.get(queryCount.intValue()).getFile();
+ : expectedResultFileCtxs.get(queryCount.intValue()).getFile();
actualResultFile = expectedResultFile == null ? null
: testCaseCtx.getActualResultFile(cUnit, expectedResultFile, new File(actualPath));
executeHttpRequest(OutputFormat.forCompilationUnit(cUnit), statement, variableCtx, ctx.getType(),
@@ -1213,15 +1238,20 @@
} else {
writeOutputToFile(actualResultFile, resultStream);
if (expectedResultFile == null) {
+ if (reqType.equals("store")) {
+ return;
+ }
Assert.fail("no result file for " + testFile.toString() + "; queryCount: " + queryCount
+ ", filectxs.size: " + numResultFiles);
}
- runScriptAndCompareWithResult(testFile, new PrintWriter(System.err), expectedResultFile, actualResultFile,
- compare);
- queryCount.increment();
- // Deletes the matched result file.
- actualResultFile.getParentFile().delete();
}
+ runScriptAndCompareWithResult(testFile, new PrintWriter(System.err), expectedResultFile, actualResultFile,
+ compare);
+ if (!reqType.equals("validate")) {
+ queryCount.increment();
+ }
+ // Deletes the matched result file.
+ actualResultFile.getParentFile().delete();
}
private void poll(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx,
@@ -1319,6 +1349,21 @@
}
}
+ public static String getKey(String statement) {
+ String key = "key=";
+ String[] lines = statement.split("\n");
+ for (String line : lines) {
+ if (line.contains(key)) {
+ String value = line.substring(line.indexOf(key) + key.length()).trim();
+ if (value.length() == 0) {
+ break;
+ }
+ return value;
+ }
+ }
+ throw new IllegalArgumentException("ERROR: key=<KEY> must be present in store/validate file");
+ }
+
public static int getRetryDelaySecs(String statement) {
final Matcher retryDelayMatcher = POLL_DELAY_PATTERN.matcher(statement);
return retryDelayMatcher.find() ? Integer.parseInt(retryDelayMatcher.group(1)) : 1;
@@ -1604,8 +1649,7 @@
}
if (!toBeDropped.isEmpty()) {
badtestcases.add(testCase);
- LOGGER.warn(
- "Last test left some garbage. Dropping dataverses: " + StringUtils.join(toBeDropped, ','));
+ LOGGER.warn("Last test left some garbage. Dropping dataverses: " + StringUtils.join(toBeDropped, ','));
StringBuilder dropStatement = new StringBuilder();
for (String dv : toBeDropped) {
dropStatement.append("drop dataverse ");
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.1.store.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.1.store.sqlpp
new file mode 100644
index 0000000..894837d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.1.store.sqlpp
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+-- key=someKey
+select * from `Metadata`.`Index`;
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.2.validate.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.2.validate.sqlpp
new file mode 100644
index 0000000..d257e9c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/validate-expected/validate-expected.2.validate.sqlpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+-- key=someKey
+select * from `Metadata`.`Index`;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 2c3d855..fd47766 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -3445,6 +3445,11 @@
</test-group>
<test-group name="misc">
<test-case FilePath="misc">
+ <compilation-unit name="validate-expected">
+ <output-dir compare="Text">validate-expected</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="misc">
<compilation-unit name="dataset-resources">
<output-dir compare="Text">dataset-resources</output-dir>
</compilation-unit>