package net.jsunit; import net.jsunit.configuration.Configuration; import net.jsunit.model.*; import org.jdom.Document; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.logging.Logger; public class DistributedTestRunManager { private Logger logger = Logger.getLogger("net.jsunit"); private RemoteServerHitter hitter; private Configuration configuration; private String overrideURL; private DistributedTestRunResult distributedTestRunResult = new DistributedTestRunResult(); private Browser remoteBrowser; public static DistributedTestRunManager forConfiguration(Configuration configuration) { return new DistributedTestRunManager(new RemoteMachineServerHitter(), configuration, null); } public static DistributedTestRunManager forConfigurationAndURL(RemoteServerHitter hitter, Configuration configuration, String overrideURL) { return new DistributedTestRunManager(hitter, configuration, overrideURL); } protected DistributedTestRunManager(RemoteServerHitter hitter, Configuration configuration, String overrideURL) { this.hitter = hitter; this.configuration = configuration; this.overrideURL = overrideURL; } public void runTests() { List<Thread> threads = new ArrayList<Thread>(); for (final URL baseURL : configuration.getRemoteMachineURLs()) threads.add(new Thread("Run JsUnit tests on " + baseURL) { public void run() { runTestsOnRemoteMachine(baseURL); } }); for (Thread thread : threads) thread.start(); for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { throw new RuntimeException("One of the test threads was interrupted."); } } } private void runTestsOnRemoteMachine(URL baseURL) { List<TestRunResult> results = new ArrayList<TestRunResult>(); try { URL fullURL = buildURL(baseURL); logger.info("Requesting run on remove machine URL " + baseURL); Document documentFromRemoteMachine = hitter.hitURL(fullURL); logger.info("Received response from remove machine URL " + baseURL); if (isMultipleTestRunResultsResult(documentFromRemoteMachine)) { DistributedTestRunResult multiple = new DistributedTestRunResultBuilder(configuration).build(documentFromRemoteMachine); results.addAll(multiple.getTestRunResults()); } else { TestRunResult single = new TestRunResultBuilder(configuration).build(documentFromRemoteMachine); results.add(single); } } catch (IOException e) { if (configuration.shouldIgnoreUnresponsiveRemoteMachines()) logger.info("Ignoring unresponsive machine " + baseURL.toString()); else { logger.info("Remote machine URL is unresponsive: " + baseURL.toString()); TestRunResult unresponsiveResult = new TestRunResult(baseURL); unresponsiveResult.setUnresponsive(); results.add(unresponsiveResult); } } for (TestRunResult result : results) { result.setURL(baseURL); //noinspection SynchronizeOnNonFinalField synchronized (distributedTestRunResult) { distributedTestRunResult.addTestRunResult(result); } } } private boolean isMultipleTestRunResultsResult(Document document) { return document.getRootElement().getName().equals(DistributedTestRunResult.NAME); } private URL buildURL(URL url) throws UnsupportedEncodingException, MalformedURLException { String fullURLString = url.toString(); fullURLString += "/runner"; boolean hasFirstParameter = false; if (overrideURL != null) { fullURLString += "?url=" + URLEncoder.encode(overrideURL, "UTF-8"); hasFirstParameter = true; } else if (configuration.getTestURL() != null) { fullURLString += "?url=" + URLEncoder.encode(configuration.getTestURL().toString(), "UTF-8"); hasFirstParameter = true; } if (remoteBrowser != null) { fullURLString += (hasFirstParameter ? "&" : "?"); fullURLString += "browserId=" + remoteBrowser.getId(); } return new URL(fullURLString); } public DistributedTestRunResult getDistributedTestRunResult() { return distributedTestRunResult; } public String getOverrideURL() { return overrideURL; } public void setOverrideURL(String overrideURL) { this.overrideURL = overrideURL; } public void limitToBrowser(Browser remoteBrowser) { this.remoteBrowser = remoteBrowser; } }