Java tutorial
// Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The SFC licenses this file // to you 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.openqa.selenium.server.mock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.apache.commons.logging.Log; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.openqa.jetty.log.LogFactory; import org.openqa.selenium.remote.server.log.LoggingManager; import org.openqa.selenium.remote.server.log.LoggingOptions; import org.openqa.selenium.remote.server.log.StdOutHandler; import org.openqa.selenium.remote.server.log.TerseFormatter; import org.openqa.selenium.server.DefaultRemoteCommand; import org.openqa.selenium.server.InjectionHelper; import org.openqa.selenium.server.RemoteCommand; import org.openqa.selenium.server.RemoteControlConfiguration; import org.openqa.selenium.server.SeleniumServer; import org.openqa.selenium.server.WindowClosedException; import org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory; import java.io.File; import java.util.logging.Handler; import java.util.logging.Logger; public class MockPIFrameUnitTest { static final Log LOGGER = LogFactory.getLog(MockPIFrameUnitTest.class); @Rule public TestName name = new TestName(); private static final String DRIVER_URL = "http://localhost:4444/selenium-server/driver/"; private static int timeoutInSeconds = 10; private String sessionId; private SeleniumServer server; @Before public void setUp() throws Exception { configureLogging(); RemoteControlConfiguration configuration = new RemoteControlConfiguration(); configuration.setTimeoutInSeconds(timeoutInSeconds); configuration.setProxyInjectionModeArg(true); server = new SeleniumServer(false, configuration); server.getBrowserLauncherFactory().addBrowserLauncher("dummy", DummyBrowserLauncher.class); server.start(); InjectionHelper.setFailOnError(false); LOGGER.info("Starting " + name.getMethodName()); } private LoggingOptions configureLogging() throws Exception { // SeleniumServer.setDebugMode(true); LoggingOptions configuration = new LoggingOptions(); File target = new File("target"); if (target.exists() && target.isDirectory()) { configuration.setLogOutFile(new File(target, "mockpiframe.log")); } else { configuration.setLogOutFile(new File("mockpiframe.log")); } LoggingManager.configureLogging(configuration, false); Logger logger = Logger.getLogger(""); for (Handler handler : logger.getHandlers()) { if (handler instanceof StdOutHandler) { handler.setFormatter(new TerseFormatter(true)); break; } } return configuration; } @After public void tearDown() { server.stop(); LoggingManager.configureLogging(new LoggingOptions(), false); DummyBrowserLauncher.clearSessionId(); InjectionHelper.setFailOnError(true); } /** * start a basic browser session */ @Test public void testStartSession() { startSession(); } /** * start a basic browser session * * @return the currently running MockPIFrame */ public MockPIFrame startSession() { // 1. driver requests new session DriverRequest driverRequest = sendCommand("getNewBrowserSession", "*dummy", "http://x"); // 2. server generates new session, awaits browser launch sessionId = waitForSessionId(driverRequest); LOGGER.debug("browser starting session " + sessionId); // 3. browser starts, requests work MockPIFrame frame = new MockPIFrame(DRIVER_URL, sessionId, "frame1"); LOGGER.debug("browser sending start message"); frame.seleniumStart(); // 4. server requests identification, asks for "getTitle" LOGGER.debug("browser expecting getTitle command, blocking.."); frame.expectCommand("getTitle", "", ""); // 5. browser replies "selenium remote runner" to getTitle frame.sendResult("OK,selenium remote runner"); // 6. server requests setContext frame.expectCommand("setContext", sessionId, ""); // 7. browser replies "OK" to setContext frame.sendResult("OK"); // 8. server replies "OK,123" to driver driverRequest.expectResult("OK," + sessionId); return frame; } /** * create a session and issue a valid "open" command */ @Test public void testRegularOpen() { openUrl(); } private MockPIFrame openUrl() { MockPIFrame frame1 = startSession(); // 1. driver issues an "open" command DriverRequest driverRequest = sendCommand("open", "blah.html", ""); // 2. original frame receives open request; replies "OK" and then closes frame1.expectCommand("open", "blah.html", ""); frame1.sendResult("OK"); // 3. old frame unloads frame1.sendClose(); // 4. new frame with same frame address loads and replies "START" MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); frame2.seleniumStart(); // 5. server automatically begins waiting for load; requests frame2 for "getTitle" frame2.expectCommand("getTitle", "", ""); // 6. browser replies "blah.html" frame2.sendResult("OK,blah.html"); // 7. server replies "OK" to driver's original "open" command driverRequest.expectResult("OK"); return frame2; } /** * create a session and issue a valid open command, simulating an out-of-order response from the * browser, as the new page load request comes in before the "OK" from the original page */ @Test public void testEvilOpen() { MockPIFrame frame1 = startSession(); // 1. driver issues an "open" command DriverRequest driverRequest = sendCommand("open", "blah.html", ""); // 2. original frame receives open request // ... but doesn't reply "OK" yet (this is the evil part!) frame1.expectCommand("open", "blah.html", ""); // 3. old frame unloads; new frame with same frame address loads and replies "START" MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); frame2.seleniumStart(); // X. original frame finally manages to reply "OK" to original "open" command sleepForAtLeast(100); frame1.sendResult("OK"); // 4. server automatically begins waiting for load; asks frame2 for "getTitle" frame2.expectCommand("getTitle", "", ""); // 5. browser replies "blah.html" frame2.sendResult("OK,blah.html"); // 6. server replies "OK" to driver's original "open" command driverRequest.expectResult("OK"); } /** * Click, then waitForPageToLoad */ @Test public void testClickThenWait() { MockPIFrame frame1 = startSession(); DriverRequest driverRequest = sendCommand("click", "foo", ""); frame1.expectCommand("click", "foo", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); driverRequest = sendCommand("waitForPageToLoad", "5000", ""); frame1.sendClose().expectCommand("testComplete", "", ""); MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); frame2.seleniumStart(); frame2.expectCommand("getTitle", "", ""); frame2.sendResult("OK,newpage.html"); driverRequest.expectResult("OK"); driverRequest = sendCommand("click", "bar", ""); frame2.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * Click, then wait for page to load; but this time, frame2 starts before frame1 declares close */ @Test public void testEvilClickThenWait() { MockPIFrame frame1 = startSession(); BrowserRequest browserRequest = frame1.getMostRecentRequest(); DriverRequest driverRequest = sendCommand("click", "foo", ""); browserRequest.expectCommand("click", "foo", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); driverRequest = sendCommand("waitForPageToLoad", "5000", ""); // this is the evil part: frame2 starts before frame1 declares close MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); browserRequest = frame2.seleniumStart(); sleepForAtLeast(100); frame1.sendClose().expectCommand("testComplete", "", ""); browserRequest.expectCommand("getTitle", "", ""); browserRequest = frame2.sendResult("OK,newpage.html"); driverRequest.expectResult("OK"); driverRequest = sendCommand("click", "bar", ""); browserRequest.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * click, then wait for page to load, but frame1 may send close before sending OK result */ @Test public void testEvilClickThenWaitRaceCondition() { MockPIFrame frame1 = startSession(); BrowserRequest browserRequest = frame1.getMostRecentRequest(); DriverRequest driverRequest = sendCommand("click", "foo", ""); browserRequest.expectCommand("click", "foo", ""); // ideally, "OK" arrives first; in practice, server may handle these requests in any order int sequenceNumber = frame1.getSequenceNumber(); frame1.setSequenceNumber(sequenceNumber + 1); frame1.sendClose(); sleepForAtLeast(100); frame1.setSequenceNumber(sequenceNumber); frame1.sendResult("OK"); driverRequest.expectResult("OK"); driverRequest = sendCommand("waitForPageToLoad", "5000", ""); MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); browserRequest = frame2.seleniumStart(); browserRequest.expectCommand("getTitle", "", ""); browserRequest = frame2.sendResult("OK,newpage.html"); driverRequest.expectResult("OK"); driverRequest = sendCommand("click", "bar", ""); browserRequest.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * Click, then sleep for a while, then send commands. We expect this to work; waitForPageToLoad * should not be mandatory */ @Test public void testClickAndPause() { MockPIFrame frame1 = startSession(); BrowserRequest browserRequest = frame1.getMostRecentRequest(); DriverRequest driverRequest = sendCommand("click", "foo", ""); browserRequest.expectCommand("click", "foo", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); frame1.sendClose(); MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); browserRequest = frame2.seleniumStart(); sleepForAtLeast(100); driverRequest = sendCommand("click", "bar", ""); browserRequest.expectCommand("getTitle", "", ""); browserRequest = frame2.sendResult("OK,blah"); browserRequest.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * Click, then sleep for a while, then start waiting for page to load. WaitForPageToLoad should * work regardles of whether it runs before or after the page loads. */ @Test public void testClickAndPauseThenWait() { MockPIFrame frame1 = startSession(); BrowserRequest browserRequest = frame1.getMostRecentRequest(); DriverRequest driverRequest = sendCommand("click", "foo", ""); browserRequest.expectCommand("click", "foo", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); frame1.sendClose(); MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); browserRequest = frame2.seleniumStart(); sleepForAtLeast(100); driverRequest = sendCommand("waitForPageToLoad", "5000", ""); browserRequest.expectCommand("getTitle", "", ""); browserRequest = frame2.sendResult("OK,newpage.html"); driverRequest.expectResult("OK"); driverRequest = sendCommand("click", "bar", ""); browserRequest.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * Click, causing a page load (i.e. close and open). Try clicking again too early; you'll get a * WindowClosedException, even if you click more than once. Eventually the page will load and then * clicking will work again. */ @Test public void testClickForgetToWait() { MockPIFrame frame1 = startSession(); BrowserRequest browserRequest = frame1.getMostRecentRequest(); DriverRequest driverRequest = sendCommand("click", "foo", ""); browserRequest.expectCommand("click", "foo", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); driverRequest = sendCommand("click", "bar", ""); frame1.sendClose(); driverRequest.expectResult(WindowClosedException.WINDOW_CLOSED_ERROR); sendCommand("click", "bar", "").expectResult(WindowClosedException.WINDOW_CLOSED_ERROR); sendCommand("click", "bar", "").expectResult(WindowClosedException.WINDOW_CLOSED_ERROR); MockPIFrame frame2 = new MockPIFrame(DRIVER_URL, sessionId, "frame2"); browserRequest = frame2.seleniumStart(); sleepForAtLeast(100); driverRequest = sendCommand("click", "bar", ""); browserRequest.expectCommand("getTitle", "", ""); browserRequest = frame2.sendResult("OK,blah"); browserRequest.expectCommand("click", "bar", ""); frame2.sendResult("OK"); driverRequest.expectResult("OK"); } /** * Test out the retryLast logic. */ @Test public void testRetryLast() throws Exception { MockPIFrame frame = startSession(); // 1. driver requests getTitle DriverRequest getTitle = sendCommand("getTitle", "", ""); // 2. browser receives getTitle; replies "OK,foo" frame.expectCommand("getTitle", "", ""); frame.sendResult("OK,foo"); // 3. driver receives "OK,foo" getTitle.expectResult("OK,foo"); // 4. browser waits around for another command that never arrives. In 10 seconds, server replies // "retryLast" frame.expectCommand("retryLast", "", ""); // 5. browser retries frame.sendRetry(); // 6. driver requests click DriverRequest click = sendCommand("click", "foo", ""); // 7. browser receives click; replies "OK" frame.expectCommand("click", "foo", ""); frame.sendResult("OK"); // 8. server receives "OK" click.expectResult("OK"); } /** * This test is fragile with respect to the length of the timeout. The FrameGroupCommandQueue, * which provides the expected string in the last assert, does not receive the same timeout as a * parameter as is set here. This test may need to be more explicit about which timeout is truly * being set and testing the timeout values along the way. * * @throws Exception */ @Test public void testSetTimeout() throws Exception { MockPIFrame frame = startSession(); int timeout = 4000; // 1. driver requests setTimeout, server replies "OK" without contacting the browser sendCommand("setTimeout", "100", "").expectResult("OK"); // 2. driver requests open DriverRequest open = sendCommand("open", "blah.html", "", timeout); // 3. original frame receives open request; replies "OK" frame.expectCommand("open", "blah.html", ""); frame.sendResult("OK"); // 4. normally, a new frame instance would come into existence, and // send back a "START". But instead, too much time passes. sleepForAtLeast(timeout); // 5. server replies to driver with an error message String result = open.getResult(); boolean hasTimeoutMessage = result.contains("timed out waiting for window"); assertTrue("wrong error message on timeout", hasTimeoutMessage); } /** * Open a subWindow, close the subWindow, select the mainWindow, and send it a command. */ @Test public void testMultiWindow() throws Exception { MockPIFrame frame = openUrl(); MockPIFrame subWindow = openSubWindow(frame); // Send subWindow a "close" command DriverRequest driverRequest1 = sendCommand("close", "", ""); subWindow.expectCommand("close", "", ""); subWindow.sendResult("OK"); subWindow.sendClose(); driverRequest1.expectResult("OK"); sendCommand("selectWindow", "null", "").expectResult("OK"); DriverRequest driverRequest = sendCommand("doubleClick", "", ""); frame.expectCommand("doubleClick", "", ""); frame.sendResult("OK"); driverRequest.expectResult("OK"); } private void sleepForAtLeast(long ms) { if (ms > 0) { long now = System.currentTimeMillis(); long deadline = now + ms; while (now < deadline) { try { Thread.sleep(deadline - now); now = deadline; // terminates loop } catch (InterruptedException ie) { now = System.currentTimeMillis(); } } } } /** * Open a subWindow, close the subWindow, and send a command to the closed subWindow. This will * fail; then we select the main window and send commands to it. * <p/> * This test passes when run singly or as part of the MockPIFrameUnitTest, but does not pass when * run in Eclipse as part of the UnitTestSuite. Raising the timeout does not necessarily help. * test. */ @Test public void testEvilClosingWindow() throws Exception { MockPIFrame frame = startSession(); MockPIFrame subWindow = openSubWindow(frame); BrowserRequest mainBrowserRequest = frame.getMostRecentRequest(); // Send subWindow a "close" command DriverRequest driverRequest = sendCommand("close", "", ""); subWindow.expectCommand("close", "", ""); subWindow.sendResult("OK"); subWindow.sendClose(); driverRequest.expectResult("OK"); // The user is SUPPOSED to selectWindow(null) here, but in this test, he forgot // sendCommand("selectWindow", "null", "").expectResult("OK"); sendCommand("doubleClick", "", "").expectResult(WindowClosedException.WINDOW_CLOSED_ERROR); sendCommand("doubleClick", "", "").expectResult(WindowClosedException.WINDOW_CLOSED_ERROR); sendCommand("selectWindow", "null", "").expectResult("OK"); // sleep for a while. for evilness. Thread.sleep(RemoteControlConfiguration.DEFAULT_RETRY_TIMEOUT_IN_SECONDS / 2); mainBrowserRequest.expectCommand("retryLast", "", ""); mainBrowserRequest = frame.sendRetry(); // send a new command driverRequest = sendCommand("submit", "", ""); mainBrowserRequest.expectCommand("submit", "", ""); // sleep less than the command timeout Thread.sleep(timeoutInSeconds / 2); mainBrowserRequest = frame.sendResult("OK"); driverRequest.expectResult("OK"); } private MockPIFrame openSubWindow(MockPIFrame frame1) { // Let's say this page has an "openWindow" button // OK, let's click on it DriverRequest driverRequest = sendCommand("click", "openWindow", ""); frame1.expectCommand("click", "openWindow", ""); frame1.sendResult("OK"); driverRequest.expectResult("OK"); // wait for subWindow to popup driverRequest = sendCommand("waitForPopUp", "subWindow", "2000"); MockPIFrame subWindow = new MockPIFrame(DRIVER_URL, sessionId, "subWindowId", "top", "subWindow"); subWindow.seleniumStart(); subWindow.expectCommand("getTitle", "", ""); subWindow.sendResult("OK,Sub Window"); driverRequest.expectResult("OK"); // select the subWindow sendCommand("selectWindow", "subWindow", "").expectResult("OK"); // send the subWindow a type command driverRequest = sendCommand("type", "subWindowLink", "foo"); subWindow.expectCommand("type", "subWindowLink", "foo"); subWindow.sendResult("OK"); driverRequest.expectResult("OK"); return subWindow; } /** * Open "frames.html", which we'll imagine has two subFrames: subFrame0 and subFrame1 * * @return a set containing a top frame and two subframes */ public SmallFrameSet openSubFrames() { MockPIFrame oldFrame = startSession(); DriverRequest driverRequest = sendCommand("open", "frames.html", ""); oldFrame.expectCommand("open", "frames.html", ""); oldFrame.sendResult("OK"); oldFrame.sendClose(); MockPIFrame subFrame0 = new MockPIFrame(DRIVER_URL, sessionId, "subFrame0Id", "top.frames[0]", ""); MockPIFrame subFrame1 = new MockPIFrame(DRIVER_URL, sessionId, "subFrame1Id", "top.frames[1]", ""); subFrame0.seleniumStart(); subFrame1.seleniumStart(); // topFrame opens last, after his subFrames have finished loading MockPIFrame topFrame = new MockPIFrame(DRIVER_URL, sessionId, "frameId"); topFrame.seleniumStart(); topFrame.expectCommand("getTitle", "", ""); topFrame.sendResult("OK,frames.html"); driverRequest.expectResult("OK"); SmallFrameSet set = new SmallFrameSet(topFrame, subFrame0, subFrame1); return set; } /** * Open a page that has subframes */ @Test public void testSubFrames() { openSubFrames(); } /** * Select a subFrame, then send an "open" command to that subFrame */ @Test public void testFramesOpen() { SmallFrameSet set = openSubFrames(); DriverRequest driverRequest = sendCommand("selectFrame", "subFrame1", ""); set.topFrame.expectCommand("getWhetherThisFrameMatchFrameExpression", "top", "subFrame1"); set.topFrame.sendResult("OK,false"); set.subFrame0.expectCommand("getWhetherThisFrameMatchFrameExpression", "top", "subFrame1"); set.subFrame0.sendResult("OK,false"); set.subFrame1.expectCommand("getWhetherThisFrameMatchFrameExpression", "top", "subFrame1"); set.subFrame1.sendResult("OK,true"); driverRequest.expectResult("OK"); driverRequest = sendCommand("open", "blah.html", ""); set.subFrame1.expectCommand("open", "blah.html", ""); set.subFrame1.sendResult("OK"); set.subFrame1.sendClose(); MockPIFrame newSubFrame1 = new MockPIFrame(DRIVER_URL, sessionId, "newSubFrame1", "top.frames[1]", ""); newSubFrame1.seleniumStart(); newSubFrame1.expectCommand("getTitle", "", ""); newSubFrame1.sendResult("OK,blah.html"); driverRequest.expectResult("OK"); } private class SmallFrameSet { MockPIFrame topFrame; MockPIFrame subFrame0; MockPIFrame subFrame1; public SmallFrameSet(MockPIFrame topFrame, MockPIFrame subFrame0, MockPIFrame subFrame1) { super(); this.topFrame = topFrame; this.subFrame0 = subFrame0; this.subFrame1 = subFrame1; } } /** * Try sending two commands at once */ @Test @Ignore public void testDoubleCommand() throws Exception { MockPIFrame frame = startSession(); BrowserRequest browserRequest = frame.getMostRecentRequest(); // 1. driver requests click "foo" DriverRequest clickFoo = sendCommand("click", "foo", ""); sleepForAtLeast(100); // 2. before the browser can respond, driver requests click "bar" DriverRequest clickBar = sendCommand("click", "bar", ""); browserRequest.expectCommand("click", "foo", ""); browserRequest = frame.sendResult("OK"); browserRequest.expectCommand("click", "bar", ""); frame.sendResult("OK"); assertEquals("click foo result got mangled", "OK", clickFoo.getResult()); assertEquals("click bar result got mangled", "OK", clickBar.getResult()); } /** * Extracts a sessionId from the DummyBrowserLauncher, so we can use it in our tests. Note that * the original driver request won't be resolved until some MockPIFrame is launched with the new * sessionId, so we need to extract it prior to calling getNewBrowserSession.getResult(). * * @param getNewBrowserSession a not-yet-resolved request to get a new browser session; used to * get an error message if we're forced to give up * @return the sessionId of the requested session */ private String waitForSessionId(DriverRequest getNewBrowserSession) { // wait until timeout long now = System.currentTimeMillis(); long timeout = AsyncHttpRequest.DEFAULT_TIMEOUT; long finish = now + timeout; if (timeout == 0) { finish = Long.MAX_VALUE; } sleepForAtLeast(10); String sessionId1; String result = null; while (System.currentTimeMillis() < finish) { // DummyBrowserLauncher records its sessionId in a static variable; extract it here sessionId1 = DummyBrowserLauncher.getSessionId(); if (sessionId1 != null) { return sessionId1; } if (!getNewBrowserSession.isAlive()) { // something must have gone wrong try { result = getNewBrowserSession.getResult(); } catch (Exception e) { throw new RuntimeException("sessionId never appeared", e); } throw new RuntimeException("sessionId never appeared, getNewBrowserSession said: " + result); } // The DBL must not have been launched yet; keep waiting sleepForAtLeast(10); } sessionId1 = DummyBrowserLauncher.getSessionId(); if (sessionId1 != null) { return sessionId1; } // sessionId never appeared; something must have gone wrong try { result = getNewBrowserSession.getResult(); } catch (Exception e) { throw new RuntimeException("sessionId never appeared", e); } throw new RuntimeException("sessionId never appeared, getNewBrowserSession said: " + result); } private DriverRequest sendCommand(String cmd, String arg1, String arg2, int timeoutInMillis) { return sendCommand(new DefaultRemoteCommand(cmd, arg1, arg2), timeoutInMillis); } private DriverRequest sendCommand(String cmd, String arg1, String arg2) { return sendCommand(new DefaultRemoteCommand(cmd, arg1, arg2), AsyncHttpRequest.DEFAULT_TIMEOUT); } private DriverRequest sendCommand(RemoteCommand cmd, int timeoutInMillis) { LOGGER.info("Driver sends " + cmd + " on session " + sessionId); return DriverRequest.request(DRIVER_URL, cmd, sessionId, timeoutInMillis); } }