001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.apisupport.project;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.ByteArrayOutputStream;
047: import java.io.File;
048: import java.io.FileInputStream;
049: import java.io.FileOutputStream;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.io.OutputStream;
053: import java.io.OutputStreamWriter;
054: import java.io.Writer;
055: import java.util.Collections;
056: import java.util.HashMap;
057: import java.util.HashSet;
058: import java.util.Iterator;
059: import java.util.Map;
060: import java.util.Properties;
061: import java.util.Set;
062: import java.util.jar.JarEntry;
063: import java.util.jar.JarOutputStream;
064: import java.util.jar.Manifest;
065: import java.util.zip.CRC32;
066: import org.netbeans.api.project.ProjectManager;
067: import org.netbeans.junit.NbTestCase;
068: import org.netbeans.modules.apisupport.project.suite.SuiteProject;
069: import org.netbeans.modules.apisupport.project.suite.SuiteProjectGenerator;
070: import org.openide.filesystems.FileObject;
071: import org.openide.filesystems.FileUtil;
072: import org.netbeans.modules.apisupport.project.universe.NbPlatform;
073: import org.openide.filesystems.FileLock;
074: import org.openide.modules.ModuleInfo;
075: import org.openide.nodes.Node;
076: import org.openide.util.Lookup;
077:
078: /**
079: * Basic setup for all the tests.
080: *
081: * @author Jesse Glick, Martin Krauskopf
082: */
083: public abstract class TestBase extends NbTestCase {
084:
085: public static final String CLUSTER_IDE = "ide9";
086: public static final String CLUSTER_PLATFORM = "platform8";
087: public static final String CLUSTER_ENTERPRISE = "enterprise5";
088: public static final String CLUSTER_APISUPPORT = "apisupport1";
089: public static final String CLUSTER_JAVA = "java2";
090:
091: protected TestBase(String name) {
092: super (name);
093: }
094:
095: private static String EEP = "example-external-projects";
096:
097: /**
098: * Tells whether NB source tree is available (which is not the case with e.g.
099: * within binary distribution).
100: */
101: private boolean sourceAvailable;
102:
103: /** Represents netbeans.org source tree this test is run in if {@link #sourceAvailable}. */
104: private File nbrootF;
105:
106: /** Represents netbeans.org source tree this test is run in if {@link #sourceAvailable}. */
107: private FileObject nbroot;
108:
109: /** Represents destination directory with NetBeans (always available). */
110: protected File destDirF;
111:
112: protected File apisZip;
113:
114: /** sample projects doesn't have datadir
115: */
116: protected static boolean noDataDir = false;
117:
118: protected @Override
119: void setUp() throws Exception {
120: super .setUp();
121: Lookup.getDefault().lookup(ModuleInfo.class);
122: sourceAvailable = isSourceAvailable();
123: if (sourceAvailable) {
124: nbrootF = FileUtil.normalizeFile(getTestNBRoot());
125: nbroot = FileUtil.toFileObject(nbrootF);
126: assertNotNull("have a file object for nbroot when using "
127: + System.getProperty("java.class.path"), nbroot);
128: destDirF = file(nbrootF, "nbbuild/netbeans")
129: .getAbsoluteFile();
130: File extexamplesF = file(getDataDir(), EEP);
131: if (!noDataDir) {
132: assertTrue("there is a dir " + extexamplesF,
133: extexamplesF.isDirectory());
134: assertNotNull("have a file object for extexamples",
135: FileUtil.toFileObject(extexamplesF));
136: }
137: } else {
138: destDirF = getXTestNBDestDir();
139: }
140:
141: assertTrue("Directory really exists: " + destDirF, destDirF
142: .isDirectory());
143:
144: // Need to set up private locations in extexamples, as if they were opened in the IDE.
145: clearWorkDir();
146:
147: ErrorManagerImpl.registerCase(this );
148:
149: // Nonexistent path, just for JavadocForBuiltModuleTest:
150: apisZip = new File(getWorkDir(), "apis.zip");
151: File userPropertiesFile = initializeBuildProperties(
152: getWorkDir(), getDataDir(), apisZip, noDataDir);
153: String[] suites = {
154: // Suite projects:
155: "suite1", "suite2", "suite4",
156: // Standalone module projects:
157: "suite3/dummy-project", };
158: if (!noDataDir) {
159: for (int i = 0; i < suites.length; i++) {
160: File platformPrivate = resolveEEPFile(suites[i]
161: + "/nbproject/private/platform-private.properties");
162: Properties p = new Properties();
163: p.setProperty("user.properties.file",
164: userPropertiesFile.getAbsolutePath());
165: platformPrivate.getParentFile().mkdirs();
166: OutputStream os = new FileOutputStream(platformPrivate);
167: try {
168: p.store(os, null);
169: } finally {
170: os.close();
171: }
172: }
173: }
174: NbPlatform.reset();
175: }
176:
177: protected @Override
178: void tearDown() throws Exception {
179: super .tearDown();
180: ErrorManagerImpl.registerCase(null);
181: }
182:
183: /**
184: * Sets up global build.properties for the default platform.
185: * For {@link PropertyUtils#userBuildProperties()}.
186: * Called automatically by {@link #setUp}.
187: * @param workDir use getWorkDir()
188: * @return resulting properties file
189: */
190: public static File initializeBuildProperties(File workDir,
191: File dataDir) throws Exception {
192: return initializeBuildProperties(workDir, dataDir, null,
193: noDataDir);
194: }
195:
196: private static File initializeBuildProperties(File workDir,
197: File dataDir, File apisZip, boolean noDataDir)
198: throws Exception {
199: File nbrootF = getTestNBRoot();
200: boolean sourceAvailable = isSourceAvailable();
201: System.setProperty("netbeans.user", workDir.getAbsolutePath());
202: File userPropertiesFile = new File(workDir, "build.properties");
203: Properties p = new Properties();
204: File defaultPlatform = sourceAvailable ? file(nbrootF,
205: "nbbuild/netbeans") : getXTestNBDestDir();
206: assertTrue("default platform available (" + defaultPlatform
207: + ')', defaultPlatform.isDirectory());
208: p.setProperty("nbplatform.default.netbeans.dest.dir",
209: defaultPlatform.getAbsolutePath());
210: p.setProperty("nbplatform.default.harness.dir",
211: "${nbplatform.default.netbeans.dest.dir}/harness");
212: if (!noDataDir) {
213: File customPlatform = file(file(dataDir, EEP),
214: "/suite3/nbplatform");
215: assertTrue("custom platform available (" + customPlatform
216: + ')', customPlatform.isDirectory());
217: p.setProperty("nbplatform.custom.netbeans.dest.dir",
218: customPlatform.getAbsolutePath());
219: if (apisZip != null) {
220: p.setProperty("nbplatform.default.javadoc", apisZip
221: .getAbsolutePath());
222: }
223: if (sourceAvailable) {
224: // Make source association work to find misc-project from its binary:
225: p.setProperty("nbplatform.default.sources", nbrootF
226: .getAbsolutePath()
227: + ":"
228: + file(file(dataDir, EEP), "/suite2")
229: .getAbsolutePath());
230: }
231: }
232: OutputStream os = new FileOutputStream(userPropertiesFile);
233: try {
234: p.store(os, null);
235: } finally {
236: os.close();
237: }
238:
239: return userPropertiesFile;
240: }
241:
242: /**
243: * Just calls <code>File(root, path.replace('/', File.separatorChar));</code>
244: */
245: protected static File file(File root, String path) {
246: return new File(root, path.replace('/', File.separatorChar));
247: }
248:
249: private static boolean isSourceAvailable() {
250: return new File(
251: getTestNBRoot(),
252: "nbbuild/netbeans/"
253: + CLUSTER_APISUPPORT
254: + "/modules/org-netbeans-modules-apisupport-project.jar")
255: .isFile();
256: }
257:
258: protected File nbRootFile() {
259: assertTrue("NB source tree is available", sourceAvailable);
260: return nbrootF;
261: }
262:
263: protected FileObject nbRoot() {
264: assertTrue("NB source tree is available", sourceAvailable);
265: return nbroot;
266: }
267:
268: protected File resolveEEPFile(final String relativePath) {
269: File eepF = FileUtil.normalizeFile(new File(getDataDir(), EEP));
270: assertTrue("has EEP directory (" + eepF + ')', eepF
271: .isDirectory());
272: File eepRelF = new File(eepF, relativePath);
273: // assertTrue("resolved file exists (" + eepRelF + ')', eepRelF.exists());
274: return eepRelF;
275: }
276:
277: protected String resolveEEPPath(final String relativePath) {
278: return resolveEEPFile(relativePath).getAbsolutePath();
279: }
280:
281: protected FileObject resolveEEP(final String relativePath) {
282: return FileUtil.toFileObject(resolveEEPFile(relativePath));
283: }
284:
285: /**
286: * Calls in turn {@link #file(File, String)} with {@link #nbrootF} as the
287: * first parameter. So the returned path will be actually relative to the
288: * netbeans.org source tree this test is run in.
289: */
290: protected File file(String path) {
291: return file(nbrootF, path);
292: }
293:
294: /**
295: * Make a temporary copy of a whole folder into some new dir in the scratch area.
296: * Stolen from ant/freeform.
297: */
298: protected File copyFolder(File d) throws IOException {
299: assert d.isDirectory();
300: File workdir = getWorkDir();
301: String name = d.getName();
302: while (name.length() < 3) {
303: name = name + "x";
304: }
305: File todir = File.createTempFile(name, null, workdir);
306: todir.delete();
307: doCopy(d, todir);
308: return todir;
309: }
310:
311: private static void doCopy(File from, File to) throws IOException {
312: if (from.isDirectory()) {
313: to.mkdir();
314: String[] kids = from.list();
315: for (int i = 0; i < kids.length; i++) {
316: doCopy(new File(from, kids[i]), new File(to, kids[i]));
317: }
318: } else {
319: assert from.isFile() : from;
320: InputStream is = new FileInputStream(from);
321: try {
322: OutputStream os = new FileOutputStream(to);
323: try {
324: FileUtil.copy(is, os);
325: } finally {
326: os.close();
327: }
328: } finally {
329: is.close();
330: }
331: }
332: }
333:
334: public static String slurp(FileObject fileObject)
335: throws IOException {
336: InputStream is = fileObject.getInputStream();
337: try {
338: ByteArrayOutputStream baos = new ByteArrayOutputStream();
339: FileUtil.copy(is, baos);
340: return baos.toString("UTF-8");
341: } finally {
342: is.close();
343: }
344: }
345:
346: public static void dump(FileObject f, String contents)
347: throws IOException {
348: FileLock lock = f.lock();
349: try {
350: OutputStream os = f.getOutputStream(lock);
351: try {
352: Writer w = new OutputStreamWriter(os, "UTF-8");
353: w.write(contents);
354: w.flush();
355: } finally {
356: os.close();
357: }
358: } finally {
359: lock.releaseLock();
360: }
361: }
362:
363: public static String slurp(File file) throws IOException {
364: InputStream is = new FileInputStream(file);
365: try {
366: ByteArrayOutputStream baos = new ByteArrayOutputStream();
367: FileUtil.copy(is, baos);
368: return baos.toString("UTF-8");
369: } finally {
370: is.close();
371: }
372: }
373:
374: public static void dump(File f, String contents) throws IOException {
375: f.getParentFile().mkdirs();
376: OutputStream os = new FileOutputStream(f);
377: try {
378: Writer w = new OutputStreamWriter(os, "UTF-8");
379: w.write(contents);
380: w.flush();
381: } finally {
382: os.close();
383: }
384: }
385:
386: // XXX copied from TestBase in ant/freeform
387: public static final class TestPCL implements PropertyChangeListener {
388:
389: public final Set<String> changed = new HashSet<String>();
390: public final Map<String, String> newvals = new HashMap<String, String>();
391: public final Map<String, String> oldvals = new HashMap<String, String>();
392:
393: public TestPCL() {
394: }
395:
396: public void reset() {
397: changed.clear();
398: newvals.clear();
399: oldvals.clear();
400: }
401:
402: public void propertyChange(PropertyChangeEvent evt) {
403: String prop = evt.getPropertyName();
404: String nue = (String) evt.getNewValue();
405: String old = (String) evt.getOldValue();
406: changed.add(prop);
407: if (prop != null) {
408: newvals.put(prop, nue);
409: oldvals.put(prop, old);
410: } else {
411: assert nue == null : "null prop name -> null new value";
412: assert old == null : "null prop name -> null old value";
413: }
414: }
415:
416: }
417:
418: /**
419: * Calls in turn {@link TestBase#generateStandaloneModule(File, String)}
420: * with the {@link #getWorkDir()} as a first parameter.
421: */
422: public NbModuleProject generateStandaloneModule(String prjDir)
423: throws IOException {
424: return generateStandaloneModule(getWorkDir(), prjDir);
425: }
426:
427: /**
428: * Returns {@link NbModuleProject} created in the {@link
429: * #getWorkDir()}/prjDir with code name base default to <em>org.example +
430: * dotted prjDir</em> which is also used as the <em>default</em> package so
431: * the layer and bundle are generated accordingly. Default module's display
432: * name is set to <em>Testing Module</em>. So final set of generated files
433: * for <em>module1</em> as the parameter may look like:
434: *
435: * <ul>
436: * <li>module1/manifest.mf
437: * <li>module1/nbproject/platform.properties
438: * <li>module1/nbproject/project.xml
439: * <li>module1/src/org/example/module1/resources/Bundle.properties
440: * <li>module1/src/org/example/module1/resources/layer.xml
441: * </ul>
442: *
443: * Do not forget to first call {@link #initializeBuildProperties} if you are not a TestBase subclass!
444: */
445: public static NbModuleProject generateStandaloneModule(
446: File workDir, String prjDir) throws IOException {
447: FileObject prjDirFO = generateStandaloneModuleDirectory(
448: workDir, prjDir);
449: return (NbModuleProject) ProjectManager.getDefault()
450: .findProject(prjDirFO);
451: }
452:
453: /**
454: * The same as {@link #generateStandaloneModule(File, String)} but without
455: * <em>opening</em> a generated project.
456: */
457: public static FileObject generateStandaloneModuleDirectory(
458: File workDir, String prjDir) throws IOException {
459: String prjDirDotted = prjDir.replace('/', '.');
460: File prjDirF = file(workDir, prjDir);
461: NbModuleProjectGenerator.createStandAloneModule(
462: prjDirF,
463: "org.example." + prjDirDotted, // cnb
464: "Testing Module", // display name
465: "org/example/" + prjDir
466: + "/resources/Bundle.properties",
467: "org/example/" + prjDir + "/resources/layer.xml",
468: NbPlatform.PLATFORM_ID_DEFAULT); // platform id
469: return FileUtil.toFileObject(prjDirF);
470: }
471:
472: /**
473: * Calls in turn {@link TestBase#generateSuite(File, String)} with the
474: * {@link #getWorkDir()} as a first parameter.
475: */
476: public SuiteProject generateSuite(String prjDir) throws IOException {
477: return generateSuite(getWorkDir(), prjDir);
478: }
479:
480: /** Generates an empty suite which has the default platform set. */
481: public static SuiteProject generateSuite(File workDir, String prjDir)
482: throws IOException {
483: return generateSuite(workDir, prjDir,
484: NbPlatform.PLATFORM_ID_DEFAULT);
485: }
486:
487: /** Generates an empty suite. */
488: public static SuiteProject generateSuite(File workDir,
489: String prjDir, String platformID) throws IOException {
490: File prjDirF = file(workDir, prjDir);
491: SuiteProjectGenerator.createSuiteProject(prjDirF, platformID);
492: return (SuiteProject) ProjectManager.getDefault().findProject(
493: FileUtil.toFileObject(prjDirF));
494: }
495:
496: /**
497: * Generates a suite component module which becomes a part of the given
498: * <code>suiteProject</code>. Module will be generated inside of the
499: * suite's project directory. <p>
500: * See {@link #generateStandaloneModule(File, String)} for details about
501: * what is generated.
502: */
503: public static NbModuleProject generateSuiteComponent(
504: SuiteProject suiteProject, String prjDir) throws Exception {
505: File suiteDir = suiteProject.getProjectDirectoryFile();
506: return generateSuiteComponent(suiteProject, suiteDir, prjDir);
507: }
508:
509: /**
510: * Generates a suite component module which becomes a part of the given
511: * <code>suiteProject</code>.
512: * <p>
513: * See {@link #generateStandaloneModule(File, String)} for details about
514: * what is generated.
515: */
516: public static NbModuleProject generateSuiteComponent(
517: SuiteProject suiteProject, File parentDir, String prjDir)
518: throws Exception {
519: String prjDirDotted = prjDir.replace('/', '.');
520: File suiteDir = suiteProject.getProjectDirectoryFile();
521: File prjDirF = file(parentDir, prjDir);
522: NbModuleProjectGenerator.createSuiteComponentModule(
523: prjDirF,
524: "org.example." + prjDirDotted, // cnb
525: "Testing Module", // display name
526: "org/example/" + prjDir
527: + "/resources/Bundle.properties",
528: "org/example/" + prjDir + "/resources/layer.xml",
529: suiteDir); // suite directory
530: return (NbModuleProject) ProjectManager.getDefault()
531: .findProject(FileUtil.toFileObject(prjDirF));
532: }
533:
534: /**
535: * Create a fresh JAR file.
536: * @param jar the file to create
537: * @param contents keys are JAR entry paths, values are text contents (will be written in UTF-8)
538: * @param manifest a manifest to store (or null for none)
539: */
540: public static void createJar(File jar,
541: Map/*<String,String>*/contents, Manifest manifest)
542: throws IOException {
543: if (manifest != null) {
544: manifest.getMainAttributes().putValue("Manifest-Version",
545: "1.0"); // workaround for JDK bug
546: }
547: jar.getParentFile().mkdirs();
548: OutputStream os = new FileOutputStream(jar);
549: try {
550: JarOutputStream jos = manifest != null ? new JarOutputStream(
551: os, manifest)
552: : new JarOutputStream(os);
553: Iterator it = contents.entrySet().iterator();
554: while (it.hasNext()) {
555: Map.Entry entry = (Map.Entry) it.next();
556: String path = (String) entry.getKey();
557: byte[] data = ((String) entry.getValue())
558: .getBytes("UTF-8");
559: JarEntry je = new JarEntry(path);
560: je.setSize(data.length);
561: CRC32 crc = new CRC32();
562: crc.update(data);
563: je.setCrc(crc.getValue());
564: jos.putNextEntry(je);
565: jos.write(data);
566: }
567: jos.close();
568: } finally {
569: os.close();
570: }
571: }
572:
573: public static void makePlatform(File d) throws IOException {
574: // To satisfy NbPlatform.defaultPlatformLocation and NbPlatform.isValid, and make at least one module:
575: Manifest mani = new Manifest();
576: mani.getMainAttributes().putValue("OpenIDE-Module", "core");
577: TestBase.createJar(new File(new File(new File(d, "platform"),
578: "core"), "core.jar"), Collections.EMPTY_MAP, mani);
579: mani = new Manifest();
580: mani.getMainAttributes().putValue("OpenIDE-Module",
581: "org.netbeans.modules.apisupport.harness");
582: mani.getMainAttributes().putValue(
583: "OpenIDE-Module-Specification-Version", "1.6.1"); // like 5.0
584: TestBase.createJar(new File(new File(new File(d, "harness"),
585: "modules"),
586: "org-netbeans-modules-apisupport-harness.jar"),
587: Collections.EMPTY_MAP, mani);
588: }
589:
590: public static void delete(File f) throws IOException {
591: if (f.isDirectory()) {
592: File[] kids = f.listFiles();
593: for (int i = 0; i < kids.length; i++) {
594: delete(kids[i]);
595: }
596: }
597: if (!f.delete()) {
598: throw new IOException("Could not delete " + f);
599: }
600: }
601:
602: private static File getTestNBRoot() {
603: String nbroot = System.getProperty("test.nbroot");
604: assertNotNull("test.nbroot property has to be set", nbroot);
605: return new File(nbroot);
606: }
607:
608: private static File getXTestNBDestDir() {
609: String destDir = System.getProperty("xtest.netbeans.dest.dir");
610: assertNotNull(
611: "xtest.netbeans.dest.dir property has to be set when running within binary distribution",
612: destDir);
613: return new File(destDir);
614: }
615:
616: private static int getChildrenLength(final Node node) {
617: return node.getChildren().getNodes(true).length;
618: }
619:
620: /** Fails with timeout if children are not updated to the required count. */
621: public static void assertAsynchronouslyUpdatedChildrenNodes(
622: final Node node, final int n) throws InterruptedException {
623: for (int current = getChildrenLength(node); current != n; current = getChildrenLength(node)) {
624: System.out.println("Waiting for " + n
625: + " child(ren); having " + current);
626: Thread.sleep(400);
627: }
628: }
629: }
|