package de.persosim.simulator.test.globaltester;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.globaltester.logging.BasicLogger;
/**
* Encapsulate ServerMode communication with an existing GlobalTester instance
* in order to include GlobalTester tests within the existing
* Unit-test-framework.
* <p/>
* This class expects a running GlobalTester instance listening on the provided
* host/port. It also assumes that this instance is already configured to use
* the PersoSim simulation as default card reader.
*
* @author amay
*
*/
public class GtServerConnection {
public static final String PREF_QUALIFIER_LOGGING = "org.globaltester.logging";
public static final String PREF_QUALIFIER_SECUREMESSAGING = "com.hjp.globaltester.epassport.securemessaging";
public static final String PREF_QUALIFIER_EAC2 = "com.hjp.globaltester.prove.eac2";
public static final String PREF_QUALIFIER_TESTMANAGER = "org.globaltester.testmanager";
private static final Pattern RESULTPATTERN = Pattern.compile(
".*?(\\d+) testcases \\((\\d+) failures, (\\d+) warnings.*");
private String serverHost;
private int cmdPort;
private Socket socket = null;
private PrintStream out = null;
private BufferedReader in = null;
private int collectedErrors = 0;
private int collectedWarnings = 0;
private int nrOfExecutedTestCases = 0;
/**
* Constructor that provides connection information to acces GlobalTester
* @param host hostname under which the GT is running
* @param port port in which GT is listening to accept commands
* @param resultPort port on which GT will listen for provided reading results in terminal tests
*/
public GtServerConnection(String host, int port, int resultPort) {
serverHost = host;
cmdPort = port;
}
/**
* Change the selected working directory on the remote GlobalTester.
*
* @param projectName project to select
* @throws IOException
*/
private void setWorkingProject(String projectName) throws IOException {
String command = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<changeProject>" + projectName + "</changeProject>";
transmitCommand(command);
}
/**
* Run a TestSuite (or TestCase) with given name from given project. Test results are
* extracted from the server response and accumulated until the next call of
* {@link #checkAndClearResults(int, int)} or
* {@link #clearCollectedResults()}
*
* @param projectName project to search the test suite in
* @param testSuiteName filename of the testsuite to execute (without extension)
* @throws IOException
*/
public void runSuiteAndSaveResults(String projectName, String testSuiteName) throws IOException {
setWorkingProject(projectName);
runSuiteAndSaveResults(testSuiteName);
}
/**
* Run a TestSuite (or TestCase) within the current project. Test results are
* extracted from the server response and accumulated until the next call of
* {@link #checkAndClearResults(int, int)} or
* {@link #clearCollectedResults()}
*
* @param testSuiteName filename of the testsuite to execute (without extension)
* @throws IOException
*/
public void runSuiteAndSaveResults(String testSuiteName) throws IOException {
String command = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<testsuite>" + testSuiteName + "</testsuite>";
String result = transmitCommand(command);
saveResults(result);
}
private void saveResults(String result) {
if (result != null) {
Matcher matcher = RESULTPATTERN
.matcher(result);
if (matcher.matches()) {
nrOfExecutedTestCases += Integer.parseInt(matcher.group(1));
collectedErrors += Integer.parseInt(matcher.group(2));
collectedWarnings += Integer.parseInt(matcher.group(3));
}
}
}
/**
* Compare the accumulated results with expected values and clears the
* accumulated results afterwards.
*
* @see #checkResults(int, int)
* @see #clearCollectedResults()
*
* @param expectedErrors
* @param expectedWarnings
*/
public void checkAndClearResults(int expectedErrors, int expectedWarnings) {
checkResults(expectedErrors, expectedWarnings);
clearCollectedResults();
}
/**
* Reset the accumulated results to 0.
*/
public void clearCollectedResults() {
nrOfExecutedTestCases = 0;
collectedErrors = 0;
collectedWarnings = 0;
}
public int getCollectedErrors() {
return collectedErrors;
}
public int getCollectedWarnings() {
return collectedWarnings;
}
/**
* Compare the accumulated results with expected values.
* <p/>
* In case of a mismatch between expected and accumulated values an
* {@link AssertionError} is thrown.
*
* @see #checkResults(int, int)
* @see #clearCollectedResults()
*
* @param expectedErrors
* @param expectedWarnings
*/
public void checkResults(int expectedErrors, int expectedWarnings) {
System.out
.printf("Executed %5d testcases via GlobalTester Servermode. Found/expected %5d/%-5d errors and %5d/%-5d warnings. ",
nrOfExecutedTestCases, collectedErrors, expectedErrors,
collectedWarnings, expectedWarnings);
if (nrOfExecutedTestCases <= 0) {
System.out.println("FAIL!");
assertTrue("No testcases have been executed", nrOfExecutedTestCases > 0);
} else if (expectedErrors != collectedErrors) {
System.out.println("FAIL!");
assertEquals("Unexpected number of errors in testsuite",
expectedErrors, collectedErrors);
} else if (expectedWarnings != collectedWarnings) {
System.out.println("FAIL!");
assertEquals("Unexpected number of errors in testsuite",
expectedWarnings, collectedWarnings);
} else {
System.out.println("OK!");
}
}
/**
* Initiate the ServerMode connection to the GlobalTester instance.
* <p/>
* NOTE: the current implementation is not thread safe
*/
public boolean connect() {
if (socket != null) {
throw new RuntimeException(
"GTServerConnection is already connected");
}
try {
socket = new Socket(serverHost, cmdPort);
if (!socket.isConnected()) {
throw new RuntimeException(
"GTServerConnection: unable to connect to a GT Server ");
}
out = new PrintStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
return true;
} catch (IOException e) {
BasicLogger.logException(this.getClass(), e);
return false;
}
}
private String transmitCommand(String command) throws IOException {
out.println(command);
return in.readLine();
}
/**
* Close the ServerMode connection to the GlobalTester instance.
* <p/>
* NOTE: the current implementation is not thread safe
* @throws IOException
*/
public void closeConnection() {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @see #runSuiteAndSaveResults(String, String)
* @param suite
* @throws IOException
*/
public void runSuiteAndSaveResults(GtSuiteDescriptor suite) throws IOException {
runSuiteAndSaveResults(suite.getProject(), suite.getTestSuiteName());
}
/**
* This method allows setting of preference values.
*
* @param qualifier
* Eclipse preference qualifier (i.e. bundle name of scope)
* @param key
* key of the preference to set
* @param value
* value to store
* @return the ServerMode response, this contains either the previous value
* or an error message
* @throws IOException
*/
public String setPreferences(String qualifier, String key, String value) throws IOException {
String command =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<setPreferences>"+
"<preference qualifier=\""+qualifier+"\" key=\""+key+"\"><![CDATA["+ value + "]]></preference>"+
"</setPreferences>";
return transmitCommand(command);
}
/**
* Returns the absolute path to the workspace directory used by the remot GT
* instance. This path is usable on the (possibly remote) machine.
*
* @return
* @throws IOException
*/
public String getWorkspaceDir() throws IOException {
String command = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><getWorkspaceDir/>";
String response = transmitCommand(command);
Matcher m = Pattern.compile("<workspaceDir>(.*)</workspaceDir>")
.matcher(response);
if (m.matches()) {
return m.group(1);
} else {
return "";
}
}
/**
* Creates a directory on the remote machine.
* <p/>
* Be aware that this allows uncontrolled access to the remote file system.
*
* @param path
* @return the ServerMode response, in case of failure this contains an
* error message
* @throws IOException
*/
public String mkDir(String path) throws IOException {
String command = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<mkDir>" + path + "</mkDir>";
return transmitCommand(command);
}
/**
* Writes data to the file on the remote machine, possibly creating it.
* <p/>
* Be aware that this allows uncontrolled access to the remote file system.
*
* @param path
* @return the ServerMode response, in case of failure this contains an
* error message
* @throws IOException
*/
public String writeFile(String filename, String content) throws IOException {
String command =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<writeFile filename=\""+filename+"\">"
+ content
+ "</writeFile>";
return transmitCommand(command);
}
/**
* Select supported GlobalTester profiles in the remote GT instance.
* <p/>
* This sets all available profiles to false except those given in supportedProfiles
* @param supportedProfiles Collection of profiles supported by this sample.
* @throws IOException
*/
public void setSupportedProfiles(Collection<String> supportedProfiles) throws IOException {
HashMap<String, Boolean> profiles = getAllProfilesDisabled();
//set supported profiles
for (String curSupProfile : supportedProfiles) {
profiles.put(curSupProfile, true);
}
//build command
StringBuffer command = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
command.append("<setPreferences>");
for (String curProfile : profiles.keySet()) {
command.append("<preference qualifier=\""+PREF_QUALIFIER_TESTMANAGER+"\" key=\"_profile_"+curProfile+"\">"+profiles.get(curProfile)+"</preference>");
}
command.append("</setPreferences>");
//send to gtServer
transmitCommand(command.toString());
}
/**
* Returns a HascMap associating false with all known profiles
* @return
*/
private HashMap<String, Boolean> getAllProfilesDisabled() {
HashMap<String, Boolean> retVal = new HashMap<String, Boolean>();
for (String curProfile : GtConstants.ALL_PROFILES) {
retVal.put(curProfile, false);
}
return retVal;
}
}