Added
Link Here
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* If you wish your version of this file to be governed by only the CDDL |
25 |
* or only the GPL Version 2, indicate your decision by adding |
26 |
* "[Contributor] elects to include this software in this distribution |
27 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
28 |
* single choice of license, a recipient has the option to distribute |
29 |
* your version of this file under either the CDDL, the GPL Version 2 or |
30 |
* to extend the choice of license to its licensees as provided above. |
31 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
32 |
* Version 2 license, then the option applies only if the new code is |
33 |
* made subject to such option by the copyright holder. |
34 |
* |
35 |
* Contributor(s): |
36 |
* |
37 |
* Portions Copyrighted 2008 Sun Microsystems, Inc. |
38 |
*/ |
39 |
|
40 |
package org.netbeans.junit; |
41 |
|
42 |
import java.io.File; |
43 |
import java.io.FileInputStream; |
44 |
import java.io.FileOutputStream; |
45 |
import java.io.IOException; |
46 |
import java.io.InputStream; |
47 |
import java.lang.reflect.Method; |
48 |
import java.net.URL; |
49 |
import java.net.URLClassLoader; |
50 |
import java.util.ArrayList; |
51 |
import java.util.Collection; |
52 |
import java.util.Enumeration; |
53 |
import java.util.LinkedHashSet; |
54 |
import java.util.List; |
55 |
import java.util.Set; |
56 |
import java.util.TreeSet; |
57 |
import java.util.regex.Matcher; |
58 |
import java.util.regex.Pattern; |
59 |
import junit.framework.Assert; |
60 |
import junit.framework.Test; |
61 |
import junit.framework.TestResult; |
62 |
|
63 |
/** |
64 |
* Wraps a test class with proper NetBeans Runtime Container environment. |
65 |
* This allows to execute tests in a very similar environment to the |
66 |
* actual invocation in the NetBeans IDE. To use write your test as |
67 |
* you are used to and add suite static method: |
68 |
* <pre> |
69 |
* public class YourTest extends NbTestCase { |
70 |
* public YourTest(String s) { super(s); } |
71 |
* |
72 |
* public static Test suite() { |
73 |
* return NbModuleSuite.create(YourTest.class); |
74 |
* } |
75 |
* |
76 |
* public void testXYZ() { ... } |
77 |
* public void testABC() { ... } |
78 |
* } |
79 |
* </pre> |
80 |
* |
81 |
* @since 1.46 |
82 |
* @author Jaroslav Tulach <jaroslav.tulach@netbeans.org> |
83 |
*/ |
84 |
public final class NbModuleSuite extends Object { |
85 |
/** Factory method to create wrapper test that knows how to setup proper |
86 |
* NetBeans Runtime Container environment. |
87 |
* Wraps the provided class into a test that set ups properly the |
88 |
* testing environment. The set of enabled modules is going to be |
89 |
* determined from the actual classpath of a module, which is common |
90 |
* when in all NetBeans tests. All other modules are kept disabled. |
91 |
* In addition,it allows one limit the clusters that shall be made available. |
92 |
* For example <code>ide.*|java.*</code> will start the container just |
93 |
* with platform, ide and java clusters. |
94 |
* |
95 |
* |
96 |
* @param clazz the class with bunch of testXYZ methods |
97 |
* @param clustersRegExp regexp to apply to name of cluster to find out if it is supposed to be included |
98 |
* in the runtime container setup or not |
99 |
* @return runtime container ready test |
100 |
*/ |
101 |
public static Test create(Class<? extends Test> clazz, String clustersRegExp) { |
102 |
return new S(clazz, clustersRegExp); |
103 |
} |
104 |
|
105 |
static final class S extends NbTestSuite { |
106 |
private final Class<?> clazz; |
107 |
private final String clusterRegExp; |
108 |
|
109 |
public S(Class<?> aClass) { |
110 |
this(aClass, ""); |
111 |
} |
112 |
|
113 |
public S(Class<?> aClass, String clusterRegExp) { |
114 |
super(); |
115 |
this.clazz = aClass; |
116 |
this.clusterRegExp = clusterRegExp; |
117 |
} |
118 |
|
119 |
@Override |
120 |
public void run(TestResult result) { |
121 |
try { |
122 |
runInRuntimeContainer(result); |
123 |
} catch (Exception ex) { |
124 |
result.addError(this, ex); |
125 |
} |
126 |
} |
127 |
|
128 |
private void runInRuntimeContainer(TestResult result) throws Exception { |
129 |
File platform = findPlatform(); |
130 |
File[] boot = new File(platform, "lib").listFiles(); |
131 |
List<URL> bootCP = new ArrayList<URL>(); |
132 |
for (int i = 0; i < boot.length; i++) { |
133 |
URL u = boot[i].toURL(); |
134 |
if (u.toExternalForm().endsWith(".jar")) { |
135 |
bootCP.add(u); |
136 |
} |
137 |
} |
138 |
|
139 |
File tools = new File(new File(new File(System.getProperty("java.home")).getParentFile(), "lib"), "tools.jar"); |
140 |
Assert.assertTrue(tools.exists()); |
141 |
bootCP.add(tools.toURL()); |
142 |
|
143 |
// loader that does not see our current classloader |
144 |
ClassLoader parent = ClassLoader.getSystemClassLoader().getParent(); |
145 |
URLClassLoader loader = new URLClassLoader(bootCP.toArray(new URL[0]), parent); |
146 |
Class<?> main = loader.loadClass("org.netbeans.Main"); // NOI18N |
147 |
Assert.assertEquals("Loaded by our classloader", loader, main.getClassLoader()); |
148 |
Method m = main.getDeclaredMethod("main", String[].class); // NOI18N |
149 |
|
150 |
System.setProperty("java.util.logging.config", "-"); |
151 |
System.setProperty("netbeans.logger.console", "true"); |
152 |
System.setProperty("netbeans.home", platform.getPath()); |
153 |
|
154 |
File ud = new File(new File(Manager.getWorkDirPath()), "userdir"); |
155 |
ud.mkdirs(); |
156 |
NbTestCase.deleteSubFiles(ud); |
157 |
|
158 |
System.setProperty("netbeans.user", ud.getPath()); |
159 |
|
160 |
TreeSet<String> modules = new TreeSet<String>(); |
161 |
modules.addAll(findEnabledModules(NbTestSuite.class.getClassLoader())); |
162 |
modules.add("org.openide.filesystems"); |
163 |
modules.add("org.openide.modules"); |
164 |
modules.add("org.openide.util"); |
165 |
modules.remove("org.netbeans.insane"); |
166 |
modules.add("org.netbeans.core.startup"); |
167 |
modules.add("org.netbeans.bootstrap"); |
168 |
turnModules(ud, modules, platform); |
169 |
|
170 |
StringBuilder sb = new StringBuilder(); |
171 |
String sep = ""; |
172 |
for (File f : findClusters()) { |
173 |
turnModules(ud, modules, f); |
174 |
sb.append(sep); |
175 |
sb.append(f.getPath()); |
176 |
sep = File.pathSeparator; |
177 |
} |
178 |
System.setProperty("netbeans.dirs", sb.toString()); |
179 |
|
180 |
System.setProperty("netbeans.security.nocheck", "true"); |
181 |
|
182 |
List<String> args = new ArrayList<String>(); |
183 |
args.add("--nosplash"); |
184 |
m.invoke(null, (Object)args.toArray(new String[0])); |
185 |
|
186 |
ClassLoader global = Thread.currentThread().getContextClassLoader(); |
187 |
Assert.assertNotNull("Global classloader is initialized", global); |
188 |
|
189 |
URL[] testCP = preparePath(clazz); |
190 |
JUnitLoader testLoader = new JUnitLoader(testCP, global, NbTestSuite.class.getClassLoader()); |
191 |
Class<?> sndClazz = testLoader.loadClass(clazz.getName()); |
192 |
|
193 |
new NbTestSuite(sndClazz).run(result); |
194 |
} |
195 |
|
196 |
private URL[] preparePath(Class<?>... classes) { |
197 |
Collection<URL> cp = new LinkedHashSet<URL>(); |
198 |
for (Class c : classes) { |
199 |
URL test = c.getProtectionDomain().getCodeSource().getLocation(); |
200 |
Assert.assertNotNull("URL found for " + c, test); |
201 |
cp.add(test); |
202 |
} |
203 |
return cp.toArray(new URL[0]); |
204 |
} |
205 |
|
206 |
|
207 |
private File findPlatform() { |
208 |
try { |
209 |
Class<?> lookup = Class.forName("org.openide.util.Lookup"); // NOI18N |
210 |
File util = new File(lookup.getProtectionDomain().getCodeSource().getLocation().toURI()); |
211 |
Assert.assertTrue("Util exists: " + util, util.exists()); |
212 |
|
213 |
return util.getParentFile().getParentFile(); |
214 |
} catch (Exception ex) { |
215 |
Assert.fail("Cannot find utilities JAR"); |
216 |
return null; |
217 |
} |
218 |
} |
219 |
|
220 |
private File[] findClusters() { |
221 |
List<File> clusters = new ArrayList<File>(); |
222 |
File plat = findPlatform(); |
223 |
|
224 |
for (File f : plat.getParentFile().listFiles()) { |
225 |
if (f.equals(plat)) { |
226 |
continue; |
227 |
} |
228 |
if (!f.getName().matches(clusterRegExp)) { |
229 |
continue; |
230 |
} |
231 |
File m = new File(new File(f, "config"), "Modules"); |
232 |
if (m.exists()) { |
233 |
clusters.add(f); |
234 |
} |
235 |
} |
236 |
return clusters.toArray(new File[0]); |
237 |
} |
238 |
|
239 |
private static Pattern CODENAME = Pattern.compile("OpenIDE-Module: *([^/$ \n\r]*)[/]?[0-9]*", Pattern.MULTILINE); |
240 |
/** Looks for all modules on classpath of given loader and builds |
241 |
* their list from them. |
242 |
*/ |
243 |
static Set<String> findEnabledModules(ClassLoader loader) throws IOException { |
244 |
Set<String> cnbs = new TreeSet<String>(); |
245 |
|
246 |
Enumeration<URL> en = loader.getResources("META-INF/MANIFEST.MF"); |
247 |
while (en.hasMoreElements()) { |
248 |
URL url = en.nextElement(); |
249 |
String manifest = asString(url.openStream(), true); |
250 |
Matcher m = CODENAME.matcher(manifest); |
251 |
if (m.find()) { |
252 |
cnbs.add(m.group(1)); |
253 |
} |
254 |
} |
255 |
|
256 |
return cnbs; |
257 |
} |
258 |
|
259 |
private static String asString(InputStream is, boolean close) throws IOException { |
260 |
byte[] arr = new byte[is.available()]; |
261 |
int len = is.read(arr); |
262 |
if (len != arr.length) { |
263 |
throw new IOException("Not fully read: " + arr.length + " was " + len); |
264 |
} |
265 |
if (close) { |
266 |
is.close(); |
267 |
} |
268 |
return new String(arr, "UTF-8"); // NOI18N |
269 |
} |
270 |
|
271 |
private static final class JUnitLoader extends URLClassLoader { |
272 |
private final ClassLoader junit; |
273 |
|
274 |
public JUnitLoader(URL[] urls, ClassLoader parent, ClassLoader junit) { |
275 |
super(urls, parent); |
276 |
this.junit = junit; |
277 |
} |
278 |
|
279 |
@Override |
280 |
protected Class<?> findClass(String name) throws ClassNotFoundException { |
281 |
if (isUnit(name)) { |
282 |
return junit.loadClass(name); |
283 |
} |
284 |
return super.findClass(name); |
285 |
} |
286 |
|
287 |
@Override |
288 |
public URL findResource(String name) { |
289 |
if (isUnit(name)) { |
290 |
return junit.getResource(name); |
291 |
} |
292 |
return super.findResource(name); |
293 |
} |
294 |
|
295 |
@Override |
296 |
public Enumeration<URL> findResources(String name) throws IOException { |
297 |
if (isUnit(name)) { |
298 |
return junit.getResources(name); |
299 |
} |
300 |
return super.findResources(name); |
301 |
} |
302 |
|
303 |
private final boolean isUnit(String res) { |
304 |
if (res.startsWith("junit")) { |
305 |
return true; |
306 |
} |
307 |
if (res.startsWith("org.junit") || res.startsWith("org/junit")) { |
308 |
return true; |
309 |
} |
310 |
if (res.startsWith("org.netbeans.junit") || res.startsWith("org/netbeans/junit")) { |
311 |
return true; |
312 |
} |
313 |
return false; |
314 |
} |
315 |
} |
316 |
|
317 |
private static Pattern ENABLED = Pattern.compile("<param name=[\"']enabled[\"']>([^<]*)</param>", Pattern.MULTILINE); |
318 |
|
319 |
private static void turnModules(File ud, TreeSet<String> modules, File... clusterDirs) throws IOException { |
320 |
File config = new File(new File(ud, "config"), "Modules"); |
321 |
config.mkdirs(); |
322 |
|
323 |
for (File c : clusterDirs) { |
324 |
File modulesDir = new File(new File(c, "config"), "Modules"); |
325 |
for (File m : modulesDir.listFiles()) { |
326 |
String n = m.getName(); |
327 |
if (n.endsWith(".xml")) { |
328 |
n = n.substring(0, n.length() - 4); |
329 |
} |
330 |
n = n.replace('-', '.'); |
331 |
|
332 |
String xml = asString(new FileInputStream(m), true); |
333 |
Matcher matcherEnabled = ENABLED.matcher(xml); |
334 |
// Matcher matcherEager = EAGER.matcher(xml); |
335 |
|
336 |
boolean found = matcherEnabled.find(); |
337 |
boolean contains = modules.contains(n); |
338 |
boolean enabled = found && "true".equals(matcherEnabled.group(1)); |
339 |
if (contains == enabled) { |
340 |
continue; |
341 |
} |
342 |
|
343 |
if (found) { |
344 |
assert matcherEnabled.groupCount() == 1 : "Groups: " + matcherEnabled.groupCount() + " for:\n" + xml; |
345 |
|
346 |
try { |
347 |
String out = |
348 |
xml.substring(0, matcherEnabled.start(1)) + |
349 |
(contains ? "true" : "false") + |
350 |
xml.substring(matcherEnabled.end(1)); |
351 |
writeModule(new File(config, m.getName()), out); |
352 |
} catch (IllegalStateException ex) { |
353 |
throw (IOException)new IOException("Unparsable:\n" + xml).initCause(ex); |
354 |
} |
355 |
} |
356 |
} |
357 |
} |
358 |
} |
359 |
|
360 |
private static void writeModule(File file, String xml) throws IOException { |
361 |
FileOutputStream os = new FileOutputStream(file); |
362 |
os.write(xml.getBytes("UTF-8")); |
363 |
os.close(); |
364 |
} |
365 |
} // end of S |
366 |
} |