/*
+----------------------------------------------------------------------+
|                    Class: CLauncher                                  |
|                                                                      |
| Developper:  Eric Gavaldo (eric.gavaldo@xqual.com)                   |
| Version:     1.1                                                     |
| License:     LGPL (http://www.gnu.org/licenses/lgpl.html)            |
+----------------------------------------------------------------------+
*/
package com.xqual.xlauncher.soapui;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Vector;

import com.xqual.xagent.launcher.CExecutionStep;
import com.xqual.xagent.launcher.CLauncher;
import com.xqual.xagent.launcher.CReturnStatus;
import com.xqual.xagent.launcher.runner.CRunner;
import com.xqual.xagent.launcher.runner.IRunner;
import com.xqual.xcommon.CAttribute;
import com.xqual.xcommon.IConstantsResults;
import com.xqual.xcommon.type.CParam;
import com.xqual.xcommon.type.CParamParsingException;
import com.xqual.xcommon.utils.CFileUtils;
import com.xqual.xcommon.utils.CUtils;

/**
 * The <code>CLauncherImpl</code> implementation of <code>ILauncher</code> for SOAPUi.
 * @author egavaldo
 * developed based on:
 * - http://www.soapui.org/Test-Automation/functional-tests.html
 */
public class CLauncherImpl extends CLauncher implements IConstantsResults {

	// +==============================================================+
	// | Attributes                                                   |
	// +==============================================================+

	static final String TRACE_HEADER = "{soapui        }  ";

	static final String CONSOLE_OUTPUT = "console_output.txt";

	// parameters impacting executing at run time set by the test operator
	private String testRootPath;
	private String soapUIInstallPath;
	
	private String testProjectXml;

	private File workingDir;

	private static final String SOAPUI_RUNNER = CUtils.getBatchName("testrunner");

	// +==============================================================+
	// | Constructors                                                 |
	// +==============================================================+

	public CLauncherImpl() {
		super(TRACE_HEADER);
	}

	// +==============================================================+
	// | Methods                                                      |
	// +==============================================================+

	@Override
	public boolean getDefaultTestcaseMustBeCreated() {
		return true;
	}
	
