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.

TestBench.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*
  2. * Copyright 2000-2016 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;
  17. import java.io.File;
  18. import java.net.URL;
  19. import java.util.ArrayList;
  20. import java.util.Enumeration;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Locale;
  24. import java.util.Set;
  25. import com.vaadin.server.ExternalResource;
  26. import com.vaadin.server.LegacyApplication;
  27. import com.vaadin.ui.Component;
  28. import com.vaadin.ui.CustomComponent;
  29. import com.vaadin.ui.HorizontalSplitPanel;
  30. import com.vaadin.ui.Label;
  31. import com.vaadin.ui.Layout;
  32. import com.vaadin.ui.LegacyWindow;
  33. import com.vaadin.ui.Link;
  34. import com.vaadin.ui.Panel;
  35. import com.vaadin.ui.VerticalLayout;
  36. import com.vaadin.v7.data.Property;
  37. import com.vaadin.v7.data.util.HierarchicalContainer;
  38. import com.vaadin.v7.ui.Tree;
  39. /**
  40. * TestBench finds out testable classes within given java packages and adds them
  41. * to menu from where they can be executed. Class is considered testable if it
  42. * is of class Application or CustomComponent.
  43. *
  44. * Note: edit TestBench.testablePackages array
  45. *
  46. * @author Vaadin Ltd.
  47. *
  48. */
  49. public class TestBench extends com.vaadin.server.LegacyApplication
  50. implements Property.ValueChangeListener {
  51. // Add here packages which are used for finding testable classes
  52. String[] testablePackages = { "com.vaadin.tests",
  53. "com.vaadin.tests.tickets" };
  54. HierarchicalContainer testables = new HierarchicalContainer();
  55. LegacyWindow mainWindow = new LegacyWindow("TestBench window");
  56. // Main layout consists of tree menu and body layout
  57. HorizontalSplitPanel mainLayout = new HorizontalSplitPanel();
  58. Tree menu;
  59. VerticalLayout bodyLayout = new VerticalLayout();
  60. Set<Class<?>> itemCaptions = new HashSet<>();
  61. @Override
  62. public void init() {
  63. // Add testable classes to hierarchical container
  64. for (int p = 0; p < testablePackages.length; p++) {
  65. testables.addItem(testablePackages[p]);
  66. try {
  67. final List<Class<?>> testableClasses = getTestableClassesForPackage(
  68. testablePackages[p]);
  69. for (final Class<?> t : testableClasses) {
  70. // ignore TestBench itself
  71. if (t.equals(TestBench.class)) {
  72. continue;
  73. }
  74. try {
  75. testables.addItem(t);
  76. itemCaptions.add(t);
  77. testables.setParent(t, testablePackages[p]);
  78. testables.setChildrenAllowed(t, false);
  79. continue;
  80. } catch (final Exception e) {
  81. try {
  82. testables.addItem(t);
  83. itemCaptions.add(t);
  84. testables.setParent(t, testablePackages[p]);
  85. testables.setChildrenAllowed(t, false);
  86. continue;
  87. } catch (final Exception e1) {
  88. e1.printStackTrace();
  89. }
  90. }
  91. }
  92. } catch (final Exception e) {
  93. e.printStackTrace();
  94. }
  95. }
  96. menu = new Tree("Testables", testables);
  97. for (final Class<?> testable : itemCaptions) {
  98. // simplify captions
  99. final String name = testable.getName()
  100. .substring(testable.getName().lastIndexOf('.') + 1);
  101. menu.setItemCaption(testable, name);
  102. }
  103. // expand all root items
  104. for (final Object id : menu.rootItemIds()) {
  105. menu.expandItemsRecursively(id);
  106. }
  107. menu.addListener(this);
  108. menu.setImmediate(true);
  109. menu.setNullSelectionAllowed(false);
  110. VerticalLayout lo = new VerticalLayout();
  111. lo.addComponent(menu);
  112. mainWindow.getPage().addUriFragmentChangedListener(
  113. event -> {
  114. String fragment = event.getUriFragment();
  115. if (fragment != null && !fragment.isEmpty()) {
  116. // try to find a proper test class
  117. // exact match
  118. for (Object next : menu.getItemIds()) {
  119. if (next instanceof Class) {
  120. Class<?> c = (Class<?>) next;
  121. String string = c.getName();
  122. if (string.equals(fragment)) {
  123. menu.setValue(c);
  124. mainLayout.setSplitPosition(0);
  125. return;
  126. }
  127. }
  128. }
  129. // simple name match
  130. for (Object next : menu.getItemIds()) {
  131. if (next instanceof Class) {
  132. Class<?> c = (Class<?>) next;
  133. String string = c.getSimpleName();
  134. if (string.equals(fragment)) {
  135. menu.setValue(c);
  136. mainLayout.setSplitPosition(0);
  137. return;
  138. }
  139. }
  140. }
  141. // ticket match
  142. for (Object next : menu.getItemIds()) {
  143. if (next instanceof Class) {
  144. Class<?> c = (Class<?>) next;
  145. String string = c.getSimpleName();
  146. if (string.startsWith("Ticket" + fragment)) {
  147. menu.setValue(c);
  148. mainLayout.setSplitPosition(0);
  149. return;
  150. }
  151. }
  152. }
  153. // just partly match lowercase
  154. for (Object next : menu.getItemIds()) {
  155. if (next instanceof Class) {
  156. Class<?> c = (Class<?>) next;
  157. String string = c.getSimpleName();
  158. if (string.toLowerCase(Locale.ROOT).contains(
  159. fragment.toLowerCase(Locale.ROOT))) {
  160. menu.setValue(c);
  161. mainLayout.setSplitPosition(0);
  162. return;
  163. }
  164. }
  165. }
  166. getMainWindow().showNotification(
  167. "No potential matc for #" + fragment);
  168. }
  169. });
  170. mainLayout.addComponent(lo);
  171. Panel bodyPanel = new Panel(bodyLayout);
  172. bodyPanel.addStyleName("light");
  173. bodyPanel.setSizeFull();
  174. mainLayout.addComponent(bodyPanel);
  175. mainLayout.setSplitPosition(30);
  176. mainWindow.setContent(mainLayout);
  177. setMainWindow(mainWindow);
  178. }
  179. private Component createTestable(Class<?> c) {
  180. try {
  181. final LegacyApplication app = (LegacyApplication) c.newInstance();
  182. app.doInit(null);
  183. Layout lo = (Layout) app.getMainWindow().getContent();
  184. lo.setParent(null);
  185. return lo;
  186. } catch (final Exception e) {
  187. try {
  188. final CustomComponent cc = (CustomComponent) c.newInstance();
  189. cc.setSizeFull();
  190. return cc;
  191. } catch (final Exception e1) {
  192. e1.printStackTrace();
  193. VerticalLayout lo = new VerticalLayout();
  194. lo.addComponent(new Label(
  195. "Cannot create application / custom component: " + e1));
  196. Link l = new Link("Try opening via app runner",
  197. new ExternalResource("../run/" + c.getName()));
  198. lo.addComponent(l);
  199. return lo;
  200. }
  201. }
  202. }
  203. // Handle menu selection and update body
  204. @Override
  205. public void valueChange(Property.ValueChangeEvent event) {
  206. bodyLayout.removeAllComponents();
  207. bodyLayout.setCaption(null);
  208. final Object o = menu.getValue();
  209. if (o != null && o instanceof Class) {
  210. final Class<?> c = (Class<?>) o;
  211. final String title = c.getName();
  212. bodyLayout.setCaption(title);
  213. bodyLayout.addComponent(createTestable(c));
  214. } else {
  215. // NOP node selected or deselected tree item
  216. }
  217. }
  218. /**
  219. * Return all testable classes within given package. Class is considered
  220. * testable if it's superclass is Application or CustomComponent.
  221. *
  222. * @param packageName
  223. * @return
  224. * @throws ClassNotFoundException
  225. */
  226. public static List<Class<?>> getTestableClassesForPackage(
  227. String packageName) throws Exception {
  228. final List<File> directories = new ArrayList<>();
  229. try {
  230. final ClassLoader cld = Thread.currentThread()
  231. .getContextClassLoader();
  232. if (cld == null) {
  233. throw new ClassNotFoundException("Can't get class loader.");
  234. }
  235. final String path = packageName.replace('.', '/');
  236. // Ask for all resources for the path
  237. final Enumeration<URL> resources = cld.getResources(path);
  238. while (resources.hasMoreElements()) {
  239. final URL url = resources.nextElement();
  240. directories.add(new File(url.getFile()));
  241. }
  242. } catch (final Exception x) {
  243. throw new Exception(
  244. packageName + " does not appear to be a valid package.");
  245. }
  246. final List<Class<?>> classes = new ArrayList<>();
  247. // For every directory identified capture all the .class files
  248. for (final File directory : directories) {
  249. if (directory.exists()) {
  250. // Get the list of the files contained in the package
  251. final String[] files = directory.list();
  252. for (int j = 0; j < files.length; j++) {
  253. // we are only interested in .class files
  254. if (files[j].endsWith(".class")) {
  255. // removes the .class extension
  256. final String p = packageName + '.'
  257. + files[j].substring(0, files[j].length() - 6);
  258. final Class<?> c = Class.forName(p);
  259. if (c.getSuperclass() != null) {
  260. if ((c.getSuperclass().equals(
  261. com.vaadin.server.VaadinSession.class))) {
  262. classes.add(c);
  263. } else if ((c.getSuperclass().equals(
  264. com.vaadin.ui.CustomComponent.class))) {
  265. classes.add(c);
  266. }
  267. }
  268. // for (Class cc : c.getInterfaces()) {
  269. // if (cc.equals(Testable.class)) {
  270. // // Class is testable
  271. // classes.add(c);
  272. // }
  273. // }
  274. }
  275. }
  276. } else {
  277. throw new ClassNotFoundException(
  278. packageName + " (" + directory.getPath()
  279. + ") does not appear to be a valid package");
  280. }
  281. }
  282. return classes;
  283. }
  284. }