All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.junit.NbModuleSuite Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * 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.nio.charset.StandardCharsets;
import java.nio.file.Files;
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.AssertionFailedError;
import junit.framework.Protectable;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestFailure;
import junit.framework.TestResult;
import org.junit.Assert;
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:
 * 
 * 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() { ... }
 * }
 * 
* For more advanced configuration see {@link #emptyConfiguration()} and {@link Configuration}. * * @since 1.46 * @author Jaroslav Tulach <[email protected]> */ 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 tests; final Class latestTestCaseClass; final List clusterRegExp; /** each odd is cluster reg exp, each even is module reg exp */ final List moduleRegExp; final List 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 clusterRegExp, List moduleRegExp, List startupArgs, ClassLoader parent, List testItems, Class 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 clazz) { return new Configuration( null, null, null, ClassLoader.getSystemClassLoader().getParent(), Collections.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 ".*". To enable * ide and java clusters, it is handy to pass in "ide|java". * There is no need to request presence of platform cluster, * as that is available all the time by default. *

* 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 list = new ArrayList(); 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 ".*". 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 ".*" 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 arr = new ArrayList(); 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 test.run.args property (e.g. * in the module's project.properties 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 newArgs = new ArrayList(); 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 testXYZ * methods present in the test class * (the one passed into {@link NbModuleSuite#createConfiguration(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 addTest 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 newTests = new ArrayList(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 testXYZ * 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 test, String... testNames) { if (test.equals(latestTestCaseClass)){ return addTest(testNames); } List newTests = new ArrayList(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 test) { if (TestCase.class.isAssignableFrom(test)){ Class tc = test.asSubclass(TestCase.class); return addTest(tc, new String[0]); } List newTests = new ArrayList(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 * honorAutoloadEager(true). * * @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. *

* The primary usage is for Ant based harness. It usually * contains full installation of various clusters and the application * picks just certain modules from that configuration. * hideExtraModules(true) allows exclusion of these * modules as well. *

* The usefulness of this method in Maven 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: *

         * 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();
         * 
* * @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 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 newTests = new ArrayList(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 false is * used, then the whole system is instructed with --nogui * 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 ide|java 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 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 ide|java 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 clazz, 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 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 clazz, 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 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 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 l = new ArrayList(); 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[0]); } static void findClusters(Collection clusters, List regExps) throws IOException { File plat = findPlatform().getCanonicalFile(); String selectiveClusters = System.getProperty("cluster.path.final"); // NOI18N Set path; if (selectiveClusters != null) { path = new TreeSet(); 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(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 bootCP = new ArrayList(); List dirs = new ArrayList(); 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 modules = new TreeSet(); 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 clusters = new LinkedHashSet(); 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> allClasses = new ArrayList>(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 args = new ArrayList(); 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 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 sndClazz = testLoader.loadClass(item.clazz.getName()).asSubclass(Test.class); toRun.addTest(sndClazz.getDeclaredConstructor().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 sorted = new TreeSet(); 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 clusters = new LinkedHashSet(); 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 findEnabledModules(ClassLoader loader) throws IOException { Set cnbs = new TreeSet(); Enumeration 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 pseudoModules = new HashSet(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 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 = "\n" + "\n" + "\n" + " false\n" + " false\n" + " true\n" + " " + jar + "\n" + " false\n" + "\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 = Files.createTempFile(artifact.replace(':', '-') + '-', ".jar").toFile(); 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 = Files.createTempFile(n.substring(0, dot) + '-', n.substring(dot)).toFile(); 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 uniqueURLs = new HashSet(); 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, StandardCharsets.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 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.hamcrest") || res.startsWith("org/hamcrest")) { 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("([^<]*)", Pattern.MULTILINE); private static Pattern AUTO = Pattern.compile("([^<]*)", Pattern.MULTILINE); private static Pattern EAGER = Pattern.compile("([^<]*)", Pattern.MULTILINE); private static void turnModules(File ud, boolean autoloads, TreeSet modules, List regExp, File... clusterDirs) throws IOException { if (regExp == null) { return; } File config = new File(new File(ud, "config"), "Modules"); config.mkdirs(); Iterator 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("false\n" + " false\n" + " true\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(StandardCharsets.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 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()); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy