Java tutorial
/* * Copyright 2012 the hegemon authors. * * 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.cueup.hegemon.testing.server; import com.cueup.hegemon.LoadPath; import com.cueup.hegemon.testing.HegemonRunner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.junit.runner.notification.RunNotifier; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; /** * Test server for Hegemon. */ public class HegemonTestServer extends AbstractHandler { private final LoadPath loadPath; /** * Creates a test server loading sources with 'loadPath'. */ public HegemonTestServer(LoadPath loadPath) { this.loadPath = loadPath; } private static class AllClassesList { private static final Set<String> TEST_CLASS_NAMES; private static final Map<String, String> TEST_CLASS_SHORT_NAMES; private static final Map<String, String> JS_NAME_TO_CLASS_NAME; private static void addClass(String className) { try { Class<?> c = Class.forName(className, false, AllClassesList.class.getClassLoader()); if (c.isAnnotationPresent(HegemonRunner.TestScript.class)) { TEST_CLASS_NAMES.add(className); TEST_CLASS_SHORT_NAMES.put(c.getSimpleName(), className); JS_NAME_TO_CLASS_NAME.put(c.getAnnotation(HegemonRunner.TestScript.class).filename(), className); } else { System.err.println("No annotation: " + className); } } catch (ClassNotFoundException e) { System.err.println("Could not load: " + className); } } private static void addClassesFromJar(File jarFile) throws IOException { JarInputStream is = new JarInputStream(new FileInputStream(jarFile)); JarEntry entry; while ((entry = is.getNextJarEntry()) != null) { String name = entry.getName(); if (name.endsWith("Test.class")) { addClass(FilenameUtils.removeExtension(name).replaceAll("/", ".")); } } } private static void addClassesFromPath(File root) throws IOException { Collection files = FileUtils.listFiles(root, new String[] { "class" }, true); for (Object file : files) { String name = root.toURI().relativize(((File) file).toURI()).getPath(); if (name.endsWith("Test.class")) { addClass(name.substring(0, name.length() - 6).replaceAll("/", ".")); } } } static { TEST_CLASS_NAMES = Sets.newHashSet(); TEST_CLASS_SHORT_NAMES = Maps.newHashMap(); JS_NAME_TO_CLASS_NAME = Maps.newHashMap(); try { String classPath = System.getProperty("java.class.path"); String separator = System.getProperty("path.separator"); for (String classpathEntry : classPath.split(separator)) { System.err.println("Adding class path: " + classpathEntry); if (classpathEntry.endsWith(".jar")) { addClassesFromJar(new File(classpathEntry)); } else { addClassesFromPath(new File(classpathEntry)); } } ClassLoader loader = AllClassesList.class.getClassLoader(); if (loader instanceof URLClassLoader) { for (URL base : ((URLClassLoader) loader).getURLs()) { if ("file".equals(base.getProtocol())) { System.err.println("Adding class path: " + base); if (base.getPath().endsWith(".jar")) { addClassesFromJar(new File(base.getPath())); } else { addClassesFromPath(new File(base.getPath())); } } else { System.err.println("Ignoring class path: " + base); } } } } catch (Throwable t) { // lint: disable=IllegalCatchCheck t.printStackTrace(); } } } private static Class loadClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException ex) { return null; } } private static Class loadClassByReference(String name) { Class result = loadClass(name); if (result != null) { return result; } List<String> toTry = Lists.newArrayList(); toTry.add(AllClassesList.TEST_CLASS_SHORT_NAMES.get(name)); toTry.add(AllClassesList.JS_NAME_TO_CLASS_NAME.get(name)); for (String option : toTry) { if (option != null) { result = loadClass(option); if (result != null) { return result; } } } return null; } private static void markHandled(Request baseRequest, HttpServletResponse response, int status, String contentType) { response.setContentType(contentType + ";charset=utf-8"); response.setStatus(status); baseRequest.setHandled(true); } /** * Hook for adding custom test output handling. * @return the custom test output handler, or null to use no custom output. */ protected CustomTestOutput createCustomTestOutputHandler() { return null; } /** * Hook for adding custom handling for each test class run. */ protected void startTestClass() { } /** * The request handler translates an HTTP request to a test class and method to run. */ @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if ("/favicon.ico".equals(target)) { response.sendError(HttpServletResponse.SC_NOT_FOUND); baseRequest.setHandled(true); return; } if (target.length() <= 1) { String[] names = AllClassesList.TEST_CLASS_NAMES .toArray(new String[AllClassesList.TEST_CLASS_NAMES.size()]); Arrays.sort(names); markHandled(baseRequest, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "text/html"); for (String name : names) { response.getWriter().println("<p><a href=\"" + name + "\">" + name + "</a></p>"); } return; } try { Class c = loadClassByReference(target.substring(1)); if (c == null) { markHandled(baseRequest, response, HttpServletResponse.SC_NOT_FOUND, "text/plain"); response.getWriter() .println("Could not find class with name or reference to " + target.substring(1)); return; } HegemonRunner runner = new HegemonRunner(c, baseRequest.getQueryString(), this.loadPath); RunNotifier notifier = new RunNotifier(); notifier.addListener(new ResponseListener(response, createCustomTestOutputHandler())); startTestClass(); long start = System.currentTimeMillis(); response.setContentType("text/html;charset=utf-8"); response.getWriter().print( "<style>.ok {color:green} .fail {color:red} .ignore {color:orange} pre {margin-left: 24px}</style>"); runner.run(notifier); response.setStatus(HttpServletResponse.SC_OK); response.getWriter() .println("<br>Finished in " + ((System.currentTimeMillis() - start) / 1000.0) + " seconds."); baseRequest.setHandled(true); } catch (Throwable t) { // lint: disable=IllegalCatchCheck markHandled(baseRequest, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "text/plain"); t.printStackTrace(response.getWriter()); } } /** * Starts an http server on a port that responds to a passed handler. */ public static void run(AbstractHandler handler, int port) throws Exception { Server server = new Server(port); server.setHandler(handler); server.start(); server.join(); } }