	@Override
	public CReturnStatus initialize(int sutId, String sutName, String sutVersion) {
		setSutDetails(sutId, sutName, sutVersion);

		// check the configuration sent by the manager
		printConfiguration();

		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		try {
			// retrieve the parameters we need
			testRootPath     = getStringParamValue("General",  "Test root path"); 
			soapUIInstallPath = getStringParamValue("soapUI",   "soapUI install path");
		} catch (CParamParsingException e) {
			traceln(LOG_PRIORITY_SEVERE, "parsing error during initialization");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception during initialize: " + e.getMessage()));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		}
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus preRun(int testId, String testPath, String testName, Vector<CAttribute> attributes, String additionalInfo) {
		traceln(LOG_PRIORITY_INFO, "preRun testId=" + testId + " testPath=" + testPath + " [" + testName + "]...");
		
		if (testPath.length()>0)
			testProjectXml = testRootPath + "/" + testPath + "/" + additionalInfo;
		else
			testProjectXml = testRootPath + "/" + additionalInfo;
		
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus run(int testId, String testPath, String testName, int testcaseId, int testcaseIndex, String testcaseName, Vector<CParam> params, String additionalInfo) {
		traceln(LOG_PRIORITY_INFO, "run testId=" + testId + " testPath=" + testRootPath + "/" + testPath + "/" + testName + " testcaseIndex=" + testcaseIndex + "...");
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();

		workingDir = new File(testRootPath);

		// +------------------------------------+
		// | Delete previous results file
		// +------------------------------------+		
		File consoleOutputFile = new File(CONSOLE_OUTPUT);
		if (consoleOutputFile.exists()) consoleOutputFile.delete();
		try {
			consoleOutputFile.createNewFile();
		} catch (IOException e) {
			traceln(LOG_PRIORITY_SEVERE, "Could not create file \"" + CONSOLE_OUTPUT + "\"...");
		}
		
		// +------------------------------------+
		// | Interpret the script
		// +------------------------------------+
		// TestSuite ==> XStudio test
		// Testcase  ==> XStudio testcase
		
		// -r			Turns on printing of a small summary report
     	// -s			The TestSuite to run
		// -c			The TestCase to run
		// -e			The endpoint to use when invoking test-requests (overrides the endpoint set in the project file)
		
		// -f <file>	(useful only with soapUI Pro)Specifies the root folder to which test results should be exported
		// -j			(useful only with soapUI Pro) Turns on exporting of JUnit-compatible reports
		// -R<type>		(only soapUI Pro) Selects which report to generate for the test objects executed
		// -F<type>		(only soapUI Pro) Sets the format of the report specified with the -R option
		
		// +---------------+
		// |    Example    |
		// +---------------+
		// In Xstudio:
		// Test Name                = "GetUser"
		// Test Canonical Path      = ""
		// Test Additional Info     = "LDAP-soapui-project.xml"
		//
		// Testcase Name            = "GetUser_NegativeTest"
		// Testcase Additional Info = ""
		//
		// Launcher Configuration:
		// Test root path           = "C:/test_repository/tests/soapui"
		//
		// Will execute the following command:
		// "C:/eviware/soapUI-4.0.0/bin/testrunner.bat" -r -j -sGetUser -cGetUser_NegativeTest "C:\test_repository\tests\soapui\LDAP-soapui-project.xml"
		
		CRunner soapUIRunner = new CRunner("[" + testId + "] "+ testPath + ":" + testName + "." + testcaseIndex,
		                                   CFileUtils.quoteFilePath(soapUIInstallPath + "/bin/" + SOAPUI_RUNNER) + " " +
		                                   "-r " +
		                                   "-j " +
		                                   "-s" + testName + " -c" + testcaseName + " " +
		                                   (additionalInfo.length()>0 ? "-e" + additionalInfo + " " : " ") +
		                                   CFileUtils.quoteFilePath(new File(testProjectXml).getAbsolutePath()),
                                           workingDir);
		short result = soapUIRunner.requestAction(IRunner.START_PROCESS, IRunner.WAIT_END_OF_EXECUTION, consoleOutputFile);
		
		// +------------------------------------+
		// | Add the output console to the test
		// +------------------------------------+
		addAttachment(consoleOutputFile);

		if (result == RESULT_FAILURE) {
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "script interpretation failed"));
			parseResultFile(executionSteps); // to have the details anyway
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "execution process succeeded"));
			return parseResultFile(executionSteps);
		}
	}

	@Override
	public CReturnStatus postRun(int testId, String testPath, String testName) {
		traceln(LOG_PRIORITY_INFO, "postRun testId=" + testId + " testPath=" + testPath + ":" + testName + "...");
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "postRun: succeeded"));
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	@Override
	public CReturnStatus terminate() {
		Vector<CExecutionStep> executionSteps = new Vector<CExecutionStep>();
		executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "Terminate"));
		return new CReturnStatus(RESULT_SUCCESS, executionSteps);
	}

	// +--------------------------+
	// ¦        Utilities         ¦
	// +--------------------------+

	private CReturnStatus parseResultFile(Vector<CExecutionStep> executionSteps) {
		// parse the result file to get the result and the execution steps
		File resultFile = new File(CONSOLE_OUTPUT);
		if (!resultFile.exists()) {
			traceln(LOG_PRIORITY_SEVERE, "Result file not found!");
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "run: result file not found!"));
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			executionSteps.add(new CExecutionStep(RESULT_SUCCESS, "run: result file found"));
		}

		String line;
		boolean errorDetected = false;

		try {
			FileInputStream fileInputStream = new FileInputStream(resultFile);
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));

			while ((line = bufferedReader.readLine()) != null) {
				line = line.trim();
				System.out.println(">" + line);
				if (line.startsWith("Time Taken")) {
					executionSteps.add(new CExecutionStep(RESULT_UNKNOWN, line));

				} else if (line.startsWith("Total TestCases")) {
					if (line.indexOf("0 failed")>0)
						executionSteps.add(new CExecutionStep(RESULT_SUCCESS, line));
					else
						executionSteps.add(new CExecutionStep(RESULT_FAILURE, line));
					errorDetected = true;

				} else if (line.startsWith("Total Failed Assertions")) {
					if (line.indexOf("Assertions: 0")>0)
						executionSteps.add(new CExecutionStep(RESULT_SUCCESS, line));
					else
						executionSteps.add(new CExecutionStep(RESULT_FAILURE, line));
					errorDetected = true;
				}
			}

			bufferedReader.close();
			fileInputStream.close();
		} catch (Exception e) {
			traceln(LOG_PRIORITY_SEVERE, "exception whle parsing the result file: " + e);
			executionSteps.add(new CExecutionStep(RESULT_FAILURE, "Exception while parsing the result file: " + e));
			errorDetected = true;
		}

		if (errorDetected) {
			return new CReturnStatus(RESULT_FAILURE, executionSteps);
		} else {
			return new CReturnStatus(RESULT_SUCCESS, executionSteps);
		}
	}
}

