/* * JBoss, Home of Professional Open Source * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.jboss.weld.examples.numberguess.ftest.cluster; import java.net.MalformedURLException; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.arquillian.container.test.api.ContainerController; import org.jboss.arquillian.container.test.api.Deployer; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.container.test.api.TargetsContainer; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.graphene.Graphene; import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.junit.InSequence; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.weld.examples.numberguess.ftest.Deployments; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.openqa.selenium.By; import org.openqa.selenium.Cookie; import org.openqa.selenium.WebDriver; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * * @author mgencur * @author kpiwko * @author maschmid * */ @RunWith(Arquillian.class) @RunAsClient public class NumberGuessClusterTest { protected String MAIN_PAGE = "home.jsf"; public static final long GRACE_TIME_TO_REPLICATE = 1000; public static final long GRACE_TIME_TO_MEMBERSHIP_CHANGE = 5000; private static final String CONTAINER1 = "container1"; private static final String CONTAINER2 = "container2"; private static final String DEPLOYMENT1 = "dep.container1"; private static final String DEPLOYMENT2 = "dep.container2"; protected By GUESS_MESSAGES = By.id("numberGuess:messages"); protected By GUESS_STATUS = By.xpath("//div[contains(text(),'I'm thinking of ')]"); protected By GUESS_FIELD = By.id("numberGuess:inputGuess"); protected By GUESS_FIELD_WITH_VALUE = By.xpath("//input[@id='numberGuess:inputGuess'][@value=3]"); protected By GUESS_SUBMIT = By.id("numberGuess:guessButton"); protected By GUESS_RESTART = By.id("numberGuess:restartButton"); protected By GUESS_SMALLEST = By.id("numberGuess:smallest"); protected By GUESS_BIGGEST = By.id("numberGuess:biggest"); protected String WIN_MSG = "Correct!"; protected String LOSE_MSG = "No guesses left!"; protected String HIGHER_MSG = "Higher!"; protected String LOWER_MSG = "Lower!"; protected Pattern guessesNumberPattern = Pattern.compile("You have (\\d+) guesses remaining."); private GameState gameState; @ArquillianResource private ContainerController controller; @ArquillianResource private Deployer deployer; @Drone WebDriver driver; String contextPath1; String contextPath2; boolean browsersSwitched = false; @Deployment(name = DEPLOYMENT1, managed=false, testable=false) @TargetsContainer(CONTAINER1) public static WebArchive createTestDeployment1() { return Deployments.createDeployment(); } @Deployment(name = DEPLOYMENT2, managed=false, testable=false) @TargetsContainer(CONTAINER2) public static WebArchive createTestDeployment2() { return Deployments.createDeployment() .addAsWebInfResource(EmptyAsset.INSTANCE, "force-hashcode-change.txt"); } @Before public void before() throws MalformedURLException { // We can't use @ArquillianResource URL here as we are using unmanaged deployments contextPath1 = System.getProperty("node1.contextPath"); contextPath2 = System.getProperty("node2.contextPath"); } protected void resetForm() { Graphene.guardHttp(driver.findElement(GUESS_RESTART)).click(); gameState = null; } protected void enterGuess(int guess) throws InterruptedException { gameState.setGuess(guess); driver.findElement(GUESS_FIELD).clear(); driver.findElement(GUESS_FIELD).sendKeys(String.valueOf(guess)); Graphene.guardHttp(driver.findElement(GUESS_SUBMIT)).click(); } protected boolean isOnGuessPage() { return !(isOnWinPage() || isOnLosePage()); } protected boolean isOnWinPage() { String text = driver.findElement(GUESS_MESSAGES).getText(); return WIN_MSG.equals(text); } protected boolean isOnLosePage() { String text = driver.findElement(GUESS_MESSAGES).getText(); return LOSE_MSG.equals(text); } public String getAddressForSecondInstance() { String loc = driver.getCurrentUrl(); String[] parsedStrings = loc.split("/"); StringBuilder sb = new StringBuilder(); for (int i = 4; i < parsedStrings.length; i++) { sb.append("/").append(parsedStrings[i]); } return sb.toString(); } private Integer getRemainingGuesses() { Matcher m = guessesNumberPattern.matcher(driver.findElement(By.tagName("body")).getText()); if (m.find()) { return Integer.parseInt(m.group(1)); } else { return null; } } /** * Asserts the game state matches what the page displays */ private void updateGameState() { GameState nextState = new GameState(); nextState.setRemainingGuesses(getRemainingGuesses()); nextState.setLargest(Integer.parseInt(driver.findElement(GUESS_BIGGEST).getText())); nextState.setSmallest(Integer.parseInt(driver.findElement(GUESS_SMALLEST).getText())); if (gameState == null) { // Initial state assertEquals("Remaining guesses dosn't match", 10, nextState.getRemainingGuesses()); assertEquals("Smallest numbers dosn't match", 0, nextState.getSmallest()); assertEquals("Largest numbers dosn't match", 100, nextState.getLargest()); } else { nextState.setPreviousGuess(gameState.getGuess()); assertEquals("Remaining guesses dosn't match", gameState.getRemainingGuesses() - 1, nextState.getRemainingGuesses()); boolean higher = driver.findElement(By.tagName("body")).getText().contains(HIGHER_MSG); boolean lower = driver.findElement(By.tagName("body")).getText().contains(LOWER_MSG); assertEquals(lower, (nextState.getLargest() < gameState.getLargest())); if (gameState.getGuess() != 0) { // Bug in numberguess, doesn't display "higher" for zero guess. assertEquals(higher, (nextState.getSmallest() > gameState.getSmallest())); } assertTrue(!lower || !higher); } gameState = nextState; } private void naiveStep() throws InterruptedException { updateGameState(); enterGuess(gameState.getSmallest()); } private void smartStep() throws InterruptedException { updateGameState(); enterGuess(gameState.getSmallest() + ((gameState.getLargest() - gameState.getSmallest()) / 2)); } private void switchBrowsers() throws MalformedURLException { String address = getAddressForSecondInstance(); String sid = driver.manage().getCookieNamed("JSESSIONID").getValue(); String contextPath = browsersSwitched ? contextPath1 : contextPath2; // We navigate to the home page on the second server, just to set the cookies driver.navigate().to(new URL(contextPath)); driver.manage().deleteAllCookies(); driver.manage().addCookie(new Cookie("JSESSIONID", sid)); // Now we navigate for reals driver.navigate().to(new URL(contextPath + "/" + address)); browsersSwitched = !browsersSwitched; } @Test @InSequence(1) public void guessingWithFailoverTest() throws MalformedURLException, InterruptedException { controller.start(CONTAINER1); deployer.deploy(DEPLOYMENT1); controller.start(CONTAINER2); deployer.deploy(DEPLOYMENT2); driver.navigate().to(new URL(contextPath1 + "/" + MAIN_PAGE)); // we always want to enter at least 3 guesses so that we can continue // in the other browser window with expected results do { resetForm(); for (int i = 0; i < 3 && isOnGuessPage(); ++i) { naiveStep(); } } while(!isOnGuessPage()); Thread.sleep(GRACE_TIME_TO_REPLICATE); deployer.undeploy(DEPLOYMENT1); controller.stop(CONTAINER1); Thread.sleep(GRACE_TIME_TO_REPLICATE); switchBrowsers(); while(isOnGuessPage()) { smartStep(); } assertTrue("Win page expected after playing smart.", isOnWinPage()); deployer.undeploy(DEPLOYMENT2); controller.stop(CONTAINER2); } @Test @InSequence(2) public void guessingWithInterleavingTest() throws MalformedURLException, InterruptedException { controller.start(CONTAINER1); deployer.deploy(DEPLOYMENT1); driver.navigate().to(new URL(contextPath1 + "/" + MAIN_PAGE)); for(;;) { smartStep(); if (!isOnGuessPage()) { break; } if (browsersSwitched) { controller.start(CONTAINER1); deployer.deploy(DEPLOYMENT1); Thread.sleep(GRACE_TIME_TO_MEMBERSHIP_CHANGE); deployer.undeploy(DEPLOYMENT2); controller.stop(CONTAINER2); } else { controller.start(CONTAINER2); deployer.deploy(DEPLOYMENT2); Thread.sleep(GRACE_TIME_TO_MEMBERSHIP_CHANGE); deployer.undeploy(DEPLOYMENT1); controller.stop(CONTAINER1); } Thread.sleep(GRACE_TIME_TO_REPLICATE); switchBrowsers(); } assertTrue("Win page expected after playing smart.", isOnWinPage()); if (browsersSwitched) { deployer.undeploy(DEPLOYMENT2); controller.stop(CONTAINER2); } else { deployer.undeploy(DEPLOYMENT1); controller.stop(CONTAINER1); } } }