/** * Copyright (C) 2009 Google Inc. * * 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.googlecode.guice; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; import java.util.Enumeration; import junit.extensions.TestDecorator; import junit.framework.Test; import junit.framework.TestResult; import junit.framework.TestSuite; /** * Builds a test suite whose tests are loaded in a private classloader and run them with a security * manager. * * @author jessewilson@google.com (Jesse Wilson) */ public class StrictContainerTestSuiteBuilder { private final ClassLoader classLoader = new NonSystemClassLoader(); private final SecurityManager securityManager; private final TestSuite testSuite = new TestSuite("StrictContainer"); public StrictContainerTestSuiteBuilder(SecurityManager securityManager) { this.securityManager = securityManager; } public void add(String testClassName) { try { Class<?> testClass = classLoader.loadClass(testClassName); testSuite.addTest(securityManaged(new TestSuite(testClass))); } catch (Exception e) { testSuite.addTest(new SuiteConstructionError(testClassName, e)); } } public void addSuite(String suiteClassname) { try { Class<?> suiteClass = classLoader.loadClass(suiteClassname); Test testSuite = (Test) suiteClass.getMethod("suite").invoke(null); this.testSuite.addTest(securityManaged(testSuite)); } catch (Exception e) { testSuite.addTest(new SuiteConstructionError(suiteClassname, e)); } } public TestSuite build() { return testSuite; } /** * A classloader that reloads everything outside of the JDK. */ static class NonSystemClassLoader extends URLClassLoader { public NonSystemClassLoader() { super(new URL[0]); for (final String element : System.getProperty("java.class.path").split(File.pathSeparator)) { try { // is it a remote/local URL? addURL(new URL(element)); } catch (MalformedURLException e1) { // nope - perhaps it's a filename? try { addURL(new File(element).toURI().toURL()); } catch (MalformedURLException e2) { throw new RuntimeException(e1); } } } } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // check our local cache to avoid duplicates synchronized (this) { Class<?> clazz = findLoadedClass(name); if (clazz != null) { return clazz; } } if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("junit.") || name.startsWith("sun.") || name.startsWith("com.sun.") || name.contains("cglib")) { return super.loadClass(name, resolve); } Class<?> clazz = findClass(name); if (resolve) { resolveClass(clazz); } return clazz; } } /** * Returns a test that sets up and tears down a security manager while it's run. */ public Test securityManaged(Test test) { if (test instanceof TestSuite) { TestSuite suite = (TestSuite) test; TestSuite result = new TestSuite(suite.getName()); @SuppressWarnings("unchecked") // a test suite's elements are tests Enumeration<Test> children = (Enumeration<Test>) suite.tests(); for (Test child : Collections.list(children)) { result.addTest(securityManaged(child)); } return result; } else { return new TestDecorator(test) { public void run(TestResult testResult) { SecurityManager originalSecurityManager = System.getSecurityManager(); System.setSecurityManager(securityManager); try { basicRun(testResult); } finally { testResult.endTest(this); System.setSecurityManager(originalSecurityManager); } } }; } } /** * A simple test that always fails with an exception. */ private static class SuiteConstructionError implements Test { private String className; private final Exception cause; public SuiteConstructionError(String className, Exception cause) { this.className = className; this.cause = cause; } public void run(TestResult testResult) { testResult.addError(this, cause); } public int countTestCases() { return 1; } @Override public String toString() { return className; } } }