Repository: asterixdb Updated Branches: refs/heads/master 71e4b4c00 -> 90883159b
[NO ISSUE][TEST] Introduce dynamic expected result poll test type - user model changes: no - storage format changes: no - interface changes: no Details: - This change allows for a new type of tests. The new test can poll for both expected results and a query and compare the two values. - A test case is added. Change-Id: Ifc132b2d2286eea1d1e119984c33ca5eef9be92a Reviewed-on: https://asterix-gerrit.ics.uci.edu/2438 Sonar-Qube: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Reviewed-by: abdullah alamoudi <bamou...@gmail.com> Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/90883159 Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/90883159 Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/90883159 Branch: refs/heads/master Commit: 90883159b99f12d98b648dff112605863822a0ac Parents: 71e4b4c Author: Abdullah Alamoudi <bamou...@gmail.com> Authored: Thu Mar 1 15:56:00 2018 -0800 Committer: abdullah alamoudi <bamou...@gmail.com> Committed: Thu Mar 1 21:35:24 2018 -0800 ---------------------------------------------------------------------- .../test/common/IExpectedResultPoller.java | 27 +++++ .../apache/asterix/test/common/IPollTask.java | 51 ++++++++ .../asterix/test/common/TestExecutor.java | 116 ++++++++++++++++--- .../poll-dynamic.1.polldynamic.sqlpp | 22 ++++ .../resources/runtimets/testsuite_sqlpp.xml | 5 + 5 files changed, 203 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/asterixdb/blob/90883159/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IExpectedResultPoller.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IExpectedResultPoller.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IExpectedResultPoller.java new file mode 100644 index 0000000..ce2760d --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IExpectedResultPoller.java @@ -0,0 +1,27 @@ +/* + * 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.asterix.test.common; + +@FunctionalInterface +public interface IExpectedResultPoller { + /** + * @return the expected result as a string + */ + String poll() throws Exception; +} http://git-wip-us.apache.org/repos/asf/asterixdb/blob/90883159/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java new file mode 100644 index 0000000..6d32518 --- /dev/null +++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/IPollTask.java @@ -0,0 +1,51 @@ +/* + * 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.asterix.test.common; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.apache.asterix.testframework.context.TestCaseContext; +import org.apache.asterix.testframework.context.TestFileContext; +import org.apache.asterix.testframework.xml.TestCase.CompilationUnit; +import org.apache.commons.lang3.mutable.MutableInt; + +public interface IPollTask { + + /** + * Execute the poll task + * + * @param testCaseCtx + * @param ctx + * @param variableCtx + * @param statement + * @param isDmlRecoveryTest + * @param pb + * @param cUnit + * @param queryCount + * @param expectedResultFileCtxs + * @param testFile + * @param actualPath + */ + void execute(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx, String statement, + boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit, MutableInt queryCount, + List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath) throws Exception; + +} http://git-wip-us.apache.org/repos/asf/asterixdb/blob/90883159/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java ---------------------------------------------------------------------- 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 e39a9f3..48603a5 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 @@ -50,6 +50,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; @@ -129,6 +130,12 @@ public class TestExecutor { public static final Set<String> NON_CANCELLABLE = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("store", "validate"))); + private final IPollTask plainExecutor = (testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, + queryCount, expectedResultFileCtxs, testFile, actualPath) -> { + executeTestFile(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, + expectedResultFileCtxs, testFile, actualPath); + }; + public static final String DELIVERY_ASYNC = "async"; public static final String DELIVERY_DEFERRED = "deferred"; public static final String DELIVERY_IMMEDIATE = "immediate"; @@ -886,6 +893,11 @@ public class TestExecutor { case "pollget": case "pollquery": poll(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, + expectedResultFileCtxs, testFile, actualPath, ctx.getType().substring("poll".length()), + plainExecutor); + break; + case "polldynamic": + polldynamic(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, expectedResultFileCtxs, testFile, actualPath); break; case "query": @@ -923,16 +935,8 @@ public class TestExecutor { 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); + validate(actualPath, testCaseCtx, cUnit, statement, variableCtx, testFile, ctx, queryCount, + expectedResultFileCtxs); break; case "txnqbc": // qbc represents query before crash InputStream resultStream = executeQuery(statement, OutputFormat.forCompilationUnit(cUnit), @@ -1158,6 +1162,21 @@ public class TestExecutor { } } + private void validate(String actualPath, TestCaseContext testCaseCtx, CompilationUnit cUnit, String statement, + Map<String, Object> variableCtx, File testFile, TestFileContext ctx, MutableInt queryCount, + List<TestFileContext> expectedResultFileCtxs) throws Exception { + String key = getKey(statement); + File expectedResultFile = (File) variableCtx.remove(key); + if (expectedResultFile == null) { + throw new IllegalStateException("There is no stored result with the key: " + key); + } + File actualResultFile = new File(actualPath, testCaseCtx.getTestCase().getFilePath() + File.separatorChar + + cUnit.getName() + '.' + ctx.getSeqNum() + ".adm"); + executeQuery(OutputFormat.forCompilationUnit(cUnit), statement, variableCtx, "validate", testFile, + expectedResultFile, actualResultFile, queryCount, expectedResultFileCtxs.size(), cUnit.getParameter(), + ComparisonEnum.TEXT); + } + protected void executeHttpRequest(OutputFormat fmt, String statement, Map<String, Object> variableCtx, String reqType, File testFile, File expectedResultFile, File actualResultFile, MutableInt queryCount, int numResultFiles, String extension, ComparisonEnum compare) throws Exception { @@ -1259,29 +1278,88 @@ public class TestExecutor { actualResultFile.getParentFile().delete(); } - private void poll(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx, + private void polldynamic(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx, String statement, boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit, MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath) throws Exception { + IExpectedResultPoller poller = getExpectedResultPoller(statement); + final String key = getKey(statement); + poll(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, queryCount, expectedResultFileCtxs, + testFile, actualPath, "validate", new IPollTask() { + @Override + public void execute(TestCaseContext testCaseCtx, TestFileContext ctx, + Map<String, Object> variableCtx, String statement, boolean isDmlRecoveryTest, + ProcessBuilder pb, CompilationUnit cUnit, MutableInt queryCount, + List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath) + throws Exception { + File actualResultFile = new File(actualPath, testCaseCtx.getTestCase().getFilePath() + + File.separatorChar + cUnit.getName() + '.' + ctx.getSeqNum() + ".polled.adm"); + if (actualResultFile.exists() && !actualResultFile.delete()) { + throw new Exception( + "Failed to delete an existing result file: " + actualResultFile.getAbsolutePath()); + } + writeOutputToFile(actualResultFile, + new ByteArrayInputStream(poller.poll().getBytes(StandardCharsets.UTF_8))); + variableCtx.put(key, actualResultFile); + validate(actualPath, testCaseCtx, cUnit, statement, variableCtx, testFile, ctx, queryCount, + expectedResultFileCtxs); + } + }); + } + + protected IExpectedResultPoller getExpectedResultPoller(String statement) { + String key = "poller="; + String value = null; + String[] lines = statement.split("\n"); + for (String line : lines) { + if (line.contains(key)) { + value = line.substring(line.indexOf(key) + key.length()).trim(); + } + } + if (value == null) { + throw new IllegalArgumentException("ERROR: poller=<...> must be present in poll-dynamic file"); + } + String staticPoller = "static:"; + if (value.startsWith(staticPoller)) { + String polled = value.substring(staticPoller.length()); + return () -> polled; + } + throw new IllegalArgumentException("ERROR: unknown poller: " + value); + } + + private void poll(TestCaseContext testCaseCtx, TestFileContext ctx, Map<String, Object> variableCtx, + String statement, boolean isDmlRecoveryTest, ProcessBuilder pb, CompilationUnit cUnit, + MutableInt queryCount, List<TestFileContext> expectedResultFileCtxs, File testFile, String actualPath, + String newType, IPollTask pollTask) throws Exception { // polltimeoutsecs=nnn, polldelaysecs=nnn int timeoutSecs = getTimeoutSecs(statement); int retryDelaySecs = getRetryDelaySecs(statement); long startTime = System.currentTimeMillis(); long limitTime = startTime + TimeUnit.SECONDS.toMillis(timeoutSecs); - ctx.setType(ctx.getType().substring("poll".length())); + Semaphore endSemaphore = new Semaphore(1); + final ExecutorService executorService = Executors.newSingleThreadExecutor(); + String originalType = ctx.getType(); + ctx.setType(newType); try { boolean expectedException = false; Exception finalException = null; LOGGER.debug("polling for up to " + timeoutSecs + " seconds w/ " + retryDelaySecs + " second(s) delay"); int responsesReceived = 0; - final ExecutorService executorService = Executors.newSingleThreadExecutor(); while (true) { try { + endSemaphore.acquire(); + Semaphore startSemaphore = new Semaphore(0); Future<Void> execution = executorService.submit(() -> { - executeTestFile(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, - queryCount, expectedResultFileCtxs, testFile, actualPath); + try { + startSemaphore.release(); + pollTask.execute(testCaseCtx, ctx, variableCtx, statement, isDmlRecoveryTest, pb, cUnit, + queryCount, expectedResultFileCtxs, testFile, actualPath); + } finally { + endSemaphore.release(); + } return null; }); + startSemaphore.acquire(); execution.get(limitTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); responsesReceived++; finalException = null; @@ -1334,11 +1412,13 @@ public class TestExecutor { throw new Exception("Poll limit (" + timeoutSecs + "s) exceeded without obtaining expected result", finalException); } - } finally { - ctx.setType("poll" + ctx.getType()); + executorService.shutdownNow(); + // ensure no leftover task is running. This avoids re-polling due to + // resetting the ctx type to poll while the last attempt is being made + endSemaphore.acquire(); + ctx.setType(originalType); } - } public InputStream executeSqlppUpdateOrDdl(String statement, OutputFormat outputFormat) throws Exception { http://git-wip-us.apache.org/repos/asf/asterixdb/blob/90883159/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/poll-dynamic/poll-dynamic.1.polldynamic.sqlpp ---------------------------------------------------------------------- diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/poll-dynamic/poll-dynamic.1.polldynamic.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/poll-dynamic/poll-dynamic.1.polldynamic.sqlpp new file mode 100644 index 0000000..e8dc9e8 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/misc/poll-dynamic/poll-dynamic.1.polldynamic.sqlpp @@ -0,0 +1,22 @@ +/* + * 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=aKey +-- poller=static:{ "one": 1 } +-- polltimeoutsecs=120 +select 1 as one; http://git-wip-us.apache.org/repos/asf/asterixdb/blob/90883159/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml ---------------------------------------------------------------------- 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 09fc832..dd1ae0c 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -3538,6 +3538,11 @@ </test-group> <test-group name="misc"> <test-case FilePath="misc"> + <compilation-unit name="poll-dynamic"> + <output-dir compare="Text">poll-dynamic</output-dir> + </compilation-unit> + </test-case> + <test-case FilePath="misc"> <compilation-unit name="validate-expected"> <output-dir compare="Text">validate-expected</output-dir> </compilation-unit>