| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.netbeans.junit; |
| |
| import java.awt.HeadlessException; |
| import java.awt.Rectangle; |
| import java.io.*; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.net.URLClassLoader; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarInputStream; |
| import java.util.jar.JarOutputStream; |
| import java.util.jar.Manifest; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import javax.swing.JFrame; |
| import javax.swing.SwingUtilities; |
| import junit.framework.Assert; |
| import junit.framework.AssertionFailedError; |
| import junit.framework.Protectable; |
| import junit.framework.Test; |
| import junit.framework.TestCase; |
| import junit.framework.TestFailure; |
| import junit.framework.TestResult; |
| import org.netbeans.junit.internal.NbModuleLogHandler; |
| |
| /** |
| * Wraps a test class with proper NetBeans Runtime Container environment. |
| * This allows to execute tests in a very similar environment to the |
| * actual invocation in the NetBeans IDE. To use write your test as |
| * you are used to and add suite static method: |
| * <pre> |
| * public class YourTest extends NbTestCase { |
| * public YourTest(String s) { super(s); } |
| * |
| * public static Test suite() { |
| * return NbModuleSuite.create(YourTest.class); |
| * } |
| * |
| * public void testXYZ() { ... } |
| * public void testABC() { ... } |
| * } |
| * </pre> |
| * For more advanced configuration see {@link #emptyConfiguration()} and {@link Configuration}. |
| * |
| * @since 1.46 |
| * @author Jaroslav Tulach <jaroslav.tulach@netbeans.org> |
| */ |
| public class NbModuleSuite { |
| private static final Logger LOG; |
| |
| static { |
| System.setProperty("org.netbeans.MainImpl.154417", "true"); |
| LOG = Logger.getLogger(NbModuleSuite.class.getName()); |
| } |
| |
| private NbModuleSuite() {} |
| |
| |
| /** Settings object that allows one to configure execution of |
| * whole {@link NbModuleSuite}. Chain the method invocations |
| * (each method returns new instance of {@link Configuration}) |
| * and call {@link #suite()} at the end to generate the final |
| * JUnit test class. |
| * |
| * @since 1.48 |
| */ |
| public static final class Configuration extends Object { |
| final List<Item> tests; |
| final Class<? extends TestCase> latestTestCaseClass; |
| final List<String> clusterRegExp; |
| /** each odd is cluster reg exp, each even is module reg exp */ |
| final List<String> moduleRegExp; |
| final List<String> startupArgs; |
| final ClassLoader parentClassLoader; |
| final boolean reuseUserDir; |
| final boolean gui; |
| final boolean enableClasspathModules; |
| final boolean honorAutoEager; |
| final boolean hideExtraModules; |
| final Level failOnMessage; |
| final Level failOnException; |
| |
| private Configuration( |
| List<String> clusterRegExp, |
| List<String> moduleRegExp, |
| List<String> startupArgs, |
| ClassLoader parent, |
| List<Item> testItems, |
| Class<? extends TestCase> latestTestCase, |
| boolean reuseUserDir, |
| boolean gui, |
| boolean enableCPModules, |
| boolean honorAutoEager, |
| Level failOnMessage, |
| Level failOnException, |
| boolean hideExtraModules |
| ) { |
| this.clusterRegExp = clusterRegExp; |
| this.moduleRegExp = moduleRegExp; |
| this.startupArgs = startupArgs; |
| this.parentClassLoader = parent; |
| this.tests = testItems; |
| this.reuseUserDir = reuseUserDir; |
| this.latestTestCaseClass = latestTestCase; |
| this.gui = gui; |
| this.enableClasspathModules = enableCPModules; |
| this.honorAutoEager = honorAutoEager; |
| this.failOnException = failOnException; |
| this.failOnMessage = failOnMessage; |
| this.hideExtraModules = hideExtraModules; |
| } |
| |
| static Configuration create(Class<? extends TestCase> clazz) { |
| return new Configuration( |
| null, null, null, ClassLoader.getSystemClassLoader().getParent(), |
| Collections.<Item>emptyList(), clazz, false, true, true, false |
| , null, null, false); |
| } |
| |
| /** Regular expression to match clusters that shall be enabled. |
| * To enable all cluster, one can use <code>".*"</code>. To enable |
| * ide and java clusters, it is handy to pass in <code>"ide|java"</code>. |
| * There is no need to request presence of <code>platform</code> cluster, |
| * as that is available all the time by default. |
| * <p> |
| * Since version 1.55 this method can be called multiple times. |
| * |
| * @param regExp regular expression to match cluster names |
| * @return clone of this configuration with cluster set to regExp value |
| */ |
| public Configuration clusters(String regExp) { |
| ArrayList<String> list = new ArrayList<String>(); |
| if (clusterRegExp != null) { |
| list.addAll(clusterRegExp); |
| } |
| if (regExp != null) { |
| list.add(regExp); |
| } |
| if (list.isEmpty()) { |
| list = null; |
| } |
| return new Configuration( |
| list, moduleRegExp, startupArgs, parentClassLoader, tests, |
| latestTestCaseClass, reuseUserDir, gui, enableClasspathModules, |
| honorAutoEager |
| , failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** By default only modules on classpath of the test are enabled, |
| * the rest are just autoloads. If you need to enable more, you can |
| * specify that with this method. To enable all available modules |
| * in all clusters pass in <code>".*"</code>. Since 1.55 this method |
| * is cummulative. |
| * |
| * @param regExp regular expression to match code name base of modules |
| * @return clone of this configuration with enable modules set to regExp value |
| */ |
| public Configuration enableModules(String regExp) { |
| if (regExp == null) { |
| return this; |
| } |
| return enableModules(".*", regExp); |
| } |
| |
| /** By default only modules on classpath of the test are enabled, |
| * the rest are just autoloads. If you need to enable more, you can |
| * specify that with this method. To enable all available modules in |
| * one cluster, use this method and pass <code>".*"</code> as list of |
| * modules. This method is cumulative. |
| * |
| * @param clusterRegExp regular expression to match clusters |
| * @param moduleRegExp regular expression to match code name base of modules |
| * @return clone of this configuration with enable modules set to regExp value |
| * @since 1.55 |
| */ |
| public Configuration enableModules(String clusterRegExp, String moduleRegExp) { |
| List<String> arr = new ArrayList<String>(); |
| if (this.moduleRegExp != null) { |
| arr.addAll(this.moduleRegExp); |
| } |
| arr.add(clusterRegExp); |
| arr.add(moduleRegExp); |
| return new Configuration( |
| this.clusterRegExp, arr, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, gui, |
| enableClasspathModules, honorAutoEager, failOnMessage, |
| failOnException, hideExtraModules); |
| } |
| |
| /** |
| * Appends one or more command line arguments which will be used to |
| * start the application. Arguments which take a parameter should |
| * usually be specified as two separate strings. Also note that this |
| * method cannot handle arguments which must be passed directly to the |
| * JVM (such as memory settings or system properties), those should be |
| * instead specified in the <code>test.run.args</code> property (e.g. |
| * in the module's <code>project.properties</code> file). |
| * |
| * @param arguments command line arguments to append; each value |
| * specified here will be passed a separate argument when starting |
| * the application under test. |
| * @return clone of this configuration object with the specified |
| * command line arguments appended to any which may have already |
| * been present |
| * @since 1.67 |
| */ |
| public Configuration addStartupArgument(String... arguments) { |
| if (arguments == null || arguments.length < 1){ |
| throw new IllegalStateException("Must specify at least one startup argument"); |
| } |
| |
| List<String> newArgs = new ArrayList<String>(); |
| if (startupArgs != null) { |
| newArgs.addAll(startupArgs); |
| } |
| newArgs.addAll(Arrays.asList(arguments)); |
| |
| return new Configuration( |
| clusterRegExp, moduleRegExp, newArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, gui, |
| enableClasspathModules, honorAutoEager, failOnMessage, |
| failOnException, hideExtraModules); |
| } |
| |
| Configuration classLoader(ClassLoader parent) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parent, tests, |
| latestTestCaseClass, reuseUserDir, gui, enableClasspathModules, |
| honorAutoEager |
| , failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** Adds new test name, or array of names into the configuration. By |
| * default the suite executes all <code>testXYZ</code> |
| * methods present in the test class |
| * (the one passed into {@link Configuration#create(java.lang.Class)} |
| * method). However if there is a need to execute just some of them, |
| * one can use this method to explicitly enumerate them by subsequent |
| * calls to <code>addTest</code> method. |
| * @param testNames list names to add to the test execution |
| * @return clone of this configuration with testNames test added to the |
| * list of executed tests |
| */ |
| public Configuration addTest(String... testNames) { |
| if (latestTestCaseClass == null){ |
| throw new IllegalStateException(); |
| } |
| List<Item> newTests = new ArrayList<Item>(tests); |
| newTests.add(new Item(true, latestTestCaseClass, testNames)); |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| newTests, latestTestCaseClass, reuseUserDir, gui, |
| enableClasspathModules, honorAutoEager, failOnMessage, |
| failOnException, hideExtraModules); |
| } |
| |
| /** Adds new test class to run, together with a list of its methods |
| * that shall be executed. The list can be empty and if so, the |
| * the suite executes all <code>testXYZ</code> |
| * methods present in the test class. |
| * |
| * @param test the class to also execute in this suite |
| * @param testNames list names to add to the test execution |
| * @return clone of this configuration with testNames test added to the |
| * list of executed tests |
| * @since 1.50 |
| */ |
| public Configuration addTest(Class<? extends TestCase> test, String... testNames) { |
| if (test.equals(latestTestCaseClass)){ |
| return addTest(testNames); |
| } |
| List<Item> newTests = new ArrayList<Item>(tests); |
| addLatest(newTests); |
| if ((testNames != null) && (testNames.length != 0)){ |
| newTests.add(new Item(true, test, testNames)); |
| } |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| newTests, test, reuseUserDir, gui, enableClasspathModules, |
| honorAutoEager |
| , failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** |
| * Add new {@link junit.framework.Test} to run. The implementation must |
| * have no parameter constructor. TastCase can be also passed as an argument |
| * of this method than it's delegated to |
| * {@link Configuration#addTest(java.lang.Class, java.lang.String[]) } |
| * |
| * @param test Test implementation to add |
| * @return clone of this configuration with new Test added to the list |
| * of executed tests |
| * @since 1.50 |
| */ |
| public Configuration addTest(Class<? extends Test> test) { |
| if (TestCase.class.isAssignableFrom(test)){ |
| Class<? extends TestCase> tc = test.asSubclass(TestCase.class); |
| return addTest(tc, new String[0]); |
| } |
| List<Item> newTests = new ArrayList<Item>(tests); |
| newTests.add(new Item(false, test, null)); |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| newTests, latestTestCaseClass, reuseUserDir, |
| gui, enableClasspathModules, honorAutoEager |
| , failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** By default all modules on classpath are enabled (so you can link |
| * with modules that you compile against), this method allows you to |
| * disable this feature, which is useful if the test is known to not |
| * link against any of classpath classes. |
| * |
| * @param enable pass false to ignore modules on classpath |
| * @return new configuration clone |
| * @since 1.56 |
| */ |
| public Configuration enableClasspathModules(boolean enable) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, |
| gui, enable, honorAutoEager, failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** By default the {@link #enableModules(java.lang.String)} method |
| * converts all autoloads into regular modules and enables them. This |
| * is maybe useful in certain situations, but does not really mimic the |
| * real behaviour of the system when it is executed. Those who need |
| * to as closely as possible simulate the real run, can use |
| * <code>honorAutoloadEager(true)</code>. |
| * |
| * @param honor true in case autoloads shall remain autoloads and eager modules eager |
| * @return new configuration filled with this data |
| * @since 1.57 |
| */ |
| public Configuration honorAutoloadEager(boolean honor) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, |
| gui, enableClasspathModules, honor |
| , failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** Allows to limit what modules get enabled in the system. |
| * The original purpose of {@link NbModuleSuite} was to enable |
| * as much of modules as possible. This was believed to |
| * resemble the real situation in the running application the best. |
| * However it turned out there |
| * are situations when too much modules can break the system |
| * and it is necessary to prevent loading some of them. |
| * This method can achieve that. |
| * <p> |
| * The primary usage is for <em>Ant</em> based harness. It usually |
| * contains full installation of various clusters and the application |
| * picks just certain modules from that configuration. |
| * <code>hideExtraModules(true)</code> allows exclusion of these |
| * modules as well. |
| * <p> |
| * The usefulness of this method in <em>Maven</em> based environment |
| * is not that big. Usually the nbm plugin makes only necessary |
| * JARs available. In combination with {@link #enableClasspathModules(boolean) |
| * enableClasspathModules(false)}, it may give you a subset of |
| * the Platform loaded in a test. In a |
| * Maven-based app declaring a dependency on the whole |
| * org.netbeans.cluster:platform use the following suite expression: |
| * <pre> |
| * NbModuleSuite.createConfiguration(ApplicationTest.class). |
| * gui(true). |
| * hideExtraModules(true). |
| * enableModules("(?!org.netbeans.modules.autoupdate|org.netbeans.modules.core.kit|org.netbeans.modules.favorites).*"). |
| * enableClasspathModules(false). |
| * suite(); |
| * </pre> |
| * |
| * @param hide true if all enabled not explicitly requested modules should |
| * be hidden |
| * @return new configuration with holds the provided parameter |
| * @since 1.72 |
| */ |
| public Configuration hideExtraModules(boolean hide) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, |
| gui, enableClasspathModules, honorAutoEager, failOnMessage, |
| failOnException, hide); |
| } |
| |
| /** Fails if there is a message sent to {@link Logger} with appropriate |
| * level or higher during the test run execution. |
| * |
| * @param level the minimal level of the message |
| * @return new configuration filled with this data |
| * @since 1.58 |
| */ |
| public Configuration failOnMessage(Level level) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, |
| gui, enableClasspathModules, honorAutoEager |
| , level, failOnException, hideExtraModules); |
| } |
| |
| /** Fails if there is an exception reported to {@link Logger} with appropriate |
| * level or higher during the test run execution. |
| * |
| * @param level the minimal level of the message |
| * @return new configuration filled with this data |
| * @since 1.58 |
| */ |
| public Configuration failOnException(Level level) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, |
| gui, enableClasspathModules, honorAutoEager |
| , failOnMessage, level, hideExtraModules); |
| } |
| |
| private void addLatest(List<Item> newTests) { |
| if (latestTestCaseClass == null){ |
| return; |
| } |
| for (Item item : newTests) { |
| if (item.clazz.equals(latestTestCaseClass)){ |
| return; |
| } |
| } |
| newTests.add(new Item(true, latestTestCaseClass, null)); |
| } |
| |
| private Configuration getReady() { |
| List<Item> newTests = new ArrayList<Item>(tests); |
| addLatest(newTests); |
| |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| newTests, latestTestCaseClass, reuseUserDir, gui, |
| enableClasspathModules |
| ,honorAutoEager, failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** Should the system run with GUI or without? The default behaviour |
| * does not prevent any module to show UI. If <code>false</code> is |
| * used, then the whole system is instructed with <code>--nogui</code> |
| * option that it shall run as much as possible in invisible mode. As |
| * a result, the main window is not shown after the start, for example. |
| * |
| * @param gui true or false |
| * @return clone of this configuration with gui mode associated |
| */ |
| public Configuration gui(boolean gui) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, |
| tests, latestTestCaseClass, reuseUserDir, gui, |
| enableClasspathModules |
| ,honorAutoEager, failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** |
| * Enables or disables userdir reuse. By default it is disabled. |
| * @param reuse true or false |
| * @return clone of this configuration with userdir reuse mode associated |
| * @since 1.52 |
| */ |
| public Configuration reuseUserDir(boolean reuse) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentClassLoader, tests, |
| latestTestCaseClass, reuse, gui, enableClasspathModules |
| ,honorAutoEager, failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** |
| * Sets the parent ClassLoader on which the NB platform should start. |
| * @param parentCL the parent ClassLoader |
| * @return clone of this configuration with the parent ClassLoader set |
| * @since 1.91 |
| */ |
| public Configuration parentClassLoader(ClassLoader parentCL) { |
| return new Configuration( |
| clusterRegExp, moduleRegExp, startupArgs, parentCL, tests, |
| latestTestCaseClass, reuseUserDir, gui, enableClasspathModules |
| ,honorAutoEager, failOnMessage, failOnException, hideExtraModules); |
| } |
| |
| /** |
| * Creates a test suite from this configuration. |
| * Same as {@link #create(org.netbeans.junit.NbModuleSuite.Configuration)} but more fluid. |
| * @return a suite ready for returning from a {@code public static Test suite()} method |
| * @since org.netbeans.modules.junit/1 1.70 |
| */ |
| public Test suite() { |
| return new S(this); |
| } |
| |
| } |
| |
| /** Factory method to create wrapper test that knows how to setup proper |
| * NetBeans Runtime Container environment. |
| * Wraps the provided class into a test that set ups properly the |
| * testing environment. The set of enabled modules is going to be |
| * determined from the actual classpath of a module, which is common |
| * when in all NetBeans tests. All other modules are kept disabled. |
| * In addition,it allows one limit the clusters that shall be made available. |
| * For example <code>ide|java</code> will start the container just |
| * with platform, ide and java clusters. |
| * |
| * |
| * @param clazz the class with bunch of testXYZ methods |
| * @param clustersRegExp regexp to apply to name of cluster to find out if it is supposed to be included |
| * in the runtime container setup or not |
| * @param moduleRegExp by default all modules on classpath are turned on, |
| * however this regular expression can specify additional ones. If not |
| * null, the specified cluster will be searched for modules with such |
| * codenamebase and those will be turned on |
| * @return runtime container ready test |
| */ |
| public static Test create(Class<? extends TestCase> clazz, String clustersRegExp, String moduleRegExp) { |
| return Configuration.create(clazz).clusters(clustersRegExp).enableModules(moduleRegExp).suite(); |
| } |
| |
| /** Factory method to create wrapper test that knows how to setup proper |
| * NetBeans Runtime Container environment. |
| * Wraps the provided class into a test that set ups properly the |
| * testing environment. The set of enabled modules is going to be |
| * determined from the actual classpath of a module, which is common |
| * when in all NetBeans tests. All other modules are kept disabled. |
| * In addition,it allows one limit the clusters that shall be made available. |
| * For example <code>ide|java</code> will start the container just |
| * with platform, ide and java clusters. |
| * |
| * |
| * @param clazz the class with bunch of testXYZ methods |
| * @param clustersRegExp regexp to apply to name of cluster to find out if it is supposed to be included |
| * in the runtime container setup or not |
| * @param moduleRegExp by default all modules on classpath are turned on, |
| * however this regular expression can specify additional ones. If not |
| * null, the specified cluster will be searched for modules with such |
| * codenamebase and those will be turned on |
| * @param tests names of test methods to execute from the <code>clazz</code>, if |
| * no test methods are specified, all tests in the class are executed |
| * @return runtime container ready test |
| * @since 1.49 |
| */ |
| public static Test create(Class<? extends TestCase> clazz, String clustersRegExp, String moduleRegExp, String... tests) { |
| Configuration conf = Configuration.create(clazz).clusters(clustersRegExp).enableModules(moduleRegExp); |
| if (tests.length > 0) { |
| conf = conf.addTest(tests); |
| } |
| return conf.suite(); |
| } |
| |
| /** Factory method to create wrapper test that knows how to setup proper |
| * NetBeans Runtime Container environment. |
| * Wraps the provided class into a test that set ups properly the |
| * testing environment. All modules, in all clusters, |
| * in the tested applicationwill be included in the test. |
| * |
| * @param clazz the class with bunch of testXYZ methods |
| * @param tests names of test methods to execute from the <code>clazz</code>, if |
| * no test methods are specified, all tests in the class are executed |
| * @return runtime container ready test |
| * @since 1.49 |
| */ |
| public static Test allModules(Class<? extends TestCase> clazz, String... tests) { |
| return create(clazz, ".*", ".*", tests); |
| } |
| |
| /** Creates default configuration wrapping a class that can be executed |
| * with the {@link NbModuleSuite} support. |
| * |
| * @param clazz the class to test, the actual instances will be created |
| * for the class of the same name, but loaded by different classloader |
| * @return config object prefilled with default values; the defaults may |
| * be altered with its addition instance methods |
| * @since 1.48 |
| */ |
| public static Configuration createConfiguration(Class<? extends TestCase> clazz) { |
| return Configuration.create(clazz); |
| } |
| |
| /** Creates empty configuration without any class assiciated. You need |
| * to call {@link Configuration#addTest(java.lang.Class, java.lang.String[])} |
| * then to register proper test classes. |
| * |
| * @return config object prefilled with default values; the defaults may |
| * be altered with its addition instance methods |
| * @since 1.50 |
| */ |
| public static Configuration emptyConfiguration() { |
| return Configuration.create(null); |
| } |
| |
| |
| /** Factory method to create wrapper test that knows how to setup proper |
| * NetBeans Runtime Container environment. This method allows better |
| * customization and control over the executed set of tests. |
| * Wraps the provided class into a test that set ups properly the |
| * testing environment, read more in {@link Configuration}. |
| * |
| * |
| * @param config the configuration for the test |
| * @return runtime container ready test |
| * @since 1.48 |
| * @see Configuration#suite |
| */ |
| public static Test create(Configuration config) { |
| return config.suite(); |
| } |
| |
| private static final class Item { |
| boolean isTestCase; |
| Class<?> clazz; |
| String[] fileNames; |
| |
| public Item(boolean isTestCase, Class<?> clazz, String[] fileNames) { |
| this.isTestCase = isTestCase; |
| this.clazz = clazz; |
| this.fileNames = fileNames; |
| } |
| } |
| |
| static final class S extends NbTestSuite { |
| final Configuration config; |
| private static int invocations; |
| private static File lastUserDir; |
| private int testCount = 0; |
| |
| public S(Configuration config) { |
| this.config = config.getReady(); |
| } |
| |
| @Override |
| public int countTestCases() { |
| return testCount; |
| } |
| |
| @Override |
| public void run(final TestResult result) { |
| result.runProtected(this, new Protectable() { |
| public @Override void protect() throws Throwable { |
| ClassLoader before = Thread.currentThread().getContextClassLoader(); |
| try { |
| runInRuntimeContainer(result); |
| } finally { |
| Thread.currentThread().setContextClassLoader(before); |
| } |
| } |
| }); |
| } |
| |
| private static String[] tokenizePath(String path) { |
| List<String> l = new ArrayList<String>(); |
| StringTokenizer tok = new StringTokenizer(path, ":;", true); // NOI18N |
| char dosHack = '\0'; |
| char lastDelim = '\0'; |
| int delimCount = 0; |
| while (tok.hasMoreTokens()) { |
| String s = tok.nextToken(); |
| if (s.length() == 0) { |
| // Strip empty components. |
| continue; |
| } |
| if (s.length() == 1) { |
| char c = s.charAt(0); |
| if (c == ':' || c == ';') { |
| // Just a delimiter. |
| lastDelim = c; |
| delimCount++; |
| continue; |
| } |
| } |
| if (dosHack != '\0') { |
| // #50679 - "C:/something" is also accepted as DOS path |
| if (lastDelim == ':' && delimCount == 1 && (s.charAt(0) == '\\' || s.charAt(0) == '/')) { |
| // We had a single letter followed by ':' now followed by \something or /something |
| s = "" + dosHack + ':' + s; |
| // and use the new token with the drive prefix... |
| } else { |
| // Something else, leave alone. |
| l.add(Character.toString(dosHack)); |
| // and continue with this token too... |
| } |
| dosHack = '\0'; |
| } |
| // Reset count of # of delimiters in a row. |
| delimCount = 0; |
| if (s.length() == 1) { |
| char c = s.charAt(0); |
| if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { |
| // Probably a DOS drive letter. Leave it with the next component. |
| dosHack = c; |
| continue; |
| } |
| } |
| l.add(s); |
| } |
| if (dosHack != '\0') { |
| //the dosHack was the last letter in the input string (not followed by the ':') |
| //so obviously not a drive letter. |
| //Fix for issue #57304 |
| l.add(Character.toString(dosHack)); |
| } |
| return l.toArray(new String[l.size()]); |
| } |
| |
| static void findClusters(Collection<File> clusters, List<String> regExps) throws IOException { |
| File plat = findPlatform().getCanonicalFile(); |
| String selectiveClusters = System.getProperty("cluster.path.final"); // NOI18N |
| Set<File> path; |
| if (selectiveClusters != null) { |
| path = new TreeSet<File>(); |
| for (String p : tokenizePath(selectiveClusters)) { |
| File f = new File(p); |
| path.add(f.getCanonicalFile()); |
| } |
| } else { |
| File parent; |
| String allClusters = System.getProperty("all.clusters"); // #194794 |
| if (allClusters != null) { |
| parent = new File(allClusters); |
| } else { |
| parent = plat.getParentFile(); |
| } |
| path = new TreeSet<File>(Arrays.asList(parent.listFiles())); |
| } |
| for (String c : regExps) { |
| for (File f : path) { |
| if (f.equals(plat)) { |
| continue; |
| } |
| if (!f.getName().matches(c)) { |
| continue; |
| } |
| File m = new File(new File(f, "config"), "Modules"); |
| if (m.exists()) { |
| clusters.add(f); |
| } |
| } |
| } |
| } |
| |
| private void runInRuntimeContainer(TestResult result) throws Exception { |
| System.getProperties().remove("netbeans.dirs"); |
| File platform = findPlatform(); |
| List<URL> bootCP = new ArrayList<URL>(); |
| List<File> dirs = new ArrayList<File>(); |
| dirs.add(new File(platform, "lib")); |
| |
| File jdkHome = new File(System.getProperty("java.home")); |
| if (new File(jdkHome.getParentFile(), "lib").exists()) { |
| jdkHome = jdkHome.getParentFile(); |
| } |
| dirs.add(new File(jdkHome, "lib")); |
| |
| //in case we're running code coverage, load the coverage libraries |
| if (System.getProperty("code.coverage.classpath") != null) { |
| dirs.add(new File(System.getProperty("code.coverage.classpath"))); |
| } |
| |
| for (File dir: dirs) { |
| File[] jars = dir.listFiles(); |
| if (jars != null) { |
| for (File jar : jars) { |
| if (jar.getName().endsWith(".jar")) { |
| bootCP.add(toURI(jar).toURL()); |
| } |
| } |
| } |
| } |
| |
| // loader that does not see our current classloader |
| JUnitLoader junit = new JUnitLoader(config.parentClassLoader, NbModuleSuite.class.getClassLoader()); |
| URLClassLoader loader = new URLClassLoader(bootCP.toArray(new URL[0]), junit); |
| Class<?> main = loader.loadClass("org.netbeans.Main"); // NOI18N |
| Assert.assertEquals("Loaded by our classloader", loader, main.getClassLoader()); |
| Method m = main.getDeclaredMethod("main", String[].class); // NOI18N |
| |
| System.setProperty("java.util.logging.config", "-"); |
| System.setProperty("netbeans.logger.console", "true"); |
| if (System.getProperty("netbeans.logger.noSystem") == null) { |
| System.setProperty("netbeans.logger.noSystem", "true"); |
| } |
| System.setProperty("netbeans.home", platform.getPath()); |
| System.setProperty("netbeans.full.hack", "true"); |
| |
| String branding = System.getProperty("branding.token"); // NOI18N |
| if (branding != null) { |
| try { |
| Method setBranding = loader.loadClass("org.openide.util.NbBundle").getMethod("setBranding", String.class); // NOI18N |
| setBranding.invoke(null, branding); |
| } catch (Throwable ex) { |
| if (ex instanceof InvocationTargetException) { |
| ex = ((InvocationTargetException)ex).getTargetException(); |
| } |
| LOG.log(Level.WARNING, "Cannot set branding to " + branding, ex); // NOI18N |
| } |
| } |
| |
| File ud = new File(new File(Manager.getWorkDirPath()), "userdir" + invocations++); |
| if (config.reuseUserDir) { |
| ud = lastUserDir != null ? lastUserDir : ud; |
| } else { |
| NbTestCase.deleteSubFiles(ud); |
| } |
| lastUserDir = ud; |
| ud.mkdirs(); |
| |
| System.setProperty("netbeans.user", ud.getPath()); |
| |
| TreeSet<String> modules = new TreeSet<String>(); |
| if (config.enableClasspathModules) { |
| modules.addAll(findEnabledModules(NbTestSuite.class.getClassLoader())); |
| } |
| modules.add("org.openide.filesystems"); |
| modules.add("org.openide.modules"); |
| modules.add("org.openide.util"); |
| modules.add("org.openide.util.ui"); |
| modules.remove("org.netbeans.insane"); |
| modules.add("org.netbeans.core.startup"); |
| modules.add("org.netbeans.core.startup.base"); |
| modules.add("org.netbeans.bootstrap"); |
| turnModules(ud, !config.honorAutoEager, modules, config.moduleRegExp, platform); |
| if (config.enableClasspathModules) { |
| turnClassPathModules(ud, NbTestSuite.class.getClassLoader()); |
| } |
| |
| StringBuilder sb = new StringBuilder(); |
| String sep = ""; |
| for (File f : findClusters()) { |
| turnModules(ud, !config.honorAutoEager, modules, config.moduleRegExp, f); |
| sb.append(sep); |
| sb.append(f.getPath()); |
| sep = File.pathSeparator; |
| } |
| System.setProperty("netbeans.dirs", sb.toString()); |
| |
| if (config.hideExtraModules) { |
| Collection<File> clusters = new LinkedHashSet<File>(); |
| if (config.clusterRegExp != null) { |
| findClusters(clusters, config.clusterRegExp); |
| } |
| clusters.add(findPlatform()); |
| for (File f : clusters) { |
| disableModules(ud, f); |
| } |
| } |
| |
| System.setProperty("netbeans.security.nocheck", "true"); |
| |
| List<Class<?>> allClasses = new ArrayList<Class<?>>(config.tests.size()); |
| for (Item item : config.tests) { |
| allClasses.add(item.clazz); |
| } |
| preparePatches(System.getProperty("java.class.path"), System.getProperties(), allClasses.toArray(new Class<?>[0])); |
| |
| List<String> args = new ArrayList<String>(); |
| args.add("--nosplash"); |
| if (!config.gui) { |
| args.add("--nogui"); |
| } |
| |
| if (config.startupArgs != null) { |
| args.addAll(config.startupArgs); |
| } |
| |
| Test handler = NbModuleLogHandler.registerBuffer(config.failOnMessage, config.failOnException); |
| m.invoke(null, (Object)args.toArray(new String[0])); |
| |
| ClassLoader global = Thread.currentThread().getContextClassLoader(); |
| Assert.assertNotNull("Global classloader is initialized", global); |
| ClassLoader testLoader = global; |
| try { |
| testLoader.loadClass("junit.framework.Test"); |
| testLoader.loadClass("org.netbeans.junit.NbTestSuite"); |
| NbTestSuite toRun = new NbTestSuite(); |
| |
| for (Item item : config.tests) { |
| if (item.isTestCase){ |
| Class<? extends TestCase> sndClazz = |
| testLoader.loadClass(item.clazz.getName()).asSubclass(TestCase.class); |
| if (item.fileNames == null) { |
| MethodOrder.orderMethods(sndClazz, null); |
| toRun.addTest(new NbTestSuiteLogCheck(sndClazz)); |
| } else { |
| NbTestSuite t = new NbTestSuiteLogCheck(); |
| t.addTests(sndClazz, item.fileNames); |
| toRun.addTest(t); |
| } |
| }else{ |
| Class<? extends Test> sndClazz = |
| testLoader.loadClass(item.clazz.getName()).asSubclass(Test.class); |
| toRun.addTest(sndClazz.newInstance()); |
| } |
| } |
| |
| if (handler != null) { |
| toRun.addTest(handler); |
| } |
| |
| testCount = toRun.countTestCases(); |
| toRun.run(result); |
| } catch (ClassNotFoundException ex) { |
| result.addError(this, ex); |
| } catch (NoClassDefFoundError ex) { |
| result.addError(this, ex); |
| } |
| if (handler != null) { |
| NbModuleLogHandler.finish(); |
| } |
| String n; |
| if (config.latestTestCaseClass != null) { |
| n = config.latestTestCaseClass.getName(); |
| } else { |
| n = "exit"; // NOI18N |
| } |
| TestResult shutdownResult = new Shutdown(global, n).run(); |
| if (shutdownResult.failureCount() > 0) { |
| final TestFailure tf = shutdownResult.failures().nextElement(); |
| result.addFailure(tf.failedTest(), (AssertionFailedError)tf.thrownException()); |
| } |
| if (shutdownResult.errorCount() > 0) { |
| final TestFailure tf = shutdownResult.errors().nextElement(); |
| result.addError(tf.failedTest(), tf.thrownException()); |
| } |
| } |
| |
| static File findPlatform() { |
| String clusterPath = System.getProperty("cluster.path.final"); // NOI18N |
| if (clusterPath != null) { |
| for (String piece : tokenizePath(clusterPath)) { |
| File d = new File(piece); |
| if (d.getName().matches("platform\\d*")) { |
| return d; |
| } |
| } |
| } |
| String allClusters = System.getProperty("all.clusters"); // #194794 |
| if (allClusters != null) { |
| File d = new File(allClusters, "platform"); // do not bother with old numbered variants |
| if (d.isDirectory()) { |
| return d; |
| } |
| } |
| try { |
| Class<?> lookup = Class.forName("org.openide.util.Lookup"); // NOI18N |
| File util = toFile(lookup.getProtectionDomain().getCodeSource().getLocation().toURI()); |
| Assert.assertTrue("Util exists: " + util, util.exists()); |
| |
| return util.getParentFile().getParentFile(); |
| } catch (Exception ex) { |
| try { |
| File nbjunit = toFile(NbModuleSuite.class.getProtectionDomain().getCodeSource().getLocation().toURI()); |
| File harness = nbjunit.getParentFile().getParentFile(); |
| Assert.assertEquals(nbjunit + " is in a folder named 'harness'", "harness", harness.getName()); |
| TreeSet<File> sorted = new TreeSet<File>(); |
| for (File p : harness.getParentFile().listFiles()) { |
| if (p.getName().startsWith("platform")) { |
| sorted.add(p); |
| } |
| } |
| Assert.assertFalse("Platform shall be found in " + harness.getParent(), sorted.isEmpty()); |
| return sorted.last(); |
| } catch (Exception ex2) { |
| Assert.fail("Cannot find utilities JAR: " + ex + " and: " + ex2); |
| } |
| return null; |
| } |
| } |
| |
| private File[] findClusters() throws IOException { |
| Collection<File> clusters = new LinkedHashSet<File>(); |
| if (config.clusterRegExp != null) { |
| findClusters(clusters, config.clusterRegExp); |
| } |
| |
| if (config.enableClasspathModules) { |
| // find "cluster" from |
| // k/o.n.m.a.p.N/csam/testModule/build/cluster/modules/org-example-testModule.jar |
| // tested in apisupport.project |
| for (String s : tokenizePath(System.getProperty("java.class.path"))) { |
| File module = new File(s); |
| File cluster = module.getParentFile().getParentFile(); |
| File m = new File(new File(cluster, "config"), "Modules"); |
| if (m.exists() || cluster.getName().equals("cluster")) { |
| clusters.add(cluster); |
| } |
| } |
| } |
| return clusters.toArray(new File[0]); |
| } |
| |
| private static String cnb(Manifest m) { |
| String cn = m.getMainAttributes().getValue("OpenIDE-Module"); |
| return cn != null ? cn.replaceFirst("/\\d+", "") : null; |
| } |
| |
| /** Looks for all modules on classpath of given loader and builds |
| * their list from them. |
| */ |
| static Set<String> findEnabledModules(ClassLoader loader) throws IOException { |
| Set<String> cnbs = new TreeSet<String>(); |
| |
| Enumeration<URL> en = loader.getResources("META-INF/MANIFEST.MF"); |
| while (en.hasMoreElements()) { |
| URL url = en.nextElement(); |
| InputStream is = url.openStream(); |
| try { |
| String cnb = cnb(new Manifest(is)); |
| if (cnb != null) { |
| cnbs.add(cnb); |
| } |
| } finally { |
| is.close(); |
| } |
| } |
| |
| return cnbs; |
| } |
| private static final Set<String> pseudoModules = new HashSet<String>(Arrays.asList( |
| "org.openide.util", |
| "org.openide.util.ui", |
| "org.openide.util.lookup", |
| "org.openide.modules", |
| "org.netbeans.bootstrap", |
| "org.openide.filesystems", |
| "org.openide.filesystems.compat8", |
| "org.netbeans.core.startup", |
| "org.netbeans.core.startup.base", |
| "org.netbeans.libs.asm")); |
| static void turnClassPathModules(File ud, ClassLoader loader) throws IOException { |
| Enumeration<URL> en = loader.getResources("META-INF/MANIFEST.MF"); |
| while (en.hasMoreElements()) { |
| URL url = en.nextElement(); |
| Manifest m; |
| InputStream is = url.openStream(); |
| try { |
| m = new Manifest(is); |
| } catch (IOException x) { |
| throw new IOException("parsing " + url + ": " + x, x); |
| } finally { |
| is.close(); |
| } |
| String cnb = cnb(m); |
| if (cnb != null) { |
| File jar = jarFromURL(url); |
| if (jar == null) { |
| continue; |
| } |
| if (pseudoModules.contains(cnb)) { |
| // Otherwise will get DuplicateException. |
| continue; |
| } |
| String mavenCP = m.getMainAttributes().getValue("Maven-Class-Path"); |
| if (mavenCP != null) { |
| // Do not use ((URLClassLoader) loader).getURLs() as this does not work for Surefire Booter. |
| jar = rewrite(jar, mavenCP.split(" "), System.getProperty("java.class.path")); |
| } |
| String xml = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + |
| "<!DOCTYPE module PUBLIC \"-//NetBeans//DTD Module Status 1.0//EN\"\n" + |
| " \"http://www.netbeans.org/dtds/module-status-1_0.dtd\">\n" + |
| "<module name=\"" + cnb + "\">\n" + |
| " <param name=\"autoload\">false</param>\n" + |
| " <param name=\"eager\">false</param>\n" + |
| " <param name=\"enabled\">true</param>\n" + |
| " <param name=\"jar\">" + jar + "</param>\n" + |
| " <param name=\"reloadable\">false</param>\n" + |
| "</module>\n"; |
| |
| File conf = new File(new File(ud, "config"), "Modules"); |
| conf.mkdirs(); |
| File f = new File(conf, cnb.replace('.', '-') + ".xml"); |
| writeModule(f, xml); |
| } |
| } |
| } |
| private static File rewrite(File jar, String[] mavenCP, String classpath) throws IOException { // #190992 |
| String[] classpathEntries = tokenizePath(classpath); |
| StringBuilder classPathHeader = new StringBuilder(); |
| for (String artifact : mavenCP) { |
| String[] grpArtVers = artifact.split(":"); |
| String partOfPath = File.separatorChar + grpArtVers[0].replace('.', File.separatorChar) + File.separatorChar + grpArtVers[1] + File.separatorChar + grpArtVers[2] + File.separatorChar + grpArtVers[1] + '-' + grpArtVers[2]; |
| File dep = null; |
| for (String classpathEntry : classpathEntries) { |
| if (classpathEntry.endsWith(".jar") && classpathEntry.contains(partOfPath)) { |
| dep = new File(classpathEntry); |
| break; |
| } |
| } |
| if (dep == null) { |
| throw new IOException("no match for " + artifact + " found in " + classpath); |
| } |
| File depCopy = File.createTempFile(artifact.replace(':', '-') + '-', ".jar"); |
| depCopy.deleteOnExit(); |
| NbTestCase.copytree(dep, depCopy); |
| if (classPathHeader.length() > 0) { |
| classPathHeader.append(' '); |
| } |
| classPathHeader.append(depCopy.getName()); |
| } |
| String n = jar.getName(); |
| int dot = n.lastIndexOf('.'); |
| File jarCopy = File.createTempFile(n.substring(0, dot) + '-', n.substring(dot)); |
| jarCopy.deleteOnExit(); |
| InputStream is = new FileInputStream(jar); |
| try { |
| OutputStream os = new FileOutputStream(jarCopy); |
| try { |
| JarInputStream jis = new JarInputStream(is); |
| Manifest mani = new Manifest(jis.getManifest()); |
| mani.getMainAttributes().putValue("Class-Path", classPathHeader.toString()); |
| JarOutputStream jos = new JarOutputStream(os, mani); |
| JarEntry entry; |
| while ((entry = jis.getNextJarEntry()) != null) { |
| if (entry.getName().matches("META-INF/.+[.]SF")) { |
| throw new IOException("cannot handle signed JARs"); |
| } |
| jos.putNextEntry(entry); |
| byte[] buf = new byte[4092]; |
| for (;;) { |
| int more = jis.read(buf, 0, buf.length); |
| if (more == -1) { |
| break; |
| } |
| jos.write(buf, 0, more); |
| } |
| } |
| jis.close(); |
| jos.close(); |
| } finally { |
| os.close(); |
| } |
| } finally { |
| is.close(); |
| } |
| return jarCopy; |
| } |
| |
| private static Pattern MANIFEST = Pattern.compile("jar:(file:.*)!/META-INF/MANIFEST.MF", Pattern.MULTILINE); |
| private static File jarFromURL(URL u) { |
| Matcher m = MANIFEST.matcher(u.toExternalForm()); |
| if (m.matches()) { |
| return toFile(URI.create(m.group(1))); |
| } else { |
| if (!u.getProtocol().equals("file")) { |
| throw new IllegalStateException(u.toExternalForm()); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| /** |
| * JDK 7 |
| */ |
| private static Method fileToPath, pathToUri, pathsGet, pathToFile; |
| |
| static { |
| try { |
| fileToPath = File.class.getMethod("toPath"); |
| } catch (NoSuchMethodException x) { |
| // fine, JDK 6 |
| } |
| if (fileToPath != null) { |
| try { |
| Class<?> path = Class.forName("java.nio.file.Path"); |
| pathToUri = path.getMethod("toUri"); |
| pathsGet = Class.forName("java.nio.file.Paths").getMethod("get", URI.class); |
| pathToFile = path.getMethod("toFile"); |
| } catch (Exception x) { |
| throw new ExceptionInInitializerError(x); |
| } |
| } |
| } |
| private static File toFile(URI u) throws IllegalArgumentException { |
| if (pathsGet != null) { |
| try { |
| return (File) pathToFile.invoke(pathsGet.invoke(null, u)); |
| } catch (Exception x) { |
| LOG.log(Level.FINE, "could not convert " + u + " to File", x); |
| } |
| } |
| String host = u.getHost(); |
| if (host != null && !host.isEmpty() && "file".equals(u.getScheme())) { |
| return new File("\\\\" + host + u.getPath().replace('/', '\\')); |
| } |
| return new File(u); |
| } |
| private static URI toURI(File f) { |
| if (fileToPath != null) { |
| try { |
| URI u = (URI) pathToUri.invoke(fileToPath.invoke(f)); |
| if (u.toString().startsWith("file:///")) { // #214131 workaround |
| u = new URI(/* "file" */u.getScheme(), /* null */u.getUserInfo(), /* null (!) */u.getHost(), /* -1 */u.getPort(), /* "/..." */u.getPath(), /* null */u.getQuery(), /* null */u.getFragment()); |
| } |
| return u; |
| } catch (Exception x) { |
| LOG.log(Level.FINE, "could not convert " + f + " to URI", x); |
| } |
| } |
| String path = f.getAbsolutePath(); |
| if (path.startsWith("\\\\")) { // UNC |
| if (!path.endsWith("\\") && f.isDirectory()) { |
| path += "\\"; |
| } |
| try { |
| return new URI("file", null, path.replace('\\', '/'), null); |
| } catch (URISyntaxException x) { |
| LOG.log(Level.FINE, "could not convert " + f + " to URI", x); |
| } |
| } |
| return f.toURI(); |
| } |
| |
| static void preparePatches(String path, Properties prop, Class<?>... classes) throws URISyntaxException { |
| Pattern tests = Pattern.compile(".*\\" + File.separator + "([^\\" + File.separator + "]+)\\" + File.separator + "tests\\.jar"); |
| StringBuilder sb = new StringBuilder(); |
| String sep = ""; |
| for (String jar : tokenizePath(path)) { |
| Matcher m = tests.matcher(jar); |
| if (m.matches()) { |
| // in case we need it one day, let's add a switch to Configuration |
| // and choose the following line instead of netbeans.systemclassloader.patches |
| // prop.setProperty("netbeans.patches." + m.group(1).replace('-', '.'), jar); |
| sb.append(sep).append(jar); |
| sep = File.pathSeparator; |
| } |
| } |
| Set<URL> uniqueURLs = new HashSet<URL>(); |
| for (Class<?> c : classes) { |
| URL test = c.getProtectionDomain().getCodeSource().getLocation(); |
| Assert.assertNotNull("URL found for " + c, test); |
| if (uniqueURLs.add(test)) { |
| sb.append(sep).append(toFile(test.toURI()).getPath()); |
| sep = File.pathSeparator; |
| } |
| } |
| prop.setProperty("netbeans.systemclassloader.patches", sb.toString()); |
| } |
| |
| private static String asString(InputStream is, boolean close) throws IOException { |
| StringBuilder builder = new StringBuilder(); |
| |
| byte[] bytes = new byte[4096]; |
| try { |
| for (int i; (i = is.read(bytes)) != -1;) { |
| builder.append(new String(bytes, 0, i, "UTF-8")); |
| } |
| } finally { |
| if (close) { |
| is.close(); |
| } |
| } |
| for (;;) { |
| int index = builder.indexOf("\r\n"); |
| if (index == -1) { |
| break; |
| } |
| builder.deleteCharAt(index); |
| } |
| return builder.toString(); |
| } |
| |
| private void disableModules(File ud, File cluster) throws IOException { |
| File confDir = new File(new File(cluster, "config"), "Modules"); |
| for (File c : confDir.listFiles()) { |
| if (!isModuleEnabled(c)) { |
| continue; |
| } |
| File udC = new File(new File(new File(ud, "config"), "Modules"), c.getName()); |
| if (!udC.exists()) { |
| File hidden = new File(udC.getParentFile(), c.getName() + "_hidden"); |
| hidden.createNewFile(); |
| } |
| } |
| } |
| |
| private static boolean isModuleEnabled(File config) throws IOException { |
| String xml = asString(new FileInputStream(config), true); |
| Matcher matcherEnabled = ENABLED.matcher(xml); |
| if (matcherEnabled.find()) { |
| return "true".equals(matcherEnabled.group(1)); |
| } |
| return false; |
| } |
| |
| private static class Shutdown extends NbTestCase { |
| Shutdown(ClassLoader global, String testClass) throws Exception { |
| super("shuttingDown[" + testClass + "]"); |
| this.global = global; |
| } |
| |
| @Override |
| protected int timeOut() { |
| return 180000; // 3 minutes for a shutdown |
| } |
| |
| @Override |
| protected Level logLevel() { |
| return Level.FINE; |
| } |
| |
| @Override |
| protected String logRoot() { |
| return "org.netbeans.core.NbLifecycleManager"; // NOI18N |
| } |
| |
| private static void waitForAWT() throws InvocationTargetException, InterruptedException { |
| final CountDownLatch cdl = new CountDownLatch(1); |
| SwingUtilities.invokeLater(new Runnable() { |
| public @Override void run() { |
| cdl.countDown(); |
| } |
| }); |
| cdl.await(10, TimeUnit.SECONDS); |
| } |
| private final ClassLoader global; |
| |
| @Override |
| protected void runTest() throws Throwable { |
| JFrame shutDown; |
| try { |
| shutDown = new JFrame("Shutting down NetBeans..."); |
| shutDown.setBounds(new Rectangle(-100, -100, 50, 50)); |
| shutDown.setVisible(true); |
| } catch (HeadlessException ex) { |
| shutDown = null; |
| } |
| |
| Class<?> lifeClazz = global.loadClass("org.openide.LifecycleManager"); // NOI18N |
| Method getDefault = lifeClazz.getMethod("getDefault"); // NOI18N |
| Method exit = lifeClazz.getMethod("exit"); |
| LOG.log(Level.FINE, "Closing via LifecycleManager loaded by {0}", lifeClazz.getClassLoader()); |
| Object life = getDefault.invoke(null); |
| if (!life.getClass().getName().startsWith("org.openide.LifecycleManager")) { // NOI18N |
| System.setProperty("netbeans.close.no.exit", "true"); // NOI18N |
| System.setProperty("netbeans.close", "true"); // NOI18N |
| exit.invoke(life); |
| waitForAWT(); |
| System.getProperties().remove("netbeans.close"); // NOI18N |
| System.getProperties().remove("netbeans.close.no.exit"); // NOI18N |
| } |
| |
| if (shutDown != null) { |
| shutDown.setVisible(false); |
| } |
| } |
| } |
| |
| |
| private static final class JUnitLoader extends ClassLoader { |
| private final ClassLoader junit; |
| |
| public JUnitLoader(ClassLoader parent, ClassLoader junit) { |
| super(parent); |
| this.junit = junit; |
| } |
| |
| @Override |
| protected Class<?> findClass(String name) throws ClassNotFoundException { |
| if (isUnit(name)) { |
| return junit.loadClass(name); |
| } |
| return super.findClass(name); |
| } |
| |
| @Override |
| public URL findResource(String name) { |
| if (isUnit(name)) { |
| return junit.getResource(name); |
| } |
| if (name.equals("META-INF/services/java.util.logging.Handler")) { // NOI18N |
| return junit.getResource("org/netbeans/junit/internal/FakeMetaInf.txt"); // NOI18N |
| } |
| return super.findResource(name); |
| } |
| |
| @Override |
| public Enumeration<URL> findResources(String name) throws IOException { |
| if (isUnit(name)) { |
| return junit.getResources(name); |
| } |
| if (name.equals("META-INF/services/java.util.logging.Handler")) { // NOI18N |
| return junit.getResources("org/netbeans/junit/internal/FakeMetaInf.txt"); // NOI18N |
| } |
| return super.findResources(name); |
| } |
| |
| private boolean isUnit(String res) { |
| if (res.startsWith("junit")) { |
| return true; |
| } |
| if (res.startsWith("org.junit") || res.startsWith("org/junit")) { |
| return true; |
| } |
| if (res.startsWith("org.netbeans.junit") || res.startsWith("org/netbeans/junit")) { |
| if (res.startsWith("org.netbeans.junit.ide") || res.startsWith("org/netbeans/junit/ide")) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| private static Pattern ENABLED = Pattern.compile("<param name=[\"']enabled[\"']>([^<]*)</param>", Pattern.MULTILINE); |
| private static Pattern AUTO = Pattern.compile("<param name=[\"']autoload[\"']>([^<]*)</param>", Pattern.MULTILINE); |
| private static Pattern EAGER = Pattern.compile("<param name=[\"']eager[\"']>([^<]*)</param>", Pattern.MULTILINE); |
| |
| private static void turnModules(File ud, boolean autoloads, TreeSet<String> modules, List<String> regExp, File... clusterDirs) throws IOException { |
| if (regExp == null) { |
| return; |
| } |
| File config = new File(new File(ud, "config"), "Modules"); |
| config.mkdirs(); |
| |
| Iterator<String> it = regExp.iterator(); |
| for (;;) { |
| if (!it.hasNext()) { |
| break; |
| } |
| String clusterReg = it.next(); |
| String moduleReg = it.next(); |
| Pattern modPattern = Pattern.compile(moduleReg); |
| for (File c : clusterDirs) { |
| if (!c.getName().matches(clusterReg)) { |
| continue; |
| } |
| |
| File modulesDir = new File(new File(c, "config"), "Modules"); |
| File[] allModules = modulesDir.listFiles(); |
| if (allModules == null) { |
| continue; |
| } |
| for (File m : allModules) { |
| String n = m.getName(); |
| if (n.endsWith(".xml")) { |
| n = n.substring(0, n.length() - 4); |
| } |
| n = n.replace('-', '.'); |
| |
| String xml = asString(new FileInputStream(m), true); |
| |
| boolean contains = modules.contains(n); |
| if (!contains && modPattern != null) { |
| contains = modPattern.matcher(n).matches(); |
| } |
| if (!contains) { |
| continue; |
| } |
| enableModule(xml, autoloads, contains, new File(config, m.getName())); |
| } |
| } |
| } |
| } |
| |
| private static void enableModule(String xml, boolean autoloads, boolean enable, File target) throws IOException { |
| boolean toEnable = false; |
| { |
| Matcher matcherEnabled = ENABLED.matcher(xml); |
| if (matcherEnabled.find()) { |
| toEnable = "false".equals(matcherEnabled.group(1)); |
| } |
| Matcher matcherEager = EAGER.matcher(xml); |
| if (matcherEager.find()) { |
| if ("true".equals(matcherEager.group(1))) { |
| return; |
| } |
| } |
| if (!autoloads) { |
| Matcher matcherAuto = AUTO.matcher(xml); |
| if (matcherAuto.find()) { |
| if ("true".equals(matcherAuto.group(1))) { |
| return; |
| } |
| } |
| } |
| if (toEnable) { |
| assert matcherEnabled.groupCount() == 1 : "Groups: " + matcherEnabled.groupCount() + " for:\n" + xml; |
| try { |
| String out = xml.substring(0, matcherEnabled.start(1)) + (enable ? "true" : "false") + xml.substring(matcherEnabled.end(1)); |
| writeModule(target, out); |
| } catch (IllegalStateException ex) { |
| throw new IOException("Unparsable:\n" + xml, ex); |
| } |
| } |
| } |
| { |
| Matcher matcherEager = AUTO.matcher(xml); |
| if (matcherEager.find()) { |
| int begin = xml.indexOf("<param name=\"autoload"); |
| int end = xml.indexOf("<param name=\"jar"); |
| String middle = "<param name=\"autoload\">false</param>\n" + " <param name=\"eager\">false</param>\n" + " <param name=\"enabled\">true</param>\n" + " "; |
| String out = xml.substring(0, begin) + middle + xml.substring(end); |
| try { |
| writeModule(target, out); |
| } catch (IllegalStateException ex) { |
| throw new IOException("Unparsable:\n" + xml, ex); |
| } |
| } |
| } |
| } |
| |
| private static void writeModule(File file, String xml) throws IOException { |
| String previous; |
| if (file.exists()) { |
| previous = asString(new FileInputStream(file), true); |
| if (previous.equals(xml)) { |
| return; |
| } |
| if (LOG.isLoggable(Level.FINE)) { |
| LOG.log(Level.FINE, "rewrite module file: {0}", file); |
| charDump(Level.FINEST, previous); |
| LOG.finest("new----"); |
| charDump(Level.FINEST, xml); |
| LOG.finest("end----"); |
| } |
| } |
| FileOutputStream os = new FileOutputStream(file); |
| os.write(xml.getBytes("UTF-8")); |
| os.close(); |
| } |
| |
| private static void charDump(Level logLevel, String text) { |
| StringBuilder sb = new StringBuilder(5 * text.length()); |
| for (int i = 0; i < text.length(); i++) { |
| if (i % 8 == 0) { |
| if (i > 0) { |
| sb.append('\n'); |
| } |
| } else { |
| sb.append(' '); |
| } |
| |
| int ch = text.charAt(i); |
| if (' ' <= ch && ch <= 'z') { |
| sb.append('\'').append((char)ch).append('\''); |
| } else { |
| sb.append('x').append(two(Integer.toHexString(ch).toUpperCase())); |
| } |
| } |
| sb.append('\n'); |
| LOG.log(logLevel, sb.toString()); |
| } |
| |
| private static String two(String s) { |
| int len = s.length(); |
| switch (len) { |
| case 0: return "00"; |
| case 1: return "0" + s; |
| case 2: return s; |
| default: return s.substring(len - 2); |
| } |
| } |
| |
| } // end of S |
| |
| private static class NbTestSuiteLogCheck extends NbTestSuite { |
| public NbTestSuiteLogCheck() { |
| } |
| public NbTestSuiteLogCheck(Class<? extends TestCase> clazz) { |
| super(clazz); |
| } |
| |
| @Override |
| public void runTest(Test test, TestResult result) { |
| int e = result.errorCount(); |
| int f = result.failureCount(); |
| LOG.log(Level.FINE, "Running test {0}", test); |
| super.runTest(test, result); |
| LOG.log(Level.FINE, "Finished: {0}", test); |
| if (e == result.errorCount() && f == result.failureCount()) { |
| NbModuleLogHandler.checkFailures((TestCase) test, result, test instanceof NbTestCase ? ((NbTestCase) test).getWorkDirPath() : Manager.getWorkDirPath()); |
| } |
| } |
| } |
| } |