001/** 002 * //////////////////////////////////////////////////////////////////////////////// 003 * // 004 * // D SOFTWARE INCORPORATED 005 * // Copyright 2007-2014 D Software Incorporated 006 * // All Rights Reserved. 007 * // 008 * // NOTICE: D Software permits you to use, modify, and distribute this file 009 * // in accordance with the terms of the license agreement accompanying it. 010 * // 011 * // Unless required by applicable law or agreed to in writing, software 012 * // distributed under the License is distributed on an "AS IS" BASIS, 013 * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * // 015 * //////////////////////////////////////////////////////////////////////////////// 016 */ 017package com.thed.launcher; 018 019import com.thed.model.Agent; 020import com.thed.model.AutomationScriptJobDTO; 021import com.thed.model.TestcaseBatchExecution; 022import com.thed.util.ScriptUtil; 023import com.thed.util.ZeeConstants; 024import com.thed.zblast.util.RestUtil; 025import org.apache.commons.configuration.PropertiesConfiguration; 026import org.apache.commons.httpclient.HttpClient; 027 028import java.io.*; 029import java.util.logging.Level; 030import java.util.logging.Logger; 031 032/** 033 * This class executes batch script in separate thread. 034 * It uses an {@link IZBotScriptLauncher} instance for starting/stopping the batch process and 035 * finally sends the overall batch status along with TestcaseBatchExecution details to server. 036 * 037 * @author ZephyrDev 038 * 039 */ 040public class ScriptProcessor implements Runnable { 041 042 /*---------------------------------------------------------- 043 * ATTRIBUTES 044 *----------------------------------------------------------*/ 045 046 private Logger logger = Logger.getLogger(ScriptProcessor.class.getName()); 047 048 private Agent agent ; 049 private TestcaseBatchExecution testcaseBatchExecution ; 050 private String url ; 051 private PropertiesConfiguration properties; 052 053 /* indicates overall execution is completed. */ 054 private boolean completed = false ; 055 056 private HttpClient client ; 057 058 private String status = ""; 059 060 /*---------------------------------------------------------- 061 * END OF ATTRIBUTES 062 *----------------------------------------------------------*/ 063 064 065 /*---------------------------------------------------------- 066 * CONSTRUCTOR 067 *----------------------------------------------------------*/ 068 069 public ScriptProcessor(Agent agent, TestcaseBatchExecution testcaseBatchExecution, String url, PropertiesConfiguration properties) { 070 this.agent = agent ; 071 this.testcaseBatchExecution = testcaseBatchExecution; 072 this.url = url ; 073 this.properties = properties; 074 this.client = new HttpClient(); 075 } 076 077 /*---------------------------------------------------------- 078 * END OF CONSTRUCTOR 079 *----------------------------------------------------------*/ 080 081 /** 082 * This method loops through the entire batch of test cases, executes them and communicates the 083 * real-time status of each testcase back to the server. 084 * <br/> 085 * The order of LifeCycle methods is given below :-<br/> 086 * <br/> 087 * <{@link IZBotScriptLauncher#batchStart()}> - Invoked once before start of batch.<br/> 088 * <{@link IZBotScriptLauncher#testcaseExecutionStart()}> - invoked once before start of each testcase.<br/> 089 * <{@link IZBotScriptLauncher#testcaseExecutionRun()}> - This method executes testcase.<br/> 090 * <br/> 091 * In sequential mode, ZBot will wait for each script to finish, gather the process status and update test status in Zephyr Server via a webservice.<br/> 092 * Exit Status 0 is translated as pass. Any Non Zero values is treated as fail.<br/> 093 * In parallel mode, it will launch them all without waiting for it to finish. It also, doesnt update the test execution status.<br/> 094 * <br/> 095 * <{@link IZBotScriptLauncher#testcaseExecutionResult()}> - process testcase result.<br/> 096 * <{@link IZBotScriptLauncher#testcaseExecutionEnd()}> - invoked once after each testcase is done executing.<br/> 097 * <{@link IZBotScriptLauncher#batchEnd()}> - invoked once after end of batch.<br/> 098 * 099 */ 100 public void run() { 101 102 logger.info("START: execute batch script: " + testcaseBatchExecution); 103 104 try { 105 status = "Batch execution starting..."; 106 /* update realtime status */ 107 updateAgentRealTimeStatus(); 108 109 /* execute all testcases */ 110 if (testcaseBatchExecution.getTestcaseExecutionList() != null) { 111 112 //handling new script jobs 113 boolean status = executeScript(testcaseBatchExecution); 114 updateAgentRealTimeStatus(); 115 AutomationScriptJobDTO automationScriptJobDTO = RestUtil.getScriptJobDetail(testcaseBatchExecution.getJobId()); 116 File file = new File(automationScriptJobDTO.getResultPath()); 117 if(!file.exists()){ 118 throw new IOException(""" 119 Cannot run program %s: No such file or directory""".formatted(file)); 120 } 121 if(automationScriptJobDTO.getIsParseResultFile()!=null && !automationScriptJobDTO.getIsParseResultFile()){ 122 if(testcaseBatchExecution.getUpdateTestcaseStatusFromScript()) { 123 ScriptUtil.processTestCaseExecution(testcaseBatchExecution, status ? "1" : "2", agent); 124 } 125 } 126 else{ 127 logger.log(Level.INFO, "Script job Parsing started"); 128 ScriptUtil.updateParseTestcaseExecutionResult(testcaseBatchExecution, agent); 129 logger.log(Level.INFO, "Script job Parsing Ends"); 130 } 131 } 132 133 /* update realtime status */ 134 updateAgentRealTimeStatus(); 135 logger.info("END: execute batch script: " + testcaseBatchExecution); 136 137 } catch (Throwable t) { 138 /* TODO: signal error */ 139 logger.log(Level.WARNING, "FAILURE: batch script execution " + t.getMessage()); 140 t.printStackTrace(); 141 } 142 143 try { 144 ScriptUtil.sendResponse(client, url, agent.getToken(), ZeeConstants.RESULT_SUCCESS, testcaseBatchExecution); 145 } catch (Exception e) { 146 logger.log(Level.WARNING, "exception in sending response to server. " + e.getMessage()); 147 e.printStackTrace(); 148 } 149 completed = true ; 150 } 151 152 /** 153 * This method is used by zephyrAgent class to fetch realtime status. 154 */ 155 public String getRealtimeStatus() { 156 return status; 157 } 158 /** 159 * Returns the boolean flag to check the overall execution is completed. 160 */ 161 public boolean isCompleted() { 162 return completed; 163 } 164 165 private void updateAgentRealTimeStatus() { 166 System.out.println(status); 167 logger.log(Level.INFO, this::getRealtimeStatus); 168 ScriptUtil.updateAgentRealTimeStatus(client, url, agent.getToken(), getRealtimeStatus()); 169 } 170 171 private boolean executeScript(TestcaseBatchExecution testcaseBatchExecution) throws IOException { 172 try { 173 status = "STATUS: Script Executing"; 174 updateAgentRealTimeStatus(); 175 logger.info(""" 176 about to run script with path: %s %s""".formatted(testcaseBatchExecution.getScriptPath(), testcaseBatchExecution.getParameter())); 177 /* logic to execute testcases sequentially. i.e. next testcase waits till first testcase is finished */ 178 String[] cmds = {testcaseBatchExecution.getScriptPath(), testcaseBatchExecution.getParameter()}; 179 Process process = Runtime.getRuntime().exec(cmds); 180 status = "STATUS: Waiting for execution completion"; 181 updateAgentRealTimeStatus(); 182 new ProcessStreamReader(process.getErrorStream()).start(); 183 new ProcessStreamReader(process.getInputStream()).start(); 184 int processStatus = process.waitFor() ; 185 status = "STATUS: Script Successfully Executed"; 186 /* real value: by convention 0 indicates successful completion of script */ 187 if (processStatus == 0) { 188 return true; /* 1 = pass */ 189 } else { 190 return false; /* 2 = fail */ 191 } 192 } catch (Exception e) { 193 status = "STATUS: Execution Error " + e.getMessage(); 194 logger.log(Level.WARNING, "exception in script execution. " + e.getMessage()); 195 e.printStackTrace(); 196 return false; 197 } 198 } 199 200 static class ProcessStreamReader extends Thread { 201 BufferedReader br; 202 private final Logger logger = Logger.getLogger(this.getClass().getName()); 203 public ProcessStreamReader(InputStream in){ 204 super(); 205 br = new BufferedReader(new InputStreamReader(in)); 206 } 207 208 @Override 209 public void run() { 210 String line; 211 try { 212 while ((line = br.readLine()) != null) { 213 logger.info(Thread.currentThread().getName() + " | Read output..." + line); 214 } 215 } catch (IOException e) { 216 logger.log(Level.SEVERE, "Error in reading process steams \n", e); 217 }finally{ 218 if(br != null){ 219 try { br.close(); } catch (IOException e) {} 220 } 221 } 222 logger.info(Thread.currentThread().getName() + " | Done reading process log input stream"); 223 } 224 } 225 226}