Commits

Dariusz Kordonski  committed 96a82df

SELENIUM-198 perhaps fixing IE tests with this :) more likely not

  • Participants
  • Parent commits 8ae88f0

Comments (0)

Files changed (9)

File atlassian-pageobjects-api/src/main/java/com/atlassian/pageobjects/binder/LoggerModule.java

+package com.atlassian.pageobjects.binder;
+
+import com.google.inject.AbstractModule;
+import org.slf4j.Logger;
+
+/**
+ * Adds a common SLF4J logger to the injection context.
+ *
+ * @since 2.1
+ */
+public class LoggerModule extends AbstractModule
+{
+    private final Logger logger;
+
+    public LoggerModule(Logger logger)
+    {
+        this.logger = logger;
+    }
+
+    @Override
+    protected void configure()
+    {
+        bind(Logger.class).toInstance(logger);
+    }
+}

File atlassian-webdriver/atlassian-webdriver-core/src/main/java/com/atlassian/webdriver/LifecycleAwareWebDriverGrid.java

 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.lang.ref.WeakReference;
 import java.net.SocketException;
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * Simple lifecycle aware webdriver helper that will setup
- * auto browsers and then retrieve a driver from the factory.
- * Once the driver is running it will be re-used if the same
- * browser property is retrieved again.
- * When the runtime is shutdown it will handle cleaning up the browser.
+ * <p/>
+ * Simple lifecycle aware Webdriver helper that will setup auto browsers and then retrieve a driver from the factory.
+ * Once the driver is running it will be re-used if the same browser property is retrieved again.
+ *
+ * <p/>
+ * When the runtime is shutdown it will handle cleaning up the browser. It also provides a way to manually quit all
+ * the registered drivers/browsers via {@link #shutdown()}. When that method is called, the shutdown hooks for those
+ * browser will be unregistered.
  *
  * @since 2.1.0
  */
 public class LifecycleAwareWebDriverGrid
 {
     private static final Logger log = LoggerFactory.getLogger(LifecycleAwareWebDriverGrid.class);
-    private static Map<String,AtlassianWebDriver> drivers = new HashMap<String,AtlassianWebDriver>();
-    private static AtlassianWebDriver currentDriver;
+    private final static Map<String,AtlassianWebDriver> drivers = new ConcurrentHashMap<String, AtlassianWebDriver>();
+    private volatile static AtlassianWebDriver currentDriver;
+
+    private static final Map<String,WeakReference<Thread>> SHUTDOWN_HOOKS = new ConcurrentHashMap<String, WeakReference<Thread>>();
 
     private LifecycleAwareWebDriverGrid() {}
 
         };
     }
 
