package com.intuit.tank.runner; /* * #%L * Intuit Tank Agent (apiharness) * %% * Copyright (C) 2011 - 2015 Intuit Inc. * %% * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * #L% */ import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.intuit.tank.harness.APITestHarness; import com.intuit.tank.harness.FlowController; import com.intuit.tank.harness.data.FailableStep; import com.intuit.tank.harness.data.HDScript; import com.intuit.tank.harness.data.HDScriptGroup; import com.intuit.tank.harness.data.HDScriptUseCase; import com.intuit.tank.harness.data.HDTestPlan; import com.intuit.tank.harness.data.HDTestVariables; import com.intuit.tank.harness.data.HDVariable; import com.intuit.tank.harness.data.RequestStep; import com.intuit.tank.harness.data.TestStep; import com.intuit.tank.harness.logging.LogEvent; import com.intuit.tank.harness.logging.LogUtil; import com.intuit.tank.harness.test.AbortScriptException; import com.intuit.tank.harness.test.GotoScriptException; import com.intuit.tank.harness.test.KillScriptException; import com.intuit.tank.harness.test.NextScriptGroupException; import com.intuit.tank.harness.test.RestartScriptException; import com.intuit.tank.harness.test.data.Variables; import com.intuit.tank.http.BaseRequest; import com.intuit.tank.http.BaseResponse; import com.intuit.tank.http.TankHttpClient; import com.intuit.tank.logging.LogEventType; import com.intuit.tank.runner.method.TestStepRunner; import com.intuit.tank.runner.method.TimerMap; import com.intuit.tank.script.ScriptConstants; import com.intuit.tank.vm.api.enumerated.WatsAgentCommand; import com.intuit.tank.vm.common.TankConstants; import com.intuit.tank.vm.common.util.MethodTimer; import com.intuit.tank.vm.settings.TankConfig; public class TestPlanRunner implements Runnable { static Logger LOG = LogManager.getLogger(TestPlanRunner.class); private Variables variables; private TimerMap timerMap; private String uniqueName; private HDTestPlan testPlan; private int threadNumber; private TankHttpClient httpClient; private BaseRequest previousRequest = null; private BaseResponse previousResponse = null; private boolean finished = false; private Map<String, String> headerMap; // SSL values long lastSslHandshake = 0; // SSLContext sslContext = null; long sslTimeout = 90000; public TestPlanRunner(HDTestPlan testPlan, int threadNumber) { headerMap = new TankConfig().getAgentConfig().getRequestHeaderMap(); this.testPlan = testPlan; this.threadNumber = threadNumber; setHttpClient(initHttpClient()); } public TestPlanRunner(HDTestPlan testPlan, int threadNumber, TankHttpClient client) { headerMap = new TankConfig().getAgentConfig().getRequestHeaderMap(); this.testPlan = testPlan; this.threadNumber = threadNumber; this.httpClient = client; } /** * @return the headerMap */ public Map<String, String> getHeaderMap() { return headerMap; } public void setHttpClient(TankHttpClient httpClient) { this.httpClient = httpClient; } public TankHttpClient getHttpClient() { return this.httpClient; } public void setUniqueName(String name) { this.uniqueName = name; } public void run() { MethodTimer mt = new MethodTimer(LOG, getClass(), "runTestPlan(" + testPlan.getTestPlanName() + ")"); LogEvent logEvent = LogUtil.getLogEvent(); variables = new Variables(); logEvent.setVariables(variables); logEvent.setTestPlan(testPlan); timerMap = new TimerMap(); HDTestVariables variableDefinitions = testPlan.getVariables(); if (variableDefinitions != null) { for (HDVariable v : variableDefinitions.getVariables()) { variables.addVariable(v.getKey(), v.getValue(), variableDefinitions.isAllowOverride()); } } variables.addVariable("THREAD_ID", Integer.toString(threadNumber), false); List<HDScriptGroup> group = testPlan.getGroup(); try { int i = 0; int loopCount = 1; mainLoop: while (i < group.size()) { HDScriptGroup hdScriptGroup = group.get(i); logEvent.setGroup(hdScriptGroup); hdScriptGroup.setParent(testPlan); int scriptGroupLoop = 0; while (scriptGroupLoop < hdScriptGroup.getLoop()) { List<HDScript> groupSteps = hdScriptGroup.getGroupSteps(); if (APITestHarness.getInstance().getCmd() == WatsAgentCommand.kill) { return; } try { runTestGroup(groupSteps, hdScriptGroup); } catch (KillScriptException e) { LOG.info(LogUtil.getLogMessage(e.getMessage())); return; } catch (NextScriptGroupException e) { scriptGroupLoop = hdScriptGroup.getLoop(); break; } catch (RestartScriptException e) { i = 0; loopCount = 1; continue mainLoop; } if (isCompleted(RunPhase.group, finished)) { LOG.info("finished or Stop set to group or less, exiting at group " + hdScriptGroup.getName()); return; } scriptGroupLoop++; } if (group.get(group.size() - 1) != hdScriptGroup) { i++; } else { finished = true; if (isCompleted(RunPhase.test, finished)) { LOG.info("finished or Stop set to test or less, exiting at test..."); return; } i = 0; loopCount++; LOG.info(LogUtil.getLogMessage("Test for test plan " + testPlan.getTestPlanName() + " has finished and is now starting over for the " + loopCount + " time")); } } } catch (Throwable e) { LOG.error(LogUtil.getLogMessage("Unexpected exception in test: " + e.toString()), e); } finally { APITestHarness.getInstance().threadComplete(); LOG.info(LogUtil.getLogMessage(mt.getNaturalTimeMessage() + " Test complete. Exiting...")); } } private void runTestGroup(List<HDScript> scripts, HDScriptGroup parent) throws KillScriptException, RestartScriptException { MethodTimer mt = new MethodTimer(LOG, getClass(), "runScriptGroup(" + parent.getName() + ")"); LogEvent logEvent = LogUtil.getLogEvent(); try { for (HDScript hdscript : scripts) { MethodTimer mt1 = new MethodTimer(LOG, getClass(), "runScript(" + hdscript.getName() + ")"); logEvent.setScript(hdscript); //LOG.info(LogUtil.getLogMessage("Entering Script", LogEventType.Informational)); hdscript.setParent(parent); int scriptLoop = 0; APITestHarness.getInstance().getUserTracker().add(hdscript.getName()); try { while (scriptLoop < hdscript.getLoop()) { String gotoGroup = null; List<HDScriptUseCase> useCaseList = hdscript.getUseCase(); for (int i = 0; i < useCaseList.size(); i++) { if (APITestHarness.getInstance().getCmd() == WatsAgentCommand.kill) { return; } HDScriptUseCase hdScriptUseCase = useCaseList.get(i); hdScriptUseCase.setParent(hdscript); if (gotoGroup != null) { if (!checkGotoGroupUseCase(hdScriptUseCase, gotoGroup)) { continue; } else { gotoGroup = null; } } try { runScriptSteps(hdScriptUseCase); } catch (GotoScriptException e) { i = 0; gotoGroup = e.getGotoTarget(); } } scriptLoop++; } } catch (AbortScriptException ase) { // ignore and go to next script } finally { LOG.info(LogUtil.getLogMessage(mt1.getNaturalTimeMessage())); APITestHarness.getInstance().getUserTracker().remove(hdscript.getName()); } if (shouldStop(RunPhase.script)) { LOG.info("Stop set to script or less, exiting at script " + hdscript.getName()); return; } } } finally { LOG.info(LogUtil.getLogMessage(mt.getNaturalTimeMessage())); } } /** * Check to see if the thread/virtual user should continue for another loop * * @param lastLoop * * @return */ private boolean isCompleted(RunPhase phase, boolean finished) { boolean ret = false; if (shouldStop(phase) || (finished && (APITestHarness.getInstance().getAgentRunData().getSimulationTime() <= 0 || APITestHarness.getInstance().hasMetSimulationTime() || APITestHarness.getInstance().isDebug()))) { ret = true; } return ret; } private boolean shouldStop(RunPhase phase) { boolean ret = false; if (APITestHarness.getInstance().getCmd() == WatsAgentCommand.stop) { ret = phase.ordinal() >= APITestHarness.getInstance().getAgentRunData().getStopBehavior().ordinal(); LOG.info("shouldStop: stopBehavior=" + APITestHarness.getInstance().getAgentRunData().getStopBehavior().name() + " : phase=" + phase.name()); } return ret; } private boolean checkGotoGroupUseCase(HDScriptUseCase hdScriptUseCase, String gotoGroup) { boolean retVal = false; List<TestStep> scriptSteps = hdScriptUseCase.getScriptSteps(); for (TestStep testStep : scriptSteps) { if (testStep instanceof RequestStep) { RequestStep rs = (RequestStep) testStep; if (gotoGroup.equals(rs.getScriptGroupName())) { retVal = true; break; } } } return retVal; } private void runScriptSteps(HDScriptUseCase hdScriptUseCase) throws KillScriptException, AbortScriptException, GotoScriptException, RestartScriptException, NextScriptGroupException { List<TestStep> scriptSteps = hdScriptUseCase.getScriptSteps(); String gotoGroup = null; LogEvent logEvent = LogUtil.getLogEvent(); for (int i = 0; i < scriptSteps.size(); i++) { while (APITestHarness.getInstance().getCmd() == WatsAgentCommand.pause) { if (APITestHarness.getInstance().hasMetSimulationTime()) { APITestHarness.getInstance().setCommand(WatsAgentCommand.stop); break; } else { try { Thread.sleep(APITestHarness.POLL_INTERVAL); } catch (InterruptedException e) { // LOG.warn("Got interrupded during pause."); } } } if (APITestHarness.getInstance().getCmd() == WatsAgentCommand.kill) { return; } TestStep testStep = scriptSteps.get(i); logEvent.setStep(testStep); logEvent.setTransactionId(UUID.randomUUID().toString()); testStep.setParent(hdScriptUseCase); if (gotoGroup != null) { if (testStep instanceof RequestStep) { RequestStep rs = (RequestStep) testStep; logEvent.setStepGroupName(rs.getScriptGroupName()); if (!gotoGroup.equalsIgnoreCase(rs.getScriptGroupName())) { continue; } } } FlowController flowController = APITestHarness.getInstance().getFlowController( Thread.currentThread().getId()); TestStepContext tsc = new TestStepContext(testStep, variables, testPlan.getTestPlanName(), uniqueName, timerMap, this); tsc.setRequest(previousRequest); tsc.setResponse(previousResponse); if (!flowController.shouldExecute(tsc)) { continue; } flowController.nextStep(tsc); if (!flowController.shouldExecute(tsc)) { continue; } TestStepRunner tsr = new TestStepRunner(tsc); if (APITestHarness.getInstance().getCmd() == WatsAgentCommand.stop) { LOG.info(LogUtil.getLogMessage("Executing step after stop command " + tsc.getTestStep(), LogEventType.Script)); } flowController.startStep(tsc); String validation = TankConstants.HTTP_CASE_FAIL; try { validation = tsr.execute(); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } finally { flowController.endStep(tsc); } previousRequest = tsc.getRequest(); previousResponse = tsc.getResponse(); if (validation.equals(TankConstants.HTTP_CASE_FAIL)) { FailableStep rs = (FailableStep) tsc.getTestStep(); String onFail = rs.getOnFail(); if (onFail.equalsIgnoreCase(TankConstants.HTTP_CASE_SKIP)) { APITestHarness.getInstance().addSkip(); String groupNameToSkip = rs.getScriptGroupName(); for (int j = i + 1; j < scriptSteps.size(); j++) { TestStep testStep2 = scriptSteps.get(j); String scriptGroupToTest = null; if (testStep2 instanceof RequestStep) { scriptGroupToTest = ((RequestStep) testStep2).getScriptGroupName(); } if (!groupNameToSkip.equals(scriptGroupToTest)) { i = j; break; } if (j == scriptSteps.size() - 1) { return; } } } else if (onFail.equalsIgnoreCase(TankConstants.HTTP_CASE_KILL)) { APITestHarness.getInstance().addKill(); throw new KillScriptException("Killing " + tsc.getTestStep()); } else if (onFail.equalsIgnoreCase(TankConstants.HTTP_CASE_SKIPGROUP)) { APITestHarness.getInstance().addSkipGroup(); throw new NextScriptGroupException("Skilling test group at " + tsc.getTestStep()); } else if (onFail.equalsIgnoreCase(TankConstants.HTTP_CASE_ABORT)) { APITestHarness.getInstance().addAbort(); throw new AbortScriptException("Aborting " + tsc.getTestStep()); } else if (onFail.equalsIgnoreCase(TankConstants.HTTP_CASE_RESTART)) { if (APITestHarness.getInstance().hasMetSimulationTime()) { APITestHarness.getInstance().addKill(); throw new KillScriptException("Killing " + tsc.getTestStep()); } else { APITestHarness.getInstance().addRestart(); throw new RestartScriptException("Restarting user at " + tsc.getTestStep()); } } else if (onFail.toLowerCase().startsWith(ScriptConstants.ACTION_GOTO_PREFIX)) { gotoGroup = StringUtils.substring(onFail, ScriptConstants.ACTION_GOTO_PREFIX.length()); i = -1; APITestHarness.getInstance().addGoto(); throw new GotoScriptException("Go to group " + gotoGroup, gotoGroup); } } if (shouldStop(RunPhase.step)) { LOG.info("Stop set to step, exiting at step " + testStep.getStepIndex()); return; } } } public TankHttpClient initHttpClient() { try { //get the client from a factory and set it here. TankHttpClient ret = (TankHttpClient) Class.forName(APITestHarness.getInstance().getTankHttpClientClass()).newInstance(); Long connectionTimeout = APITestHarness.getInstance().getTankConfig().getAgentConfig().getConnectionTimeout(); if (connectionTimeout != null) { ret.setConnectionTimeout(connectionTimeout); } return ret; } catch (Exception e) { LOG.error("TankHttpClient specified incorrectly: " + e, e); throw new RuntimeException(e); } } }