Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

TB3Runner.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.tests.tb3;
  17. import java.lang.annotation.Annotation;
  18. import java.lang.reflect.Field;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.lang.reflect.Modifier;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.LinkedList;
  25. import java.util.List;
  26. import java.util.concurrent.ExecutorService;
  27. import java.util.concurrent.Executors;
  28. import org.apache.http.params.HttpConnectionParams;
  29. import org.apache.http.params.HttpParams;
  30. import org.junit.Ignore;
  31. import org.junit.Test;
  32. import org.junit.runners.BlockJUnit4ClassRunner;
  33. import org.junit.runners.Parameterized;
  34. import org.junit.runners.model.FrameworkMethod;
  35. import org.junit.runners.model.InitializationError;
  36. import org.junit.runners.model.Statement;
  37. import org.openqa.selenium.remote.DesiredCapabilities;
  38. import org.openqa.selenium.remote.HttpCommandExecutor;
  39. import org.openqa.selenium.remote.internal.HttpClientFactory;
  40. import com.vaadin.tests.annotations.TestCategory;
  41. import com.vaadin.tests.tb3.AbstractTB3Test.BrowserUtil;
  42. import com.vaadin.tests.tb3.MultiBrowserTest.Browser;
  43. /**
  44. * This runner is loosely based on FactoryTestRunner by Ted Young
  45. * (http://tedyoung.me/2011/01/23/junit-runtime-tests-custom-runners/). The
  46. * generated test names give information about the parameters used (unlike
  47. * {@link Parameterized}).
  48. *
  49. * @since 7.1
  50. */
  51. public class TB3Runner extends BlockJUnit4ClassRunner {
  52. /**
  53. * Socket timeout for HTTP connections to the grid hub. The connection is
  54. * closed after 15 minutes of inactivity to avoid builds hanging for up to
  55. * three hours per connection if the test client crashes/hangs.
  56. */
  57. private static final int SOCKET_TIMEOUT = 15 * 60 * 1000;
  58. /**
  59. * This is the total limit of actual JUnit test instances run in parallel
  60. */
  61. private static final int MAX_CONCURRENT_TESTS;
  62. /**
  63. * This is static so it is shared by all tests running concurrently on the
  64. * same machine and thus can limit the number of threads in use.
  65. */
  66. private static final ExecutorService service;
  67. static {
  68. if (localWebDriverIsUsed()) {
  69. MAX_CONCURRENT_TESTS = 10;
  70. } else {
  71. MAX_CONCURRENT_TESTS = 50;
  72. }
  73. service = Executors.newFixedThreadPool(MAX_CONCURRENT_TESTS);
  74. // reduce socket timeout to avoid tests hanging for three hours
  75. try {
  76. Field field = HttpCommandExecutor.class
  77. .getDeclaredField("httpClientFactory");
  78. assert (Modifier.isStatic(field.getModifiers()));
  79. field.setAccessible(true);
  80. field.set(null, new HttpClientFactory() {
  81. @Override
  82. public HttpParams getHttpParams() {
  83. HttpParams params = super.getHttpParams();
  84. // fifteen minute timeout
  85. HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
  86. return params;
  87. }
  88. });
  89. } catch (Exception e) {
  90. e.printStackTrace();
  91. throw new RuntimeException(
  92. "Changing socket timeout for TestBench failed", e);
  93. }
  94. }
  95. protected static boolean localWebDriverIsUsed() {
  96. String useLocalWebDriver = System.getProperty("useLocalWebDriver");
  97. return useLocalWebDriver != null
  98. && useLocalWebDriver.toLowerCase().equals("true");
  99. }
  100. public TB3Runner(Class<?> klass) throws InitializationError {
  101. super(klass);
  102. setScheduler(new ParallelScheduler(service));
  103. }
  104. @Override
  105. protected List<FrameworkMethod> computeTestMethods() {
  106. List<FrameworkMethod> tests = new LinkedList<FrameworkMethod>();
  107. if (!AbstractTB3Test.class.isAssignableFrom(getTestClass()
  108. .getJavaClass())) {
  109. throw new RuntimeException(getClass().getName() + " only supports "
  110. + AbstractTB3Test.class.getName());
  111. }
  112. try {
  113. AbstractTB3Test testClassInstance = getTestClassInstance();
  114. Collection<DesiredCapabilities> desiredCapabilities = getDesiredCapabilities(testClassInstance);
  115. TestNameSuffix testNameSuffixProperty = findAnnotation(
  116. testClassInstance.getClass(), TestNameSuffix.class);
  117. for (FrameworkMethod m : getTestMethods()) {
  118. // No browsers available for this test, so we need to
  119. // wrap the test method inside IgnoredTestMethod.
  120. // This will add @Ignore annotation to it.
  121. if (desiredCapabilities.size() <= 0
  122. || categoryIsExcludedOrNotExcplicitlyIncluded()) {
  123. tests.add(new IgnoredTestMethod(m.getMethod()));
  124. } else {
  125. for (DesiredCapabilities capabilities : desiredCapabilities) {
  126. TB3Method method = new TB3Method(m.getMethod(),
  127. capabilities);
  128. if (testNameSuffixProperty != null) {
  129. method.setTestNameSuffix("-"
  130. + System.getProperty(testNameSuffixProperty
  131. .property()));
  132. }
  133. tests.add(method);
  134. }
  135. }
  136. }
  137. } catch (Exception e) {
  138. throw new RuntimeException("Error retrieving browsers to run on", e);
  139. }
  140. return tests;
  141. }
  142. private boolean categoryIsExcludedOrNotExcplicitlyIncluded() {
  143. Class<?> c = getTestClass().getJavaClass();
  144. if (categoryIsExcluded(c)) {
  145. return true;
  146. }
  147. if (explicitInclusionIsUsed()) {
  148. return !categoryIsIncluded(c);
  149. }
  150. return false;
  151. }
  152. private boolean categoryIsIncluded(Class<?> c) {
  153. String include = System.getProperty("categories.include");
  154. if (include != null && include.trim().length() > 0) {
  155. return hasCategoryFor(c, include.toLowerCase().trim());
  156. }
  157. return false;
  158. }
  159. private static boolean explicitInclusionIsUsed() {
  160. String include = System.getProperty("categories.include");
  161. return include != null && include.trim().length() > 0;
  162. }
  163. private static boolean categoryIsExcluded(Class<?> c) {
  164. String exclude = System.getProperty("categories.exclude");
  165. if (exclude != null && exclude.trim().length() > 0) {
  166. return hasCategoryFor(c, exclude.toLowerCase().trim());
  167. }
  168. return false;
  169. }
  170. private static boolean hasCategoryFor(Class<?> c, String searchString) {
  171. if (hasCategory(c)) {
  172. return searchString.contains(getCategory(c).toLowerCase());
  173. }
  174. return false;
  175. }
  176. private static boolean hasCategory(Class<?> c) {
  177. return c.getAnnotation(TestCategory.class) != null;
  178. }
  179. private static String getCategory(Class<?> c) {
  180. return c.getAnnotation(TestCategory.class).value();
  181. }
  182. private List<FrameworkMethod> getTestMethods() {
  183. return getTestClass().getAnnotatedMethods(Test.class);
  184. }
  185. /*
  186. * Returns a list of desired browser capabilities according to browsers
  187. * defined in the test class, filtered by possible filter parameters. Use
  188. * {@code @RunLocally} annotation or com.vaadin.testbench.runLocally
  189. * property to override all capabilities.
  190. */
  191. private Collection<DesiredCapabilities> getDesiredCapabilities(
  192. AbstractTB3Test testClassInstance) {
  193. Collection<DesiredCapabilities> desiredCapabilites = getFilteredCapabilities(testClassInstance);
  194. Browser runLocallyBrowser = testClassInstance.getRunLocallyBrowser();
  195. if (runLocallyBrowser != null) {
  196. desiredCapabilites = new ArrayList<DesiredCapabilities>();
  197. desiredCapabilites.add(runLocallyBrowser.getDesiredCapabilities());
  198. }
  199. return desiredCapabilites;
  200. }
  201. /*
  202. * Takes the desired browser capabilities defined in the test class and
  203. * returns a list of browser capabilities filtered browsers.include and
  204. * browsers.exclude system properties. (if present)
  205. */
  206. private Collection<DesiredCapabilities> getFilteredCapabilities(
  207. AbstractTB3Test testClassInstance) {
  208. Collection<DesiredCapabilities> desiredCapabilites = testClassInstance
  209. .getBrowsersToTest();
  210. ArrayList<DesiredCapabilities> filteredCapabilities = new ArrayList<DesiredCapabilities>();
  211. String include = System.getProperty("browsers.include");
  212. String exclude = System.getProperty("browsers.exclude");
  213. for (DesiredCapabilities d : desiredCapabilites) {
  214. String browserName = (d.getBrowserName() + d.getVersion())
  215. .toLowerCase();
  216. if (include != null && include.trim().length() > 0) {
  217. if (include.trim().toLowerCase().contains(browserName)) {
  218. filteredCapabilities.add(d);
  219. }
  220. } else {
  221. filteredCapabilities.add(d);
  222. }
  223. if (exclude != null && exclude.trim().length() > 0) {
  224. if (exclude.trim().toLowerCase().contains(browserName)) {
  225. filteredCapabilities.remove(d);
  226. }
  227. }
  228. }
  229. return filteredCapabilities;
  230. }
  231. private AbstractTB3Test getTestClassInstance()
  232. throws InstantiationException, IllegalAccessException,
  233. InvocationTargetException {
  234. AbstractTB3Test testClassInstance = (AbstractTB3Test) getTestClass()
  235. .getOnlyConstructor().newInstance();
  236. return testClassInstance;
  237. }
  238. // This is a FrameworkMethod class that will always
  239. // return @Ignore and @Test annotations for the wrapped method.
  240. private class IgnoredTestMethod extends FrameworkMethod {
  241. private class IgnoreTestAnnotations {
  242. // We use this method to easily get our hands on
  243. // the Annotation instances for @Ignore and @Test
  244. @Ignore
  245. @Test
  246. public void ignoredTest() {
  247. }
  248. }
  249. public IgnoredTestMethod(Method method) {
  250. super(method);
  251. }
  252. @Override
  253. public Annotation[] getAnnotations() {
  254. return getIgnoredTestMethod().getAnnotations();
  255. }
  256. private Method getIgnoredTestMethod() {
  257. try {
  258. return IgnoreTestAnnotations.class.getMethod("ignoredTest",
  259. null);
  260. } catch (Exception e) {
  261. return null;
  262. }
  263. }
  264. @Override
  265. public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
  266. return getIgnoredTestMethod().getAnnotation(annotationType);
  267. }
  268. }
  269. /**
  270. * Finds the given annotation in the given class or one of its super
  271. * classes. Return the first found annotation
  272. *
  273. * @param searchClass
  274. * @param annotationClass
  275. * @return
  276. */
  277. private <T extends Annotation> T findAnnotation(Class<?> searchClass,
  278. Class<T> annotationClass) {
  279. if (searchClass == Object.class) {
  280. return null;
  281. }
  282. if (searchClass.getAnnotation(annotationClass) != null) {
  283. return searchClass.getAnnotation(annotationClass);
  284. }
  285. return findAnnotation(searchClass.getSuperclass(), annotationClass);
  286. }
  287. /*
  288. * (non-Javadoc)
  289. *
  290. * @see
  291. * org.junit.runners.BlockJUnit4ClassRunner#withBefores(org.junit.runners
  292. * .model.FrameworkMethod, java.lang.Object,
  293. * org.junit.runners.model.Statement)
  294. */
  295. @Override
  296. protected Statement withBefores(final FrameworkMethod method,
  297. final Object target, Statement statement) {
  298. if (!(method instanceof TB3Method)) {
  299. throw new RuntimeException("Unexpected method type "
  300. + method.getClass().getName() + ", expected TB3Method");
  301. }
  302. final TB3Method tb3method = (TB3Method) method;
  303. // setDesiredCapabilities before running the real @Befores (which use
  304. // capabilities)
  305. final Statement realBefores = super.withBefores(method, target,
  306. statement);
  307. return new Statement() {
  308. @Override
  309. public void evaluate() throws Throwable {
  310. ((AbstractTB3Test) target)
  311. .setDesiredCapabilities(tb3method.capabilities);
  312. try {
  313. realBefores.evaluate();
  314. } catch (Throwable t) {
  315. // Give the test a chance to e.g. produce an error
  316. // screenshot before failing the test by re-throwing the
  317. // exception
  318. ((AbstractTB3Test) target).onUncaughtException(t);
  319. throw t;
  320. }
  321. }
  322. };
  323. }
  324. private static class TB3Method extends FrameworkMethod {
  325. private DesiredCapabilities capabilities;
  326. private String testNameSuffix = "";
  327. public TB3Method(Method method, DesiredCapabilities capabilities) {
  328. super(method);
  329. this.capabilities = capabilities;
  330. }
  331. public void setTestNameSuffix(String testNameSuffix) {
  332. this.testNameSuffix = testNameSuffix;
  333. }
  334. @Override
  335. public Object invokeExplosively(final Object target, Object... params)
  336. throws Throwable {
  337. // Executes the test method with the supplied parameters
  338. return super.invokeExplosively(target);
  339. }
  340. @Override
  341. public String getName() {
  342. return String.format("%s[%s]", getMethod().getName()
  343. + testNameSuffix,
  344. BrowserUtil.getUniqueIdentifier(capabilities));
  345. }
  346. }
  347. }