+    /**
+     * A manual shut down of the registered drivers. This basically resets the grid to a blank state. The shutdown hooks
+     * for the drivers that have been closed are also removed.
+     *
+     */
+    public static void shutdown()
+    {
+        for (Map.Entry<String,AtlassianWebDriver> driver: drivers.entrySet())
+        {
+            quit(driver.getValue());
+            removeHook(driver);
+        }
+        drivers.clear();
+        SHUTDOWN_HOOKS.clear();
+        currentDriver = null;
+    }
+
+    private static void removeHook(Map.Entry<String, AtlassianWebDriver> driver)
+    {
+        WeakReference<Thread> hookRef = SHUTDOWN_HOOKS.get(driver.getKey());
+        if (hookRef != null && hookRef.get() != null)
+        {
+            Runtime.getRuntime().removeShutdownHook(hookRef.get());
+        }
+    }
+
+    private static void quit(AtlassianWebDriver webDriver)
+    {
+        try
+        {
+            webDriver.quit();
+        }
+        catch (WebDriverException e)
+        {
+            if (!isKnownQuitException(e))
+            {
+                throw e;
+            }
+        }
+    }
+
     private static boolean browserIsConfigured(String browserProperty)
     {
         return drivers.containsKey(browserProperty);
     }
 
     private static void addShutdownHook(final String browserProperty, final WebDriver driver) {
-        Runtime.getRuntime().addShutdownHook(new Thread()
+        final Thread quitter = new Thread()
         {
             @Override
             public void run()
                 try
                 {
                     drivers.remove(browserProperty);
-
-                    boolean isIEDriver = true;
                     if (driver.equals(currentDriver))
                     {
                         currentDriver = null;
                     }
-
-                    if (driver instanceof AtlassianWebDriver)
-                    {
-                        AtlassianWebDriver wrapperDriver = (AtlassianWebDriver) driver;
-                        final WebDriver webDriver = wrapperDriver.getDriver();
-                        isIEDriver = webDriver instanceof InternetExplorerDriver;
-                    }
-                    else
-                    {
-                        isIEDriver = driver instanceof InternetExplorerDriver;
-                    }
-
                     // quitting InternetExplorerDriver in a shutdown hook crashes the JVM with a segfault
-                    if (!isIEDriver)
+                    if (!isIeDriver(driver))
+                    {
                         driver.quit();
+                    }
                 }
                 catch (WebDriverException e)
                 {
                     }
                 }
             }
-        });
+        };
+        SHUTDOWN_HOOKS.put(browserProperty, new WeakReference<Thread>(quitter));
+        Runtime.getRuntime().addShutdownHook(quitter);
+    }
+
+    private static boolean isIeDriver(WebDriver driver)
+    {
+        if (driver instanceof AtlassianWebDriver)
+        {
+            AtlassianWebDriver wrapperDriver = (AtlassianWebDriver) driver;
+            final WebDriver webDriver = wrapperDriver.getDriver();
+            return webDriver instanceof InternetExplorerDriver;
+        }
+        else
+        {
+            return driver instanceof InternetExplorerDriver;
+        }
     }
 
     /**
         String msg = e.getMessage();
 
         if (cause instanceof SocketException)
+        {
             return true;
-
+        }
         // Chrome does not have a cause, so need to check the message.
-        if (msg != null && msg.indexOf("Chrome did not respond to 'WaitForAllTabsToStopLoading'") >= 0)
+        if (msg != null && msg.contains("Chrome did not respond to 'WaitForAllTabsToStopLoading'"))
+        {
             return true;
-
+        }
         return false;
     }
 }

File atlassian-webdriver/atlassian-webdriver-core/src/main/java/com/atlassian/webdriver/testing/rule/DefaultProductContextRules.java

+package com.atlassian.webdriver.testing.rule;
+
+import com.google.inject.Inject;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * <p/>
+ * A version of default, recommended rules that is injectable into the test. Use together with
+ * {@link com.atlassian.webdriver.testing.runner.ProductContextRunner}.
+ *
+ * @since 2.1
+ */
+public final class DefaultProductContextRules
+{
+    private DefaultProductContextRules()
+    {
+        throw new AssertionError("Don't instantiate me");
+    }
+
+    public final static class ForClass implements TestRule
+     {
+         private final RuleChain chain;
+
+         @Inject
+         public ForClass(DriverCleanupRule driverCleanupRule)
+         {
+             this.chain = RuleChain.outerRule(driverCleanupRule);
+         }
+
+         @Override
+         public Statement apply(Statement base, Description description)
+         {
+             return chain.apply(base, description);
+         }
+     }
+
+    public final static class ForMethod implements TestRule
+    {
+        private final RuleChain chain;
+
+        @Inject
+        public ForMethod(IgnoreBrowserRule ignoreBrowserRule, LogPageSourceRule logPageSourceRule,
+                         SessionCleanupRule sessionCleanupRule, WebDriverScreenshotRule webDriverScreenshotRule,
+                         WindowSizeRule windowSizeRule)
+        {
+            this.chain = RuleChain.outerRule(ignoreBrowserRule)
+                    .around(sessionCleanupRule)
+                    .around(windowSizeRule)
+                    .around(webDriverScreenshotRule)
+                    .around(logPageSourceRule);
+        }
+
+        @Override
+        public Statement apply(Statement base, Description description)
+        {
+            return chain.apply(base, description);
+        }
+    }
+
+}

File atlassian-webdriver/atlassian-webdriver-core/src/main/java/com/atlassian/webdriver/testing/rule/DriverCleanupRule.java

