Java tutorial
/** * Copyright 2010-2016 interactive instruments GmbH * * 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. * See the License for the specific language governing permissions and * limitations under the License. */ package de.interactive_instruments.etf.sel.mapping; import static de.interactive_instruments.etf.dal.dto.result.TestResultStatus.UNDEFINED; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.*; import com.eviware.soapui.impl.support.AbstractHttpRequest; import com.eviware.soapui.impl.support.http.HttpRequestTestStep; import com.eviware.soapui.impl.wsdl.WsdlProject; import com.eviware.soapui.impl.wsdl.support.PathUtils; import com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequestStep; import com.eviware.soapui.impl.wsdl.teststeps.RestTestRequestStep; import com.eviware.soapui.impl.wsdl.teststeps.WsdlRunTestCaseTestStep; import com.eviware.soapui.impl.wsdl.teststeps.assertions.basic.XQueryContainsAssertion; import com.eviware.soapui.model.TestPropertyHolder; import com.eviware.soapui.model.project.Project; import com.eviware.soapui.model.propertyexpansion.PropertyExpander; import com.eviware.soapui.model.testsuite.*; import com.eviware.soapui.model.testsuite.AssertionError; import com.eviware.soapui.plugins.ListenerConfiguration; import de.interactive_instruments.SUtils; import de.interactive_instruments.UriUtils; import de.interactive_instruments.exceptions.ExcUtils; import org.apache.commons.io.IOUtils; import de.interactive_instruments.IFile; import de.interactive_instruments.etf.sel.Utils; import de.interactive_instruments.etf.testdriver.TestResultCollector; /** * @author J. Herrmann ( herrmann <aT) interactive-instruments (doT> de ) */ @ListenerConfiguration public class TestRunCollector implements TestRunListener { private TestResultCollector collector; private IFile tmpDir; /** * @see de.interactive_instruments.etf.dal.dto.result.TestResultStatus */ private enum TestResultStatus { PASSED(0), FAILED(1), PASSED_MANUAL(7); public final int value; TestResultStatus(final int value) { this.value = value; } } private TestResultStatus testAssertionStatus = TestResultStatus.PASSED; private TestResultStatus testStepStatus = TestResultStatus.PASSED; private void setManualOrError(final boolean manual) { if (manual) { testAssertionStatus = TestResultStatus.PASSED_MANUAL; if (testStepStatus != TestResultStatus.FAILED) { testStepStatus = TestResultStatus.PASSED_MANUAL; } } else { setFailed(); } } private void setFailed() { testAssertionStatus = TestResultStatus.FAILED; testStepStatus = TestResultStatus.FAILED; } // called by SoapUI GUI public TestRunCollector() { } // called by Test Driver public TestRunCollector(final TestResultCollector collector) { this.collector = collector; this.tmpDir = new IFile(collector.getTempDir()); } @Override public void beforeRun(final TestCaseRunner testCaseRunner, final TestCaseRunContext testCaseRunContext) { if (collector == null) { final Project project = testCaseRunner.getTestCase().getTestSuite().getProject(); if (project instanceof WsdlProject && ((WsdlProject) project).getActiveEnvironment() instanceof CollectorInjectionAdapter) { collector = ((CollectorInjectionAdapter) ((WsdlProject) project).getActiveEnvironment()) .getTestResultCollector(); this.tmpDir = new IFile(collector.getTempDir()); try { this.tmpDir = IFile.createTempDir("sel"); } catch (IOException e) { collector.internalError(e); } } else { collector = new DummyCollector(); } } testAssertionStatus = TestResultStatus.PASSED; testStepStatus = TestResultStatus.PASSED; collector.startTestCase(testCaseRunner.getTestCase().getId()); } @Override public void afterRun(final TestCaseRunner testCaseRunner, final TestCaseRunContext testCaseRunContext) { // Check if the collector is still in the writing test step state if (collector.currentModelType() == 4) { collector.error("Exception occurred in Test Step: " + testCaseRunner.getReason()); // end step (we need the id if we are in a sub collector context collector.end(testCaseRunner.getRunContext().getCurrentStep().getId(), UNDEFINED.value()); } collector.end(testCaseRunner.getTestCase().getId()); } @Override public void beforeStep(final TestCaseRunner testCaseRunner, final TestCaseRunContext testCaseRunContext) { // deprecated } @Override public void beforeStep(final TestCaseRunner testCaseRunner, final TestCaseRunContext testCaseRunContext, final TestStep testStep) { Objects.requireNonNull(collector, "Collector not initialized before test step") .startTestStep(testStep.getId()); if (testStep instanceof HttpTestRequestStep || testStep instanceof RestTestRequestStep) { final HttpRequestTestStep testRequest = (HttpRequestTestStep) testStep; if (testRequest.getHttpRequest() instanceof AbstractHttpRequest) { final AbstractHttpRequest httpRequest = testRequest.getHttpRequest(); final IFile dumpFile = new IFile(collector.getTempDir(), "dumpFile-" + UUID.randomUUID().toString()); httpRequest.setDumpFile(dumpFile.getAbsolutePath()); // 2GB httpRequest.setMaxSize(2147483648L); } } } /** * Returns true if message is a manual test instruction */ private static boolean addMessage(final String message, final TestResultCollector collector, final int i) { final int startIndex = message.indexOf("<etfTranslate "); final int endIndex = message.lastIndexOf("</etfTranslate>"); if (startIndex != -1) { final int wi = message.indexOf("what='", startIndex + 14); final boolean singleQ = wi > -1; final int whatIndex = singleQ ? wi : message.indexOf("what=\"", startIndex + 14); if (whatIndex != -1) { final int endWhatIndex = message.indexOf(singleQ ? "'" : "\"", whatIndex + 6); if (whatIndex < endWhatIndex) { final String t = message.substring(whatIndex + 6, endWhatIndex); final String translationTemplate = t.startsWith("TR.") ? t : "TR." + t; final boolean manual = translationTemplate.startsWith("TR.manual."); if (endIndex > 0) { // Check that are no >s in what final int messagesIndex = message.indexOf(">", startIndex + 1); if (whatIndex < messagesIndex && messagesIndex < endIndex) { final Map<String, String> tokenArguments = new TreeMap<>(); parseRec(message, messagesIndex + 1, endIndex, tokenArguments); if (!tokenArguments.isEmpty()) { // if there is only one tokenArgument combination with an INFO token, // change the template to TR.INFO if (tokenArguments.size() == 1 && tokenArguments.containsKey("INFO")) { // Add message with INFO message collector.addMessage("TR.fallbackInfo", tokenArguments); } else { // Add message with token/arguments collector.addMessage(translationTemplate, tokenArguments); } } else { // Add message without tokenArguments collector.addMessage(translationTemplate); } return manual; } } else { collector.addMessage(translationTemplate); return manual; } } } } try { // TODO temporary fallback final Map<String, String> map = new HashMap<String, String>() { { put("INFO", message); } }; collector.addMessage("TR.fallbackInfo", map); collector.saveAttachment(IOUtils.toInputStream(message, "UTF-8"), "Error." + i, "text/plain", "Error"); } catch (IOException e) { collector.internalError(e); } return false; } /** * Existing arguments are appended (comma-separated)! * * Ignores: PASS and FAIL * * @param message * @param parseIndex * @param maxIndex * @param tokenArguments */ private static void parseRec(final String message, final int parseIndex, final int maxIndex, final Map<String, String> tokenArguments) { final int startIndex = message.indexOf("<", parseIndex); if (startIndex != -1) { final int closingTagSignIndex = message.indexOf(">", startIndex + 2); if (closingTagSignIndex != -1 && message.charAt(closingTagSignIndex - 1) != '/') { final int firstAttributeInTag = message.indexOf(" ", startIndex + 2); final int tokenNameEndIndex = firstAttributeInTag > startIndex && firstAttributeInTag < closingTagSignIndex ? firstAttributeInTag : closingTagSignIndex; final String token = message.substring(startIndex + 1, tokenNameEndIndex); if (!SUtils.isNullOrEmpty(token) && !token.equals("PASS") && !token.equals("FAIL")) { // Argument ends with the token final int argumentEndIndex = message.indexOf("</" + token + ">", closingTagSignIndex); if (tokenNameEndIndex < argumentEndIndex) { // Arguments begins after closing tag sign final String argument = message.substring(closingTagSignIndex + 1, argumentEndIndex); // Support listing, by appending an argument to a token that already exists final String existingArgument = tokenArguments.get(token); if (existingArgument != null) { // there is already an existing argument. Append non null, unique argument if (!SUtils.isNullOrEmpty(argument) && !argument.equals(existingArgument)) { tokenArguments.put(token, argument + ", " + existingArgument); } } else { // Add non empty argument or write the string "[not found in response]" if (!SUtils.isNullOrEmpty(argument)) { tokenArguments.put(token, argument); } else { tokenArguments.put(token, "[ empty value (invalid response?) ]"); } } final int completeEndIndex = argumentEndIndex + token.length(); if (completeEndIndex + 15 < maxIndex) { parseRec(message, completeEndIndex + 1, maxIndex, tokenArguments); } } } } } } @Override public void afterStep(final TestCaseRunner testCaseRunner, final TestCaseRunContext testCaseRunContext, final TestStepResult testStepResult) { Objects.requireNonNull(collector, "Collector not initialized after test step"); testStepStatus = TestResultStatus.PASSED; int status = -1; if (testStepResult.getTestStep() instanceof HttpTestRequestStep || testStepResult.getTestStep() instanceof RestTestRequestStep) { final HttpRequestTestStep testRequest = (HttpRequestTestStep) testStepResult.getTestStep(); final AbstractHttpRequest httpRequest; if (testRequest.getHttpRequest() instanceof AbstractHttpRequest) { httpRequest = (AbstractHttpRequest) testRequest.getHttpRequest(); } else { httpRequest = null; } // Endpoint final String endpoint; if (testRequest.hasProperty("Endpoint")) { endpoint = UriUtils.withoutQueryParameters(PropertyExpander.expandProperties(testRequest, testRequest.getProperty("Endpoint").getValue())); try { collector.saveAttachment(endpoint, "Endpoint", "text/plain", "ServiceEndpoint"); } catch (IOException e) { collector.internalError(e); } } else { endpoint = null; } if (httpRequest != null) { if (httpRequest.getDumpFile() != null) { final IFile file = new IFile( PathUtils.resolveResourcePath(httpRequest.getDumpFile(), httpRequest)); if (file.exists() && file.length() > 0) { try { final IFile copied = file.copyTo( tmpDir.secureExpandPathDown("response-" + testStepResult.getTestStep().getId()) .toString()); collector.markAttachment(copied.getName(), "Service Response", "UTF-8", null, "ServiceResponse"); } catch (IOException e) { collector.internalError(e); } } else { // collector.getLogger().info("Received empty response"); } } else { try { collector.saveAttachment(httpRequest.getResponse().getContentAsString(), "Service Response", null, "ServiceResponse"); } catch (final IOException e) { collector.internalError(e); } } } // Request if (testRequest.hasProperty("Request")) { // GET, PUT, ... if (!testRequest.getProperty("Request").getValue().isEmpty()) { // POST final String endpointText = !SUtils.isNullOrEmpty(endpoint) ? "Endpoint: " + SUtils.ENDL + PropertyExpander.expandProperties(testRequest, testRequest.getPropertyValue("Endpoint")) + SUtils.ENDL : ""; final String addRequestInfo = PropertyExpander.expandProperties(testRequest, "<!-- " + SUtils.ENDL + endpointText + "RequestHeaders: " + SUtils.ENDL + testRequest.getHttpRequest().getRequestHeaders().toString() + SUtils.ENDL + "-->" + SUtils.ENDL); final String expandedProperties = PropertyExpander.expandProperties(testRequest, addRequestInfo + testRequest.getProperty("Request").getValue()); try { collector.saveAttachment(IOUtils.toInputStream(expandedProperties, "UTF-8"), "Request Parameter", null, "PostParameter"); } catch (final IOException e) { ExcUtils.suppress(e); } } if (httpRequest != null && httpRequest instanceof TestPropertyHolder) { final List<TestProperty> propertyList = ((TestPropertyHolder) httpRequest).getPropertyList(); final Map<String, String> parameterMap = new HashMap<>(); for (final TestProperty testProperty : propertyList) { parameterMap.put(testProperty.getName(), testProperty.getValue()); } final String query = UriUtils.withQueryParameters(endpoint, parameterMap, true); if (!SUtils.isNullOrEmpty(query)) { try { collector.saveAttachment(PropertyExpander.expandProperties(testRequest, query), "Request Parameter", null, "GetParameter"); } catch (final IOException e) { ExcUtils.suppress(e); } } } else { // Request type not supported } } final List<TestAssertion> assertionList = testRequest.getTestRequest().getAssertionList(); for (int i1 = 0, assertionListSize = assertionList.size(); i1 < assertionListSize; i1++) { final TestAssertion assertion = assertionList.get(i1); collector.startTestAssertion(assertion.getId()); testAssertionStatus = TestResultStatus.PASSED; if (assertion.getErrors() != null) { int i = 1; final AssertionError[] errors = assertion.getErrors(); for (int i2 = 0, errorsLength = errors.length; i2 < errorsLength; i2++) { final AssertionError error = errors[i2]; collector.error(error.getMessage()); if (assertion instanceof XQueryContainsAssertion) { final XQueryContainsAssertion xqueryAssertion = (XQueryContainsAssertion) assertion; // OK, this is very tricky: // we need to call selectFromCurrent() to get the whole message // because XmlUnit shortens the output. But as this assertion maybe reused in the run // we need to reset the internal properties... final String expectedContent = xqueryAssertion.getExpectedContent(); xqueryAssertion.selectFromCurrent(); final String translation = xqueryAssertion.getExpectedContent(); if (translation.contains("<etfTranslate")) { setManualOrError(addMessage(translation, collector, i++)); } else { setManualOrError(addMessage(error.getMessage(), collector, i++)); } xqueryAssertion.setExpectedContent(expectedContent); } else { setManualOrError(addMessage(error.getMessage(), collector, i++)); } } } collector.end(assertion.getId(), testAssertionStatus.value); } } else { if (testStepResult.getStatus() == TestStepResult.TestStepStatus.FAILED) { if (testStepResult.getTestStep() instanceof WsdlRunTestCaseTestStep) { status = 2; } else { status = 1; } } else { status = 0; } // Add messages final String[] messages = testStepResult.getMessages(); if (messages != null) { for (int i = 0, messagesLength = messages.length; i < messagesLength; i++) { final String message = messages[i]; try { if (message.contains("<etfTranslate")) { addMessage(message, collector, i + 1); } else { collector.saveAttachment(IOUtils.toInputStream(message, "UTF-8"), "Message." + (i + 1), "text/plain", "Message"); } } catch (IOException e) { collector.internalError(e); } } } } if (status == -1) { // auto determine collector.end(testStepResult.getTestStep().getId(), testStepResult.getTimeStamp() + testStepResult.getTimeTaken()); } else { collector.end(testStepResult.getTestStep().getId(), status, testStepResult.getTimeStamp() + testStepResult.getTimeTaken()); } } }