You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TB3TestLocator.java 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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.io.File;
  18. import java.io.IOException;
  19. import java.lang.reflect.Modifier;
  20. import java.net.JarURLConnection;
  21. import java.net.URISyntaxException;
  22. import java.net.URL;
  23. import java.util.ArrayList;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.Comparator;
  27. import java.util.Enumeration;
  28. import java.util.List;
  29. import java.util.jar.JarEntry;
  30. public class TB3TestLocator {
  31. /**
  32. * Traverses the directory on the classpath (inside or outside a Jar file)
  33. * specified by 'basePackage'. Collects all classes inside the location
  34. * which can be assigned to 'baseClass' except for classes inside packages
  35. * listed in 'ignoredPackages'.
  36. *
  37. * @param baseClass
  38. * @param basePackage
  39. * @param ignorePackages
  40. * @return
  41. */
  42. public Class<?>[] findTests(Class<? extends AbstractTB3Test> baseClass,
  43. String basePackage, String[] ignorePackages) {
  44. try {
  45. List<?> l = findClasses(baseClass, basePackage, ignorePackages);
  46. return l.toArray(new Class[] {});
  47. } catch (IOException e) {
  48. // TODO Auto-generated catch block
  49. e.printStackTrace();
  50. }
  51. return null;
  52. }
  53. /**
  54. * Traverses the directory on the classpath (inside or outside a Jar file)
  55. * specified by 'basePackage'. Collects all classes inside the location
  56. * which can be assigned to 'baseClass' except for classes inside packages
  57. * listed in 'ignoredPackages'.
  58. *
  59. * @param baseClass
  60. * @param basePackage
  61. * @param ignoredPackages
  62. * @return
  63. * @throws IOException
  64. */
  65. protected <T> List<Class<? extends T>> findClasses(Class<T> baseClass,
  66. String basePackage, String[] ignoredPackages) throws IOException {
  67. List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>();
  68. String basePackageDirName = "/" + basePackage.replace('.', '/');
  69. URL location = baseClass.getResource(basePackageDirName);
  70. if (location.getProtocol().equals("file")) {
  71. try {
  72. File f = new File(location.toURI());
  73. if (!f.exists()) {
  74. throw new IOException("Directory " + f.toString()
  75. + " does not exist");
  76. }
  77. findPackages(f, basePackage, baseClass, classes,
  78. ignoredPackages);
  79. } catch (URISyntaxException e) {
  80. throw new IOException(e.getMessage());
  81. }
  82. } else if (location.getProtocol().equals("jar")) {
  83. JarURLConnection juc = (JarURLConnection) location.openConnection();
  84. findClassesInJar(juc, basePackage, baseClass, classes);
  85. }
  86. Collections.sort(classes, new Comparator<Class<? extends T>>() {
  87. @Override
  88. public int compare(Class<? extends T> o1, Class<? extends T> o2) {
  89. return o1.getName().compareTo(o2.getName());
  90. }
  91. });
  92. return classes;
  93. }
  94. /**
  95. * Traverses the given directory and collects all classes which are inside
  96. * the given 'javaPackage' and can be assigned to the given 'baseClass'. The
  97. * found classes are added to 'result'.
  98. *
  99. * @param parent
  100. * The directory to traverse
  101. * @param javaPackage
  102. * The java package which 'parent' contains
  103. * @param baseClass
  104. * The class which the target classes extend
  105. * @param result
  106. * The collection to which found classes are added
  107. * @param ignoredPackages
  108. * A collection of packages (including sub packages) to ignore
  109. */
  110. private <T> void findPackages(File parent, String javaPackage,
  111. Class<T> baseClass, Collection<Class<? extends T>> result,
  112. String[] ignoredPackages) {
  113. for (String ignoredPackage : ignoredPackages) {
  114. if (javaPackage.equals(ignoredPackage)) {
  115. return;
  116. }
  117. }
  118. for (File file : parent.listFiles()) {
  119. if (file.isDirectory()) {
  120. findPackages(file, javaPackage + "." + file.getName(),
  121. baseClass, result, ignoredPackages);
  122. } else if (file.getName().endsWith(".class")) {
  123. String fullyQualifiedClassName = javaPackage + "."
  124. + file.getName().replace(".class", "");
  125. addClassIfMatches(result, fullyQualifiedClassName, baseClass);
  126. }
  127. }
  128. }
  129. /**
  130. * Traverses a Jar file using the given connection and collects all classes
  131. * which are inside the given 'javaPackage' and can be assigned to the given
  132. * 'baseClass'. The found classes are added to 'result'.
  133. *
  134. * @param javaPackage
  135. * The java package containing the classes (classes may be in a
  136. * sub package)
  137. * @param baseClass
  138. * The class which the target classes extend
  139. * @param result
  140. * The collection to which found classes are added
  141. * @throws IOException
  142. */
  143. private <T> void findClassesInJar(JarURLConnection juc, String javaPackage,
  144. Class<T> baseClass, Collection<Class<? extends T>> result)
  145. throws IOException {
  146. String javaPackageDir = javaPackage.replace('.', '/');
  147. Enumeration<JarEntry> ent = juc.getJarFile().entries();
  148. while (ent.hasMoreElements()) {
  149. JarEntry e = ent.nextElement();
  150. if (e.getName().endsWith(".class")
  151. && e.getName().startsWith(javaPackageDir)) {
  152. String fullyQualifiedClassName = e.getName().replace('/', '.')
  153. .replace(".class", "");
  154. addClassIfMatches(result, fullyQualifiedClassName, baseClass);
  155. }
  156. }
  157. }
  158. /**
  159. * Verifies that the class represented by 'fullyQualifiedClassName' can be
  160. * loaded, assigned to 'baseClass' and is not an abstract or anonymous
  161. * class.
  162. *
  163. * @param result
  164. * The collection to add to
  165. * @param fullyQualifiedClassName
  166. * The candidate class
  167. * @param baseClass
  168. * The class 'fullyQualifiedClassName' should be assignable to
  169. */
  170. @SuppressWarnings("unchecked")
  171. protected <T> void addClassIfMatches(Collection<Class<? extends T>> result,
  172. String fullyQualifiedClassName, Class<T> baseClass) {
  173. try {
  174. // Try to load the class
  175. Class<?> c = Class.forName(fullyQualifiedClassName);
  176. if (!baseClass.isAssignableFrom(c)) {
  177. return;
  178. }
  179. if (!includeInSuite(c)) {
  180. return;
  181. }
  182. if (!Modifier.isAbstract(c.getModifiers()) && !c.isAnonymousClass()) {
  183. result.add((Class<? extends T>) c);
  184. }
  185. } catch (Exception e) {
  186. // Could ignore that class cannot be loaded
  187. e.printStackTrace();
  188. } catch (LinkageError e) {
  189. // Ignore. Client side classes will at least throw LinkageErrors
  190. }
  191. }
  192. /**
  193. * @return true if the class should be included in the suite, false if not
  194. */
  195. private boolean includeInSuite(Class<?> c) {
  196. if (c.getAnnotation(ExcludeFromSuite.class) != null) {
  197. return false;
  198. }
  199. IncludeIfProperty includeIfProperty = c
  200. .getAnnotation(IncludeIfProperty.class);
  201. if (includeIfProperty != null) {
  202. String includeValue = includeIfProperty.value();
  203. String systemPropertyValue = System.getProperty(includeIfProperty
  204. .property());
  205. if (!includeValue.equals(systemPropertyValue)) {
  206. return false;
  207. }
  208. }
  209. return true;
  210. }
  211. }