+package com.atlassian.webdriver.testing.rule;
+
+import com.atlassian.webdriver.LifecycleAwareWebDriverGrid;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * <p/>
+ * A class rule that cleans up web drivers from the {@link com.atlassian.webdriver.LifecycleAwareWebDriverGrid}
+ * after a specific amount of time elapses since the last test has been run.
+ *
+ * <p/>
+ * Some {@link org.openqa.selenium.WebDriver}s suffer from bugs that prevent shutdown hooks from cleaning them
+ * up properly. If you need to run your tests using those browsers (e.g. Chrome, IE), use this rule to
+ * make sure the browser will be closed after the test suite finishes.
+ *
+ * <p/>
+ * This rule is only effective if you use it in all your tests as a class rule. If you are using an injection context
+ * (e.g. a {@link com.atlassian.pageobjects.TestedProduct} with a {@link com.atlassian.pageobjects.PageBinder}),
+ * make sure this product's life cycle does not exceed this rule's life cycle (which is generally <code>true</code>,
+ * if you use a {@link com.atlassian.webdriver.testing.runner.ProductContextRunner}, or just manually instantiate
+ * the produce per test class).
+ *
+ * <p/>
+ * When instantiating the class clients can set a custom timeout, or use default constructor that sets a 3 seconds
+ * timeout.
+ *
+ * @since 2.1
+ */
+@ThreadSafe
+public class DriverCleanupRule extends TestWatcher
+{
+
+    private static final Timer KILLER_TIMER = new Timer("Atlassian-BrowserKiller", true);
+    private static final Queue<TimerTask> KILLER_TASKS = new ConcurrentLinkedQueue<TimerTask>();
+
+    private final long timeout;
+    private final TimeUnit timeUnit;
+
+    public DriverCleanupRule(long timeout, TimeUnit timeUnit)
+    {
+        this.timeout = timeout;
+        this.timeUnit = timeUnit;
+    }
+
+    public DriverCleanupRule()
+    {
+        this(3, TimeUnit.SECONDS);
+    }
+
+    @Override
+    protected void starting(Description description)
+    {
+        for (Iterator<TimerTask> tasks = KILLER_TASKS.iterator(); tasks.hasNext();)
+        {
+            tasks.next().cancel();
+            tasks.remove();
+        }
+    }
+
+    @Override
+    protected void finished(Description description)
+    {
+        final TimerTask newKiller = new BrowserKiller();
+        KILLER_TASKS.add(newKiller);
+        KILLER_TIMER.schedule(newKiller, timeUnit.toMillis(timeout));
+    }
+
+    private static final class BrowserKiller extends TimerTask
+    {
+
+        @Override
+        public void run()
+        {
+            LifecycleAwareWebDriverGrid.shutdown();
+        }
+    }
+}

File atlassian-webdriver/atlassian-webdriver-core/src/main/java/com/atlassian/webdriver/testing/rule/TestedProductRule.java

  *
  * @since 2.1.0
  * @see InjectionRules
+ * @see com.atlassian.webdriver.testing.runner.ProductContextRunner
  */
 public class TestedProductRule<T extends TestedProduct<WebDriverTester>> implements TestRule, TestedProduct<WebDriverTester>
 {

File atlassian-webdriver/atlassian-webdriver-core/src/test/java/com/atlassian/webdriver/it/AbstractFileBasedServerTest.java

 
 import com.atlassian.webdriver.it.pageobjects.SimpleTestedProduct;
 import com.atlassian.webdriver.testing.annotation.TestedProductClass;
-import com.atlassian.webdriver.testing.rule.IgnoreBrowserRule;
-import com.atlassian.webdriver.testing.rule.SessionCleanupRule;
-import com.atlassian.webdriver.testing.rule.WebDriverScreenshotRule;
+import com.atlassian.webdriver.testing.rule.DefaultProductContextRules;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.runner.RunWith;
 
 {
     @Inject protected static SimpleTestedProduct product;
 
-    @Inject @Rule public IgnoreBrowserRule ignoreRule;
-    @Inject @Rule public WebDriverScreenshotRule webDriverScreenshotRule;
-    @Inject @Rule public SessionCleanupRule sessionCleanupRule;
+    @Inject @ClassRule public static DefaultProductContextRules.ForClass classRules;
+    @Inject @Rule public DefaultProductContextRules.ForMethod methodRules;
 
 }

File atlassian-webdriver/atlassian-webdriver-core/src/test/java/com/atlassian/webdriver/it/pageobjects/SimpleTestedProduct.java

 import com.atlassian.pageobjects.TestedProductFactory;
 import com.atlassian.pageobjects.binder.BrowserModule;
 import com.atlassian.pageobjects.binder.InjectPageBinder;
+import com.atlassian.pageobjects.binder.LoggerModule;
 import com.atlassian.pageobjects.binder.StandardModule;
 import com.atlassian.webdriver.AtlassianWebDriverModule;
 import com.atlassian.webdriver.pageobjects.DefaultWebDriverTester;
 import com.atlassian.webdriver.pageobjects.WebDriverTester;
+import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 
         this.webDriverTester =  new DefaultWebDriverTester();
         this.pageBinder = new InjectPageBinder(productInstance, webDriverTester, new StandardModule(this),
-                new AtlassianWebDriverModule(this), new BrowserModule());
+                new AtlassianWebDriverModule(this), new BrowserModule(),
+                new LoggerModule(LoggerFactory.getLogger(SimpleTestedProduct.class)));
     }
 
     public <P extends Page> P visit(Class<P> pageClass, Object... args)

File atlassian-webdriver/atlassian-webdriver-core/src/test/java/com/atlassian/webdriver/it/tests/TestArgumentConversionBug.java

 package com.atlassian.webdriver.it.tests;
 
 import com.atlassian.pageobjects.browser.Browser;
+import com.atlassian.pageobjects.browser.IgnoreBrowser;
 import com.atlassian.webdriver.AtlassianWebDriver;
 import com.atlassian.webdriver.it.AbstractFileBasedServerTest;
 import com.atlassian.webdriver.it.pageobjects.page.ArgumentConversionBugPage;
-import com.atlassian.webdriver.testing.annotation.IgnoreBrowser;
 import org.junit.Before;
 import org.junit.Test;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 
+import javax.inject.Inject;
 import java.util.List;
 
 @IgnoreBrowser(Browser.HTMLUNIT_NOJS)
 {
 
     ArgumentConversionBugPage argConversionBugPage;
-    AtlassianWebDriver driver;
+    @Inject AtlassianWebDriver driver;
 
     @Before
     public void init()
     {
         argConversionBugPage = product.visit(ArgumentConversionBugPage.class);
-        driver = product.getTester().getDriver();
     }
 
     // This test is checking that the arg processing bug has been fixed.

File atlassian-webdriver/atlassian-webdriver-core/src/test/java/com/atlassian/webdriver/it/tests/TestJavaScriptUtils.java

 package com.atlassian.webdriver.it.tests;
 
 import com.atlassian.pageobjects.browser.Browser;
+import com.atlassian.pageobjects.browser.IgnoreBrowser;
+import com.atlassian.pageobjects.browser.RequireBrowser;
 import com.atlassian.webdriver.AtlassianWebDriver;
 import com.atlassian.webdriver.it.AbstractFileBasedServerTest;
 import com.atlassian.webdriver.it.pageobjects.page.JavaScriptUtilsPage;
-import com.atlassian.webdriver.testing.annotation.IgnoreBrowser;
-import com.atlassian.webdriver.testing.annotation.TestBrowser;
 import com.atlassian.webdriver.utils.MouseEvents;
 import org.junit.Before;
 import org.junit.Test;
     }
 
     @Test
-    @TestBrowser("htmlunit")
+    @RequireBrowser(Browser.HTMLUNIT)
     public void testCssHoverBreaksForHtmlUnit()
     {
         WebElement hoveringDiv = driver.findElement(By.id("hovering-element"));
     }
 
     @Test
-    @TestBrowser("firefox")
+    @RequireBrowser(Browser.FIREFOX)
     public void testCssHoverBreaksForFirefox()
     {
         WebElement hoveringDiv = driver.findElement(By.id("hovering-element"));
 
     // https://studio.atlassian.com/browse/SELENIUM-175
     @Test
-    @TestBrowser("ie")
+    @RequireBrowser(Browser.IE)
     public void testJQueryHoverBreaksForIE()
     {
         WebElement hoveringDiv = driver.findElement(By.id("hovering-jquery-element"));