/* * Copyright 2000-2016 Vaadin Ltd. * * 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 com.vaadin.tests; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import com.vaadin.server.ExternalResource; import com.vaadin.server.LegacyApplication; import com.vaadin.server.Page; import com.vaadin.server.Page.UriFragmentChangedEvent; import com.vaadin.ui.Component; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.HorizontalSplitPanel; import com.vaadin.ui.Label; import com.vaadin.ui.Layout; import com.vaadin.ui.LegacyWindow; import com.vaadin.ui.Link; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; import com.vaadin.v7.data.Property; import com.vaadin.v7.data.util.HierarchicalContainer; import com.vaadin.v7.ui.Tree; /** * TestBench finds out testable classes within given java packages and adds them * to menu from where they can be executed. Class is considered testable if it * is of class Application or CustomComponent. * * Note: edit TestBench.testablePackages array * * @author Vaadin Ltd. * */ public class TestBench extends com.vaadin.server.LegacyApplication implements Property.ValueChangeListener { // Add here packages which are used for finding testable classes String[] testablePackages = { "com.vaadin.tests", "com.vaadin.tests.tickets" }; HierarchicalContainer testables = new HierarchicalContainer(); LegacyWindow mainWindow = new LegacyWindow("TestBench window"); // Main layout consists of tree menu and body layout HorizontalSplitPanel mainLayout = new HorizontalSplitPanel(); Tree menu; VerticalLayout bodyLayout = new VerticalLayout(); // TODO this could probably be a simple Set HashMap<Class<?>, String> itemCaptions = new HashMap<>(); @Override public void init() { // Add testable classes to hierarchical container for (int p = 0; p < testablePackages.length; p++) { testables.addItem(testablePackages[p]); try { final List<Class<?>> testableClasses = getTestableClassesForPackage( testablePackages[p]); for (final Iterator<Class<?>> it = testableClasses .iterator(); it.hasNext();) { final Class<?> t = it.next(); // ignore TestBench itself if (t.equals(TestBench.class)) { continue; } try { testables.addItem(t); itemCaptions.put(t, t.getName()); testables.setParent(t, testablePackages[p]); testables.setChildrenAllowed(t, false); continue; } catch (final Exception e) { try { testables.addItem(t); itemCaptions.put(t, t.getName()); testables.setParent(t, testablePackages[p]); testables.setChildrenAllowed(t, false); continue; } catch (final Exception e1) { e1.printStackTrace(); } } } } catch (final Exception e) { e.printStackTrace(); } } menu = new Tree("Testables", testables); for (final Iterator<Class<?>> i = itemCaptions.keySet().iterator(); i .hasNext();) { final Class<?> testable = i.next(); // simplify captions final String name = testable.getName() .substring(testable.getName().lastIndexOf('.') + 1); menu.setItemCaption(testable, name); } // expand all root items for (final Iterator<?> i = menu.rootItemIds().iterator(); i .hasNext();) { menu.expandItemsRecursively(i.next()); } menu.addListener(this); menu.setImmediate(true); menu.setNullSelectionAllowed(false); VerticalLayout lo = new VerticalLayout(); lo.addComponent(menu); mainWindow.getPage().addUriFragmentChangedListener( new Page.UriFragmentChangedListener() { @Override public void uriFragmentChanged( UriFragmentChangedEvent source) { String fragment = source.getUriFragment(); if (fragment != null && !"".equals(fragment)) { // try to find a proper test class // exact match Iterator<?> iterator = menu.getItemIds().iterator(); while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof Class) { Class<?> c = (Class<?>) next; String string = c.getName(); if (string.equals(fragment)) { menu.setValue(c); mainLayout.setSplitPosition(0); return; } } } // simple name match iterator = menu.getItemIds().iterator(); while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof Class) { Class<?> c = (Class<?>) next; String string = c.getSimpleName(); if (string.equals(fragment)) { menu.setValue(c); mainLayout.setSplitPosition(0); return; } } } // ticket match iterator = menu.getItemIds().iterator(); while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof Class) { Class<?> c = (Class<?>) next; String string = c.getSimpleName(); if (string .startsWith("Ticket" + fragment)) { menu.setValue(c); mainLayout.setSplitPosition(0); return; } } } // just partly mach lowercase iterator = menu.getItemIds().iterator(); while (iterator.hasNext()) { Object next = iterator.next(); if (next instanceof Class) { Class<?> c = (Class<?>) next; String string = c.getSimpleName(); if (string.toLowerCase() .contains(fragment.toLowerCase())) { menu.setValue(c); mainLayout.setSplitPosition(0); return; } } } getMainWindow().showNotification( "No potential matc for #" + fragment); } } }); mainLayout.addComponent(lo); Panel bodyPanel = new Panel(bodyLayout); bodyPanel.addStyleName("light"); bodyPanel.setSizeFull(); mainLayout.addComponent(bodyPanel); mainLayout.setSplitPosition(30); mainWindow.setContent(mainLayout); setMainWindow(mainWindow); } private Component createTestable(Class<?> c) { try { final LegacyApplication app = (LegacyApplication) c.newInstance(); app.doInit(null); Layout lo = (Layout) app.getMainWindow().getContent(); lo.setParent(null); return lo; } catch (final Exception e) { try { final CustomComponent cc = (CustomComponent) c.newInstance(); cc.setSizeFull(); return cc; } catch (final Exception e1) { e1.printStackTrace(); VerticalLayout lo = new VerticalLayout(); lo.addComponent(new Label( "Cannot create application / custom component: " + e1.toString())); Link l = new Link("Try opening via app runner", new ExternalResource("../run/" + c.getName())); lo.addComponent(l); return lo; } } } // Handle menu selection and update body @Override public void valueChange(Property.ValueChangeEvent event) { bodyLayout.removeAllComponents(); bodyLayout.setCaption(null); final Object o = menu.getValue(); if (o != null && o instanceof Class) { final Class<?> c = (Class<?>) o; final String title = c.getName(); bodyLayout.setCaption(title); bodyLayout.addComponent(createTestable(c)); } else { // NOP node selected or deselected tree item } } /** * Return all testable classes within given package. Class is considered * testable if it's superclass is Application or CustomComponent. * * @param packageName * @return * @throws ClassNotFoundException */ public static List<Class<?>> getTestableClassesForPackage( String packageName) throws Exception { final ArrayList<File> directories = new ArrayList<>(); try { final ClassLoader cld = Thread.currentThread() .getContextClassLoader(); if (cld == null) { throw new ClassNotFoundException("Can't get class loader."); } final String path = packageName.replace('.', '/'); // Ask for all resources for the path final Enumeration<URL> resources = cld.getResources(path); while (resources.hasMoreElements()) { final URL url = resources.nextElement(); directories.add(new File(url.getFile())); } } catch (final Exception x) { throw new Exception( packageName + " does not appear to be a valid package."); } final ArrayList<Class<?>> classes = new ArrayList<>(); // For every directory identified capture all the .class files for (final Iterator<File> it = directories.iterator(); it.hasNext();) { final File directory = it.next(); if (directory.exists()) { // Get the list of the files contained in the package final String[] files = directory.list(); for (int j = 0; j < files.length; j++) { // we are only interested in .class files if (files[j].endsWith(".class")) { // removes the .class extension final String p = packageName + '.' + files[j].substring(0, files[j].length() - 6); final Class<?> c = Class.forName(p); if (c.getSuperclass() != null) { if ((c.getSuperclass().equals( com.vaadin.server.VaadinSession.class))) { classes.add(c); } else if ((c.getSuperclass().equals( com.vaadin.ui.CustomComponent.class))) { classes.add(c); } } // for (int i = 0; i < c.getInterfaces().length; i++) { // Class cc = c.getInterfaces()[i]; // if (c.getInterfaces()[i].equals(Testable.class)) { // // Class is testable // classes.add(c); // } // } } } } else { throw new ClassNotFoundException( packageName + " (" + directory.getPath() + ") does not appear to be a valid package"); } } return classes; } }