Java tutorial
/* * Copyright (c) 2013 3 Round Stones Inc., Some Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.callimachusproject.webdriver.helpers; import static java.net.URLDecoder.decode; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLEncoder; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; import junit.framework.TestSuite; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.SocketConfig; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.callimachusproject.Version; import org.callimachusproject.test.TemporaryServer; import org.callimachusproject.test.TemporaryServerFactory; import org.callimachusproject.test.WebResource; import org.callimachusproject.util.DomainNameSystemResolver; import org.callimachusproject.webdriver.pages.CalliPage; import org.callimachusproject.webdriver.pages.FolderView; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.ie.InternetExplorerDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.LocalFileDetector; import org.openqa.selenium.remote.RemoteWebDriver; import org.openrdf.OpenRDFException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class BrowserFunctionalTestCase extends TestCase { private static final char DELIM = ' '; private static final String PASSWORD = "testPassword1"; private static final String EMAIL = "test@example.com"; private static final int PORT = 8088; protected static final Logger logger = LoggerFactory.getLogger(BrowserFunctionalTestCase.class); private static final String HOSTNAME = DomainNameSystemResolver.getInstance().getLocalHostName(); private static final String ORIGIN = "http://" + HOSTNAME + ":" + PORT; private static final TemporaryServer server; private static final Map<String, RemoteWebDriverFactory> factories = new LinkedHashMap<String, RemoteWebDriverFactory>(); static { String service = System.getProperty("org.callimachusproject.test.service"); if (service == null || service.length() == 0) { String email = System.getProperty("org.callimachusproject.test.email"); if (email == null || email.length() == 0) { email = EMAIL; } String password = System.getProperty("org.callimachusproject.test.password"); if (password == null || password.length() == 0) { password = PASSWORD; } server = new TemporaryServerFactory(ORIGIN, PORT, email, password.toCharArray()).createServer(); } else { server = null; } String remotewebdriver = System.getProperty("org.callimachusproject.test.remotewebdriver"); try { final URL url = remotewebdriver == null || remotewebdriver.length() == 0 ? null : new URL(remotewebdriver); if (url == null) { checkAndStore("chrome", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { return new ChromeDriver(); } }); checkAndStore("firefox", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { return new FirefoxDriver(); } }); checkAndStore("ie", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { return new InternetExplorerDriver(); } }); } else { factories.put("chrome", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { DesiredCapabilities caps = DesiredCapabilities.chrome(); caps.setVersion("35"); caps.setCapability("platform", "Windows 7"); caps.setCapability("name", name); caps.setCapability("build", getBuild()); caps.setCapability("tags", getHostTag()); return new RemoteWebDriver(url, caps); } }); factories.put("firefox", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { DesiredCapabilities caps = DesiredCapabilities.firefox(); caps.setCapability("platform", "Windows 7"); caps.setCapability("name", name); caps.setCapability("build", getBuild()); caps.setCapability("tags", getHostTag()); return new RemoteWebDriver(url, caps); } }); factories.put("ie11", new RemoteWebDriverFactory() { public RemoteWebDriver create(String name) { DesiredCapabilities caps = DesiredCapabilities.internetExplorer(); caps.setVersion("11"); caps.setCapability("platform", "Windows 7"); caps.setCapability("name", name); caps.setCapability("build", getBuild()); caps.setCapability("tags", getHostTag()); caps.setCapability("requireWindowFocus", true); return new RemoteWebDriver(url, caps); } }); } } catch (MalformedURLException e) { throw new AssertionError(e); } } public static TestSuite suite() throws Exception { return new TestSuite(); } private static void checkAndStore(String browser, RemoteWebDriverFactory supplier) { try { supplier.create("availability").quit(); factories.put(browser, supplier); } catch (IllegalStateException e) { logger.warn("Local {} web driver not available", browser); } catch (WebDriverException e) { logger.warn("Remote {} web driver not available", browser); } } public static TestSuite suite(Class<? extends BrowserFunctionalTestCase> testcase) throws Exception { TestSuite suite = new TestSuite(testcase.getName()); for (Method method : testcase.getMethods()) { if (method.getName().startsWith("test") && method.getParameterTypes().length == 0 && method.getReturnType().equals(Void.TYPE)) { addTests(testcase, suite, method); } } if (suite.countTestCases() == 0) { suite.addTest(TestSuite.warning(testcase.getName() + " has no public test methods")); } return suite; } private static void addTests(Class<? extends BrowserFunctionalTestCase> testcase, TestSuite suite, Method method) throws InstantiationException, IllegalAccessException { for (String browser : getInstalledWebDrivers().keySet()) { BrowserFunctionalTestCase test = testcase.newInstance(); test.setName(method.getName() + DELIM + browser); suite.addTest(test); } } private static Map<String, RemoteWebDriverFactory> getInstalledWebDrivers() { return factories; } private static String getStartUrl() { if (server == null) { return System.getProperty("org.callimachusproject.test.service"); } else { try { return server.getRepository().getCallimachusUrl(server.getOrigin(), "/"); } catch (OpenRDFException e) { logger.error(e.toString(), e); return ORIGIN + "/"; } } } private static String getHostTag() { String authority = HOSTNAME; if (authority.contains(".")) { return authority.substring(0, authority.indexOf('.')); } else if (authority.contains(":")) { return authority.substring(0, authority.indexOf(':')); } else { return authority; } } private static int getPort() { return URI.create(getStartUrl()).getPort(); } private static String getBuild() { String build = System.getProperty("org.callimachusproject.test.build"); String implementationVersion = Version.class.getPackage().getImplementationVersion(); if (build != null) { return build; } else if (implementationVersion != null) { return implementationVersion; } else { RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); long startTime = bean.getStartTime(); long code = startTime % (1000 * 60 * 60 * 24 * 30) / 1000 / 60; String version = Version.getInstance().getVersionCode(); return version + "+" + Long.toHexString(code); } } public RemoteWebDriver driver; public WebBrowserDriver browser; public CalliPage page; private String folderUrl; public BrowserFunctionalTestCase() { super(); } public BrowserFunctionalTestCase(BrowserFunctionalTestCase parent) { super(); if (parent != null) { this.driver = parent.driver; this.browser = parent.browser; this.page = parent.page; } } public String getUsername() { if (server == null) { return System.getProperty("org.callimachusproject.test.username"); } else { return server.getUsername(); } } public char[] getPassword() { if (server == null) { String password = System.getProperty("org.callimachusproject.test.password"); if (password == null) { return null; } else { return password.toCharArray(); } } else { return server.getPassword(); } } public String getUniqueToken() { return getBrowserName(); } @Override public void runBare() throws Throwable { init(); Throwable exception = null; try { try { setUp(); runTest(); } catch (Throwable running) { exception = running; } finally { try { tearDown(); } catch (Throwable tearingDown) { if (exception == null) { exception = tearingDown; } } } String jobId = driver.getSessionId().toString(); if (exception == null) { recordPass(jobId); } else { recordFailure(jobId, exception); throw exception; } } finally { try { destroy(); } catch (Throwable ex) { if (exception == null) throw ex; } } } public void init() throws Exception, IOException { if (server != null) { server.resume(); String url = getStartUrl(); WebResource home = new WebResource(url); home.get("text/html"); home.ref("/callimachus/scripts.js").get("text/javascript"); home.ref("/callimachus/1.4/styles/callimachus.less?less").get("text/css"); } driver = createWebDriver(); browser = new WebBrowserDriver(driver); page = new CalliPage(browser); } @Override public void setUp() throws Exception { folderUrl = null; String username = getUsername(); if (username != null) { logger.info("Login {}", username); page.openLogin().with(username, getPassword()).login(); } String folderName = getFolderName(); logger.info("Create folder {}", folderName); page.openCurrentFolder().openFolderCreate().with(folderName).create().waitUntilFolderOpen(folderName); folderUrl = driver.getCurrentUrl(); } @Override public void runTest() throws Throwable { Method runMethod = null; try { runMethod = this.getClass().getMethod(getMethodName(), (Class[]) null); } catch (NoSuchMethodException e) { fail("Method \"" + getMethodName() + "\" not found"); } if (!Modifier.isPublic(runMethod.getModifiers())) { fail("Method \"" + getMethodName() + "\" should be public"); } try { runMethod.invoke(this, (Object[]) new Class[0]); } catch (InvocationTargetException e) { e.fillInStackTrace(); throw e.getTargetException(); } catch (IllegalAccessException e) { e.fillInStackTrace(); throw e; } } @Override public void tearDown() throws Exception { if (folderUrl != null) { String folderName = getFolderName(); logger.info("Delete folder {}", folderName); page.open(folderUrl, FolderView.class).waitUntilFolderOpen(folderName).openEdit() .waitUntilTitle(folderName).delete(); } logger.info("Logout"); page.logout(); super.tearDown(); } public void destroy() throws Throwable, Exception { try { if (driver != null) { driver.quit(); } } finally { driver = null; if (server != null) { server.pause(); } } } private RemoteWebDriver createWebDriver() { RemoteWebDriverFactory driverFactory = getInstalledWebDrivers().get(getBrowserName()); if (driverFactory == null) throw new WebDriverException("No WebDrivers available for " + getBrowserName() + ", try one of " + getInstalledWebDrivers().keySet()); String testname = getMethodName(); RemoteWebDriver driver = driverFactory.create(testname); init(driver); return driver; } private void init(RemoteWebDriver driver) { try { driver.setFileDetector(new LocalFileDetector()); } catch (WebDriverException e) { // ignore } int wait = WebBrowserDriver.IMPLICITLY_WAIT; driver.manage().timeouts().implicitlyWait(wait, TimeUnit.SECONDS); driver.manage().timeouts().setScriptTimeout(wait, TimeUnit.SECONDS); driver.navigate().to(getStartUrl()); } private void recordPass(String jobId) throws IOException { int p = getPort(); String port = ""; if (p > 0) { port = ", \"" + p + "\""; } recordTest(jobId, "{\"name\": \"" + getMethodName() + "\", \"build\": \"" + getBuild() + "\", \"tags\": [\"" + getHostTag() + "\"" + port + "], \"video-upload-on-pass\": false, \"passed\": true}", 3); } private void recordFailure(String jobId, Throwable e) throws IOException { int p = getPort(); String port = ""; if (p > 0) { port = ", \"" + p + "\""; } recordTest(jobId, "{\"name\": \"" + getMethodName() + "\", \"build\": \"" + getBuild() + "\", \"tags\": [\"" + getHostTag() + "\"" + port + ", \"" + e.getClass().getSimpleName() + "\"], \"passed\": false}", 3); } private void recordTest(String jobId, String data, int retry) { String remotewebdriver = System.getProperty("org.callimachusproject.test.remotewebdriver"); if (remotewebdriver != null && remotewebdriver.contains("saucelabs.com")) { URI uri = URI.create(remotewebdriver); CloseableHttpClient client = HttpClientBuilder.create().useSystemProperties() .setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(5000).build()).build(); try { try { String info = uri.getUserInfo(); putTestData(info, jobId, data, client); } finally { client.close(); } } catch (IOException ex) { if (retry > 0) { logger.warn(ex.toString(), ex); recordTest(jobId, data, retry - 1); } else { logger.error(ex.toString(), ex); } } catch (RuntimeException ex) { logger.error(ex.toString(), ex); } } } private void putTestData(String info, String jobId, String data, CloseableHttpClient client) throws IOException, ClientProtocolException { String username = decode(info.substring(0, info.indexOf(':')), "UTF-8"); String password = decode(info.substring(info.indexOf(':') + 1), "UTF-8"); UsernamePasswordCredentials cred = new UsernamePasswordCredentials(username, password); AuthScope scope = new AuthScope("saucelabs.com", 80); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(scope, cred); HttpClientContext ctx = HttpClientContext.create(); ctx.setCredentialsProvider(credsProvider); HttpPut put = new HttpPut("http://saucelabs.com/rest/v1/" + username + "/jobs/" + jobId); put.setEntity(new StringEntity(data, ContentType.APPLICATION_JSON)); client.execute(put, new ResponseHandler<Void>() { public Void handleResponse(HttpResponse response) throws ClientProtocolException, IOException { if (300 <= response.getStatusLine().getStatusCode()) { System.err.println(EntityUtils.toString(response.getEntity())); } assertTrue(response.getStatusLine().toString(), 300 > response.getStatusLine().getStatusCode()); return null; } }, ctx); } private String getMethodName() { String name = getName(); if (name == null) return "test"; return name.substring(0, name.indexOf(DELIM)); } private String getBrowserName() { String name = getName(); if (name == null) { Map<String, RemoteWebDriverFactory> map = getInstalledWebDrivers(); if (map.isEmpty()) throw new WebDriverException("No WebDrivers installed"); return map.keySet().iterator().next(); } return name.substring(name.lastIndexOf(DELIM) + 1); } private String getFolderName() { String name = getName(); if (name == null) { name = "test"; } try { return URLEncoder.encode(name, "UTF-8").replace("+", "&") + "'s%20Folder"; } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } }