0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.junit;
0043:
0044: import java.awt.EventQueue;
0045: import java.io.File;
0046: import java.io.FileNotFoundException;
0047: import java.io.FileOutputStream;
0048: import java.io.FilterOutputStream;
0049: import java.io.IOException;
0050: import java.io.OutputStream;
0051: import java.io.PrintStream;
0052: import java.lang.ref.Reference;
0053: import java.lang.reflect.Field;
0054: import java.net.URL;
0055: import java.util.ArrayList;
0056: import java.util.Arrays;
0057: import java.util.Collection;
0058: import java.util.Collections;
0059: import java.util.HashMap;
0060: import java.util.HashSet;
0061: import java.util.IdentityHashMap;
0062: import java.util.Iterator;
0063: import java.util.List;
0064: import java.util.Map;
0065: import java.util.Set;
0066: import java.util.logging.Level;
0067: import java.util.prefs.BackingStoreException;
0068: import java.util.prefs.Preferences;
0069: import junit.framework.AssertionFailedError;
0070: import junit.framework.TestCase;
0071: import junit.framework.TestResult;
0072: import org.netbeans.insane.live.LiveReferences;
0073: import org.netbeans.insane.live.Path;
0074: import org.netbeans.insane.scanner.CountingVisitor;
0075: import org.netbeans.insane.scanner.ScannerUtils;
0076: import org.netbeans.junit.diff.Diff;
0077: import org.netbeans.junit.internal.MemoryPreferencesFactory;
0078:
0079: /**
0080: * NetBeans extension to JUnit's {@link TestCase}.
0081: * Adds various abilities such as comparing golden files, getting a working
0082: * directory for test files, testing memory usage, etc.
0083: */
0084: public abstract class NbTestCase extends TestCase implements NbTest {
0085:
0086: /**
0087: * active filter
0088: */
0089: private Filter filter;
0090: /** the amount of time the test was executing for
0091: */
0092: private long time;
0093: /** our working directory */
0094: private String workDirPath;
0095:
0096: /**
0097: * Constructs a test case with the given name.
0098: * @param name name of the testcase
0099: */
0100: public NbTestCase(String name) {
0101: super (name);
0102: }
0103:
0104: /**
0105: * Sets active filter.
0106: * @param filter Filter to be set as active for current test, null will reset filtering.
0107: */
0108: public void setFilter(Filter filter) {
0109: this .filter = filter;
0110: }
0111:
0112: /**
0113: * Returns expected fail message.
0114: * @return expected fail message if it's expected this test fail, null otherwise.
0115: */
0116: public String getExpectedFail() {
0117: if (filter == null)
0118: return null;
0119: return filter.getExpectedFail(this .getName());
0120: }
0121:
0122: /**
0123: * Checks if a test isn't filtered out by the active filter.
0124: * @return true if the test can run
0125: */
0126: public boolean canRun() {
0127: if (null == filter) {
0128: //System.out.println("NBTestCase.canRun(): filter == null name=" + name ());
0129: return true; // no filter was aplied
0130: }
0131: boolean isIncluded = filter.isIncluded(this .getName());
0132: //System.out.println("NbTestCase.canRun(): filter.isIncluded(this.getName())="+isIncluded+" ; this="+this);
0133: return isIncluded;
0134: }
0135:
0136: /**
0137: * Provide ability for tests, setUp and tearDown to request that they run only in the AWT event queue.
0138: * By default, false.
0139: * @return true to run all test methods, setUp and tearDown in the EQ, false to run in whatever thread
0140: */
0141: protected boolean runInEQ() {
0142: return false;
0143: }
0144:
0145: /** Provides support for tests that can have problems with terminating.
0146: * Runs the test in a "watchdog" that measures the time the test shall
0147: * take and if it does not terminate it reports a failure.
0148: *
0149: * @return amount ms to give one test to finish or 0 (default) to disable time outs
0150: * @since 1.20
0151: */
0152: protected int timeOut() {
0153: return 0;
0154: }
0155:
0156: /**
0157: * Allows easy collecting of log messages send thru java.util.logging API.
0158: * Overwrite and return the log level to collect logs to logging file.
0159: * If the method returns non-null level, then the level is assigned to
0160: * the <code>Logger.getLogger("")</code> and the messages reported to it
0161: * are then send into regular log file (which is accessible thru {@link NbTestCase#getLog})
0162: * and in case of failure the last few messages is also included
0163: * in <code>failure.getMessage()</code>.
0164: *
0165: * @return default implementation returns <code>null</code> which disables any logging
0166: * support in test
0167: * @since 1.27
0168: * @see Log#enable
0169: */
0170: protected Level logLevel() {
0171: return null;
0172: }
0173:
0174: /**
0175: * Runs the test case, while conditionally skip some according to result of
0176: * {@link #canRun} method.
0177: */
0178: @Override
0179: public void run(final TestResult result) {
0180: if (canRun()) {
0181: System.setProperty("netbeans.full.hack", "true"); // NOI18N
0182: System.setProperty("java.util.prefs.PreferencesFactory",
0183: MemoryPreferencesFactory.class.getName());//NOI18N
0184: try {
0185: Preferences.userRoot().sync();
0186: } catch (BackingStoreException bex) {
0187: }
0188: Level lev = logLevel();
0189: if (lev != null) {
0190: Log.configure(lev, NbTestCase.this );
0191: }
0192: super .run(result);
0193: }
0194: }
0195:
0196: private static void appendThread(StringBuffer sb, String indent,
0197: Thread t, Map<Thread, StackTraceElement[]> data) {
0198: sb.append(indent).append("Thread ").append(t.getName()).append(
0199: '\n');
0200: indent = indent.concat(" ");
0201: for (StackTraceElement e : data.get(t)) {
0202: sb.append(indent).append(e.getClassName()).append('.')
0203: .append(e.getMethodName()).append(':').append(
0204: e.getLineNumber()).append('\n');
0205: }
0206: }
0207:
0208: private static void appendGroup(StringBuffer sb, String indent,
0209: ThreadGroup tg, Map<Thread, StackTraceElement[]> data) {
0210: sb.append(indent).append("Group ").append(tg.getName()).append(
0211: '\n');
0212: indent = indent.concat(" ");
0213:
0214: int groups = tg.activeGroupCount();
0215: ThreadGroup[] chg = new ThreadGroup[groups];
0216: tg.enumerate(chg, false);
0217: for (ThreadGroup inner : chg) {
0218: if (inner != null)
0219: appendGroup(sb, indent, inner, data);
0220: }
0221:
0222: int threads = tg.activeCount();
0223: Thread[] cht = new Thread[threads];
0224: tg.enumerate(cht, false);
0225: for (Thread t : cht) {
0226: if (t != null)
0227: appendThread(sb, indent, t, data);
0228: }
0229: }
0230:
0231: private static String threadDump() {
0232: Map<Thread, StackTraceElement[]> all = Thread
0233: .getAllStackTraces();
0234: ThreadGroup root = Thread.currentThread().getThreadGroup();
0235: while (root.getParent() != null)
0236: root = root.getParent();
0237:
0238: StringBuffer sb = new StringBuffer();
0239: appendGroup(sb, "", root, all);
0240: return sb.toString();
0241: }
0242:
0243: /**
0244: * Runs the bare test sequence. It checks {@link #runInEQ} and possibly
0245: * schedules the call of <code>setUp</code>, <code>runTest</code> and <code>tearDown</code>
0246: * to AWT event thread. It also consults {@link #timeOut} and if so, it starts a
0247: * count down and aborts the <code>runTest</code> if the time out expires.
0248: * @exception Throwable if any exception is thrown
0249: */
0250: @Override
0251: public void runBare() throws Throwable {
0252: abstract class Guard implements Runnable {
0253: private boolean finished;
0254: private Throwable t;
0255:
0256: public abstract void doSomething() throws Throwable;
0257:
0258: public void run() {
0259: try {
0260: doSomething();
0261: } catch (Throwable thrwn) {
0262: this .t = Log.wrapWithMessages(thrwn);
0263: } finally {
0264: synchronized (this ) {
0265: finished = true;
0266: notifyAll();
0267: }
0268: }
0269: }
0270:
0271: public synchronized void waitFinished() throws Throwable {
0272: waitFinished(0);
0273: }
0274:
0275: public synchronized void waitFinished(int time)
0276: throws Throwable {
0277: if (!finished) {
0278: try {
0279: wait(time);
0280: } catch (InterruptedException ex) {
0281: if (t == null) {
0282: t = ex;
0283: }
0284: }
0285: }
0286: if (t != null) {
0287: throw t;
0288: }
0289:
0290: if (!finished) {
0291: throw new AssertionFailedError("The test "
0292: + getName() + " did not finish in " + time
0293: + "ms\n" + threadDump());
0294: }
0295: }
0296: }
0297: /* original sequence from TestCase.runBare():
0298: setUp();
0299: try {
0300: runTest();
0301: } finally {
0302: tearDown();
0303: }
0304: */
0305: // setUp
0306: if (runInEQ()) {
0307: Guard setUp = new Guard() {
0308: public void doSomething() throws Throwable {
0309: setUp();
0310: }
0311: };
0312: EventQueue.invokeLater(setUp);
0313: // need to have timeout because previous test case can block AWT thread
0314: setUp.waitFinished(timeOut());
0315: } else {
0316: setUp();
0317: }
0318: try {
0319: // runTest
0320: Guard runTest = new Guard() {
0321: public void doSomething() throws Throwable {
0322: long now = System.nanoTime();
0323: try {
0324: runTest();
0325: } finally {
0326: long last = System.nanoTime() - now;
0327: if (last < 1) {
0328: last = 1;
0329: }
0330: NbTestCase.this .time = last;
0331: }
0332: }
0333: };
0334: if (runInEQ()) {
0335: EventQueue.invokeLater(runTest);
0336: runTest.waitFinished(timeOut());
0337: } else {
0338: if (timeOut() == 0) {
0339: // Regular test.
0340: runTest.run();
0341: runTest.waitFinished();
0342: } else {
0343: // Regular test with time out
0344: Thread watchDog = new Thread(runTest,
0345: "Test Watch Dog: " + getName());
0346: watchDog.start();
0347: runTest.waitFinished(timeOut());
0348: }
0349: }
0350: } finally {
0351: // tearDown
0352: if (runInEQ()) {
0353: Guard tearDown = new Guard() {
0354: public void doSomething() throws Throwable {
0355: tearDown();
0356: }
0357: };
0358: EventQueue.invokeLater(tearDown);
0359: // need to have timeout because test can block AWT thread
0360: tearDown.waitFinished(timeOut());
0361: } else {
0362: tearDown();
0363: }
0364: }
0365: }
0366:
0367: /** Parses the test name to find out whether it encodes a number. The
0368: * testSomeName1343 represents nubmer 1343.
0369: * @return the number
0370: * @exception may throw AssertionFailedError if the number is not found in the test name
0371: */
0372: protected final int getTestNumber() {
0373: try {
0374: java.util.regex.Matcher m = java.util.regex.Pattern
0375: .compile("test[a-zA-Z]*([0-9]+)")
0376: .matcher(getName());
0377: assertTrue("Name does not contain numbers: " + getName(), m
0378: .find());
0379: return Integer.valueOf(m.group(1)).intValue();
0380: } catch (Exception ex) {
0381: ex.printStackTrace();
0382: fail("Name: " + getName() + " does not represent number");
0383: return 0;
0384: }
0385: }
0386:
0387: /** in nanoseconds */
0388: final long getExecutionTime() {
0389: return time;
0390: }
0391:
0392: // additional asserts !!!!
0393:
0394: /**
0395: * Asserts that two files are the same (their content is identical), when files
0396: * differ {@link org.netbeans.junit.AssertionFileFailedError AssertionFileFailedError} exception is thrown.
0397: * Depending on the Diff implementation additional output can be generated to the file/dir specified by the
0398: * <b>diff</b> param.
0399: * @param message the detail message for this assertion
0400: * @param test first file to be compared, by the convention this should be the test-generated file
0401: * @param pass second file to be comapred, it should be so called 'golden' file, which defines
0402: * the correct content for the test-generated file.
0403: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0404: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0405: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0406: * by the '.diff'.
0407: * @param externalDiff instance of class implementing the {@link org.netbeans.junit.diff.Diff} interface, it has to be
0408: * already initialized, when passed in this assertFile function.
0409: */
0410: static public void assertFile(String message, String test,
0411: String pass, String diff, Diff externalDiff) {
0412: Diff diffImpl = null == externalDiff ? Manager.getSystemDiff()
0413: : externalDiff;
0414: File diffFile = getDiffName(pass, null == diff ? null
0415: : new File(diff));
0416:
0417: if (null == diffImpl) {
0418: fail("diff is not available");
0419: } else {
0420: try {
0421: if (null == diffFile) {
0422: if (diffImpl.diff(test, pass, null))
0423: throw new AssertionFileFailedError(message, "");
0424: } else {
0425: if (diffImpl.diff(test, pass, diffFile
0426: .getAbsolutePath()))
0427: throw new AssertionFileFailedError(message,
0428: diffFile.getAbsolutePath());
0429: }
0430: } catch (IOException e) {
0431: fail("exception in assertFile : " + e.getMessage());
0432: }
0433: }
0434: }
0435:
0436: /**
0437: * Asserts that two files are the same, it uses specific {@link org.netbeans.junit.diff.Diff Diff} implementation to
0438: * compare two files and stores possible differencies in the output file.
0439: * @param test first file to be compared, by the convention this should be the test-generated file
0440: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0441: * correct content for the test-generated file.
0442: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0443: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0444: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0445: * by the '.diff'.
0446: * @param externalDiff instance of class implementing the {@link org.netbeans.junit.diff.Diff} interface, it has to be
0447: * already initialized, when passed in this assertFile function.
0448: */
0449: static public void assertFile(String test, String pass,
0450: String diff, Diff externalDiff) {
0451: assertFile(null, test, pass, diff, externalDiff);
0452: }
0453:
0454: /**
0455: * Asserts that two files are the same, it compares two files and stores possible differencies
0456: * in the output file, the message is displayed when assertion fails.
0457: * @param message the detail message for this assertion
0458: * @param test first file to be compared, by the convention this should be the test-generated file
0459: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0460: * correct content for the test-generated file.
0461: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0462: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0463: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0464: * by the '.diff'.
0465: */
0466: static public void assertFile(String message, String test,
0467: String pass, String diff) {
0468: assertFile(message, test, pass, diff, null);
0469: }
0470:
0471: /**
0472: * Asserts that two files are the same, it compares two files and stores possible differencies
0473: * in the output file.
0474: * @param test first file to be compared, by the convention this should be the test-generated file
0475: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0476: * correct content for the test-generated file.
0477: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0478: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0479: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0480: * by the '.diff'.
0481: */
0482: static public void assertFile(String test, String pass, String diff) {
0483: assertFile(null, test, pass, diff, null);
0484: }
0485:
0486: /**
0487: * Asserts that two files are the same, it just compares two files and doesn't produce any additional output.
0488: * @param test first file to be compared, by the convention this should be the test-generated file
0489: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0490: * correct content for the test-generated file.
0491: */
0492: static public void assertFile(String test, String pass) {
0493: assertFile(null, test, pass, null, null);
0494: }
0495:
0496: /**
0497: * Asserts that two files are the same (their content is identical), when files
0498: * differ {@link org.netbeans.junit.AssertionFileFailedError AssertionFileFailedError} exception is thrown.
0499: * Depending on the Diff implementation additional output can be generated to the file/dir specified by the
0500: * <b>diff</b> param.
0501: * @param message the detail message for this assertion
0502: * @param test first file to be compared, by the convention this should be the test-generated file
0503: * @param pass second file to be comapred, it should be so called 'golden' file, which defines
0504: * the correct content for the test-generated file.
0505: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0506: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0507: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0508: * by the '.diff'.
0509: * @param externalDiff instance of class implementing the {@link org.netbeans.junit.diff.Diff} interface, it has to be
0510: * already initialized, when passed in this assertFile function.
0511: */
0512: static public void assertFile(String message, File test, File pass,
0513: File diff, Diff externalDiff) {
0514: Diff diffImpl = null == externalDiff ? Manager.getSystemDiff()
0515: : externalDiff;
0516: File diffFile = getDiffName(pass.getAbsolutePath(), diff);
0517:
0518: /*
0519: System.out.println("NbTestCase.assertFile(): diffFile="+diffFile);
0520: System.out.println("NbTestCase.assertFile(): diffImpl="+diffImpl);
0521: System.out.println("NbTestCase.assertFile(): externalDiff="+externalDiff);
0522: */
0523:
0524: if (null == diffImpl) {
0525: fail("diff is not available");
0526: } else {
0527: try {
0528: if (diffImpl.diff(test, pass, diffFile)) {
0529: throw new AssertionFileFailedError(message,
0530: null == diffFile ? "" : diffFile
0531: .getAbsolutePath());
0532: }
0533: } catch (IOException e) {
0534: fail("exception in assertFile : " + e.getMessage());
0535: }
0536: }
0537: }
0538:
0539: /**
0540: * Asserts that two files are the same, it uses specific {@link org.netbeans.junit.diff.Diff Diff} implementation to
0541: * compare two files and stores possible differencies in the output file.
0542: * @param test first file to be compared, by the convention this should be the test-generated file
0543: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0544: * correct content for the test-generated file.
0545: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0546: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0547: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0548: * by the '.diff'.
0549: * @param externalDiff instance of class implementing the {@link org.netbeans.junit.diff.Diff} interface, it has to be
0550: * already initialized, when passed in this assertFile function.
0551: */
0552: static public void assertFile(File test, File pass, File diff,
0553: Diff externalDiff) {
0554: assertFile(null, test, pass, diff, externalDiff);
0555: }
0556:
0557: /**
0558: * Asserts that two files are the same, it compares two files and stores possible differencies
0559: * in the output file, the message is displayed when assertion fails.
0560: * @param message the detail message for this assertion
0561: * @param test first file to be compared, by the convention this should be the test-generated file
0562: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0563: * correct content for the test-generated file.
0564: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0565: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0566: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0567: * by the '.diff'.
0568: */
0569: static public void assertFile(String message, File test, File pass,
0570: File diff) {
0571: assertFile(message, test, pass, diff, null);
0572: }
0573:
0574: /**
0575: * Asserts that two files are the same, it compares two files and stores possible differencies
0576: * in the output file.
0577: * @param test first file to be compared, by the convention this should be the test-generated file
0578: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0579: * correct content for the test-generated file.
0580: * @param diff file, where differences will be stored, when null differences will not be stored. In case
0581: * it points to directory the result file name is constructed from the <b>pass</b> argument and placed to that
0582: * directory. Constructed file name consists from the name of pass file (without extension and path) appended
0583: * by the '.diff'.
0584: */
0585: static public void assertFile(File test, File pass, File diff) {
0586: assertFile(null, test, pass, diff, null);
0587: }
0588:
0589: /**
0590: * Asserts that two files are the same, it just compares two files and doesn't produce any additional output.
0591: * @param test first file to be compared, by the convention this should be the test-generated file
0592: * @param pass second file to be comapred, it should be so called 'golden' file, which defines the
0593: * correct content for the test-generated file.
0594: */
0595: static public void assertFile(File test, File pass) {
0596: assertFile("Difference between " + test + " and " + pass, test,
0597: pass, null, null);
0598: }
0599:
0600: /**
0601: */
0602: static private File getDiffName(String pass, File diff) {
0603: if (null == diff)
0604: return null;
0605:
0606: if (!diff.exists() || diff.isFile())
0607: return diff;
0608:
0609: StringBuffer d = new StringBuffer();
0610: int i1, i2;
0611:
0612: d.append(diff.getAbsolutePath());
0613: i1 = pass.lastIndexOf('\\');
0614: i2 = pass.lastIndexOf('/');
0615: i1 = i1 > i2 ? i1 : i2;
0616: i1 = -1 == i1 ? 0 : i1 + 1;
0617:
0618: i2 = pass.lastIndexOf('.');
0619: i2 = -1 == i2 ? pass.length() : i2;
0620:
0621: if (0 < d.length())
0622: d.append("/");
0623:
0624: d.append(pass.substring(i1, i2));
0625: d.append(".diff");
0626: return new File(d.toString());
0627: }
0628:
0629: // methods for work with tests' workdirs
0630:
0631: /** Returns path to test method working directory as a String. Path is constructed
0632: * as ${nbjunit.workdir}/${package}.${classname}/${testmethodname}. (The nbjunit.workdir
0633: * property should be set in junit.properties; otherwise the default is ${java.io.tmpdir}/tests.)
0634: * Please note that this method does not guarantee that the working directory really exists.
0635: * @return a path to a test method working directory
0636: */
0637: public String getWorkDirPath() {
0638: if (workDirPath != null) {
0639: return workDirPath;
0640: }
0641:
0642: String name = getName();
0643: // start - PerformanceTestCase overrides getName() method and then
0644: // name can contain illegal characters
0645: String osName = System.getProperty("os.name");
0646: if (osName != null && osName.startsWith("Windows")) {
0647: char ntfsIllegal[] = { '"', '/', '\\', '?', '<', '>', '|',
0648: ':' };
0649: for (int i = 0; i < ntfsIllegal.length; i++) {
0650: name = name.replace(ntfsIllegal[i], '~');
0651: }
0652: }
0653: // end
0654:
0655: // #94319 - shorten workdir path if the following is too long
0656: // "Manager.getWorkDirPath()+File.separator+getClass().getName()+File.separator+name"
0657: int len1 = Manager.getWorkDirPath().length();
0658: String clazz = getClass().getName();
0659: int len2 = clazz.length();
0660: int len3 = name.length();
0661:
0662: int tooLong = Integer.getInteger("nbjunit.too.long", 100);
0663: if (len1 + len2 + len3 > tooLong) {
0664: clazz = abbrevDots(clazz);
0665: len2 = clazz.length();
0666: }
0667:
0668: if (len1 + len2 + len3 > tooLong) {
0669: name = abbrevCapitals(name);
0670: }
0671:
0672: String p = Manager.getWorkDirPath() + File.separator + clazz
0673: + File.separator + name;
0674: String realP;
0675:
0676: for (int i = 0;; i++) {
0677: realP = i == 0 ? p : p + "-" + i;
0678: if (usedPaths.add(realP)) {
0679: break;
0680: }
0681: }
0682:
0683: workDirPath = realP;
0684: return realP;
0685: }
0686:
0687: private static Set<String> usedPaths = new HashSet<String>();
0688:
0689: private static String abbrevDots(String dotted) {
0690: StringBuffer sb = new StringBuffer();
0691: String sep = "";
0692: for (String item : dotted.split("\\.")) {
0693: sb.append(sep);
0694: sb.append(item.charAt(0));
0695: sep = ".";
0696: }
0697: return sb.toString();
0698: }
0699:
0700: private static String abbrevCapitals(String name) {
0701: if (name.startsWith("test")) {
0702: name = name.substring(4);
0703: }
0704: StringBuffer sb = new StringBuffer();
0705: for (int i = 0; i < name.length(); i++) {
0706: if (Character.isUpperCase(name.charAt(i))) {
0707: sb.append(Character.toLowerCase(name.charAt(i)));
0708: }
0709: }
0710: return sb.toString();
0711: }
0712:
0713: /** Returns unique working directory for a test (each test method has a unique dir).
0714: * If not available, method tries to create it. This method uses {@link #getWorkDirPath}
0715: * method to determine the unique path.
0716: * <p><strong>Warning:</strong> the working directory is <em>not</em> guaranteed
0717: * to be empty when you get it, so if this is being called in {@link #setUp} you
0718: * are strongly advised to first call {@link #clearWorkDir} to ensure that each
0719: * test run starts with a clean slate.</p>
0720: * @throws IOException if the directory cannot be created
0721: * @return file to the working directory directory
0722: */
0723: public File getWorkDir() throws IOException {
0724: // construct path from workdir classpath + classname + methodname
0725:
0726: /*
0727: String path = this.getClass().getResource("").getFile().toString();
0728: String srcElement="src";
0729: String workdirElement="workdir";
0730: int srcStart = path.lastIndexOf(srcElement);
0731: // base path
0732: path = path.substring(0,srcStart)+workdirElement;
0733: // package+class
0734: path += "/"+this.getClass().getName().replace('.','/');
0735: // method name
0736: path += "/"+getName();
0737: */
0738:
0739: // new way how to get path - from defined property + classname +methodname
0740:
0741: // now we have path, so if not available, create workdir
0742: String path = getWorkDirPath();
0743: File workdir = Manager.normalizeFile(new File(path));
0744: if (workdir.exists()) {
0745: if (!workdir.isDirectory()) {
0746: // work dir exists, but is not directory - this should not happen
0747: // trow exception
0748: throw new IOException(
0749: "workdir exists, but is not a directory, workdir = "
0750: + path);
0751: } else {
0752: // everything looks correctly, return the path
0753: return workdir;
0754: }
0755: } else {
0756: // we need to create it
0757: boolean result = workdir.mkdirs();
0758: if (result == false) {
0759: // mkdirs() failed - throw an exception
0760: throw new IOException(
0761: "workdir creation failed, workdir = " + path);
0762: } else {
0763: // everything looks ok - return path
0764: return workdir;
0765: }
0766: }
0767: }
0768:
0769: // private method for deleting a file/directory (and all its subdirectories/files)
0770: private static void deleteFile(File file) throws IOException {
0771: if (file.isDirectory()) {
0772: // file is a directory - delete sub files first
0773: File files[] = file.listFiles();
0774: for (int i = 0; i < files.length; i++) {
0775: deleteFile(files[i]);
0776: }
0777:
0778: }
0779: // file is a File :-)
0780: boolean result = file.delete();
0781: if (result == false) {
0782: // a problem has appeared
0783: throw new IOException("Cannot delete file, file = "
0784: + file.getPath());
0785: }
0786: }
0787:
0788: // private method for deleting every subfiles/subdirectories of a file object
0789: static void deleteSubFiles(File file) throws IOException {
0790: if (file.isDirectory()) {
0791: File files[] = file.listFiles();
0792: for (int i = 0; i < files.length; i++) {
0793: deleteFile(files[i]);
0794: }
0795: } else {
0796: // probably do nothing - file is not a directory
0797: }
0798: }
0799:
0800: /** Deletes all files including subdirectories in test's working directory.
0801: * @throws IOException if any problem has occured during deleting files/directories
0802: */
0803: public void clearWorkDir() throws IOException {
0804: synchronized (logStreamTable) {
0805: File workdir = getWorkDir();
0806: closeAllStreams();
0807: deleteSubFiles(workdir);
0808: }
0809: }
0810:
0811: private String lastTestMethod = null;
0812:
0813: private boolean hasTestMethodChanged() {
0814: if (!this .getName().equals(lastTestMethod)) {
0815: lastTestMethod = this .getName();
0816: return true;
0817: } else {
0818: return false;
0819: }
0820: }
0821:
0822: // hashtable holding all already used logs and correspondig printstreams
0823: private Map<String, PrintStream> logStreamTable = new HashMap<String, PrintStream>();
0824:
0825: private PrintStream getFileLog(String logName) throws IOException {
0826: OutputStream outputStream;
0827: FileOutputStream fileOutputStream;
0828:
0829: synchronized (logStreamTable) {
0830: if (hasTestMethodChanged()) {
0831: // we haven't used logging capability - create hashtables
0832: closeAllStreams();
0833: } else {
0834: if (logStreamTable.containsKey(logName)) {
0835: //System.out.println("Getting stream from cache:"+logName);
0836: return logStreamTable.get(logName);
0837: }
0838: }
0839: // we didn't used this log, so let's create it
0840: OutputStream fileLog = new WFOS(new File(getWorkDir(),
0841: logName));
0842: PrintStream printStreamLog = new PrintStream(fileLog, true);
0843: logStreamTable.put(logName, printStreamLog);
0844: //System.out.println("Created new stream:"+logName);
0845: return printStreamLog;
0846: }
0847: }
0848:
0849: private void closeAllStreams() {
0850: for (PrintStream ps : logStreamTable.values()) {
0851: ps.close();
0852: }
0853: logStreamTable.clear();
0854: }
0855:
0856: private static class WFOS extends FilterOutputStream {
0857: private File f;
0858: private int bytes;
0859:
0860: public WFOS(File f) throws FileNotFoundException {
0861: super (new FileOutputStream(f));
0862: this .f = f;
0863: }
0864:
0865: @Override
0866: public void write(byte[] b, int off, int len)
0867: throws IOException {
0868: add(len);
0869: out.write(b, off, len);
0870: }
0871:
0872: @Override
0873: public void write(byte[] b) throws IOException {
0874: add(b.length);
0875: out.write(b);
0876: }
0877:
0878: @Override
0879: public void write(int b) throws IOException {
0880: add(1);
0881: out.write(b);
0882: }
0883:
0884: private synchronized void add(int i) throws IOException {
0885: bytes += i;
0886: if (bytes >= 1048576L) { // 1mb
0887: out.close();
0888: File trim = new File(f.getParent(), "TRIMMED_"
0889: + f.getName());
0890: trim.delete();
0891: f.renameTo(trim);
0892: f.delete();
0893: out = new FileOutputStream(f);
0894: bytes = 0;
0895: }
0896: }
0897:
0898: } // end of WFOS
0899:
0900: // private PrintStream wrapper for System.out
0901: PrintStream systemOutPSWrapper = new PrintStream(System.out);
0902:
0903: /** Returns named log stream. If log cannot be created as a file in the
0904: * testmethod working directory, PrintStream created from System.out is used. Please
0905: * note, that tests shoudn't call log.close() method, unless they really don't want
0906: * to use this log anymore.
0907: * @param logName name of the log - file in the working directory
0908: * @return Log PrintStream
0909: */
0910: public PrintStream getLog(String logName) {
0911: try {
0912: return getFileLog(logName);
0913: } catch (IOException ioe) {
0914: /// hey, file is not available - log will be made to System.out
0915: // we should probably write a little note about it
0916: //System.err.println("Test method "+this.getName()+" - cannot open file log to file:"+logName
0917: // +" - defaulting to System.out");
0918: return systemOutPSWrapper;
0919: }
0920: }
0921:
0922: /** Return default log named as ${testmethod}.log. If the log cannot be created
0923: * as a file in testmethod working directory, PrinterStream to System.out is returned
0924: * @return log
0925: */
0926: public PrintStream getLog() {
0927: return getLog(this .getName() + ".log");
0928: }
0929:
0930: /** Simple and easy to use method for printing a message to a default log
0931: * @param message meesage to log
0932: */
0933: public void log(String message) {
0934: getLog().println(message);
0935: }
0936:
0937: /** Easy to use method for logging a message to a named log
0938: * @param log which log to use
0939: * @param message message to log
0940: */
0941: public void log(String log, String message) {
0942: getLog(log).println(message);
0943: }
0944:
0945: // reference file stuff ...
0946:
0947: /** Get PrintStream to log inteded for reference files comparision. Reference
0948: * log is stored as a file named ${testmethod}.ref in test method working directory.
0949: * If the file cannot be created, the testcase will automatically fail.
0950: * @return PrintStream to referencing log
0951: */
0952: public PrintStream getRef() {
0953: String refFilename = this .getName() + ".ref";
0954: try {
0955: return getFileLog(refFilename);
0956: } catch (IOException ioe) {
0957: // canot get ref file - return system.out
0958: //System.err.println("Test method "+this.getName()+" - cannot open ref file:"+refFilename
0959: // +" - defaulting to System.out and failing test");
0960: fail("Could not open reference file: " + refFilename);
0961: return systemOutPSWrapper;
0962: }
0963: }
0964:
0965: /** Easy to use logging method for printing a message to a reference log.
0966: * @param message message to log
0967: */
0968: public void ref(String message) {
0969: getRef().println(message);
0970: }
0971:
0972: /** Get the test method specific golden file from ${xtest.data}/goldenfiles/${classname}
0973: * directory. If not found, try also deprecated src/data/goldenfiles/${classname}
0974: * resource directory.
0975: * @param filename filename to get from golden files directory
0976: * @return golden file
0977: */
0978: public File getGoldenFile(String filename) {
0979: String fullClassName = this .getClass().getName();
0980: String goldenFileName = fullClassName.replace('.', '/') + "/"
0981: + filename;
0982: // golden files are in ${xtest.data}/goldenfiles/${classname}/...
0983: File goldenFile = new File(getDataDir() + "/goldenfiles/"
0984: + goldenFileName);
0985: if (goldenFile.exists()) {
0986: // Return if found, otherwise try to find golden file in deprecated
0987: // location. When deprecated part is removed, add assertTrue(goldenFile.exists())
0988: // instead of if clause.
0989: return goldenFile;
0990: }
0991:
0992: /** Deprecated - this part is deprecated */
0993: // golden files are in data/goldenfiles/${classname}/* ...
0994: String className = fullClassName;
0995: int lastDot = fullClassName.lastIndexOf('.');
0996: if (lastDot != -1) {
0997: className = fullClassName.substring(lastDot + 1);
0998: }
0999: goldenFileName = className + "/" + filename;
1000: URL url = this .getClass().getResource(
1001: "data/goldenfiles/" + goldenFileName);
1002: assertNotNull(
1003: "Golden file not found in any of the following locations:\n "
1004: + goldenFile
1005: + "\n "
1006: + "src/"
1007: + fullClassName.replace('.', '/').substring(0,
1008: fullClassName.indexOf(className))
1009: + "data/goldenfiles/" + goldenFileName, url);
1010: String resString = convertNBFSURL(url);
1011: goldenFile = new File(resString);
1012: return goldenFile;
1013: /** Deprecated end. */
1014: }
1015:
1016: /** Returns pointer to directory with test data (golden files, sample files, ...).
1017: * It is the same from xtest.data property.
1018: * @return data directory
1019: */
1020: public File getDataDir() {
1021: String xtestData = System.getProperty("xtest.data");
1022: if (xtestData != null) {
1023: return Manager.normalizeFile(new File(xtestData));
1024: } else {
1025: // property not set (probably run from IDE) => try to find it
1026: String className = getClass().getName();
1027: URL url = this .getClass().getResource(
1028: className.substring(className.lastIndexOf('.') + 1)
1029: + ".class"); // NOI18N
1030: File dataDir = new File(url.getFile()).getParentFile();
1031: int index = 0;
1032: while ((index = className.indexOf('.', index) + 1) > 0) {
1033: dataDir = dataDir.getParentFile();
1034: }
1035: dataDir = new File(dataDir.getParentFile(), "data"); //NOI18N
1036: return Manager.normalizeFile(dataDir);
1037: }
1038: }
1039:
1040: /** Get the default testmethod specific golden file from
1041: * data/goldenfiles/${classname}/${testmethodname}.pass
1042: * @return filename to get from golden files resource directory
1043: */
1044: public File getGoldenFile() {
1045: return getGoldenFile(this .getName() + ".pass");
1046: }
1047:
1048: /** Compares golden file and reference log. If both files are the
1049: * same, test passes. If files differ, test fails and diff file is
1050: * created (diff is created only when using native diff, for details
1051: * see JUnit module documentation)
1052: * @param testFilename reference log file name
1053: * @param goldenFilename golden file name
1054: * @param diffFilename diff file name (optional, if null, then no diff is created)
1055: */
1056: public void compareReferenceFiles(String testFilename,
1057: String goldenFilename, String diffFilename) {
1058: try {
1059: if (!getRef().equals(systemOutPSWrapper)) {
1060: // better flush the reference file
1061: getRef().flush();
1062: getRef().close();
1063: }
1064: File goldenFile = getGoldenFile(goldenFilename);
1065: File testFile = new File(getWorkDir(), testFilename);
1066: File diffFile = new File(getWorkDir(), diffFilename);
1067: String message = "Files differ";
1068: if (System.getProperty("xtest.home") == null) {
1069: // show location of diff file only when run without XTest (run file in IDE)
1070: message += "; check " + diffFile;
1071: }
1072: assertFile(message, testFile, goldenFile, diffFile);
1073: } catch (IOException ioe) {
1074: fail("Could not obtain working direcory");
1075: }
1076: }
1077:
1078: /** Compares default golden file and default reference log. If both files are the
1079: * same, test passes. If files differ, test fails and default diff (${methodname}.diff)
1080: * file is created (diff is created only when using native diff, for details
1081: * see JUnit module documentation)
1082: */
1083: public void compareReferenceFiles() {
1084: compareReferenceFiles(this .getName() + ".ref", this .getName()
1085: + ".pass", this .getName() + ".diff");
1086: }
1087:
1088: // utility stuff for getting resources from NetBeans' filesystems
1089:
1090: /** Converts NetBeans filesystem URL to absolute path.
1091: * @param url URL to convert
1092: * @return absolute path
1093: * @deprecated No longer applicable as of NB 4.0 at the latest.
1094: * <code>FileObject.getURL()</code> should be returning a <code>file</code>-protocol
1095: * URL, which can be converted to a disk path using <code>new File(URI)</code>; or
1096: * use <code>FileUtil.toFile</code>.
1097: */
1098: public static String convertNBFSURL(URL url) {
1099: if (url == null) {
1100: throw new IllegalArgumentException(
1101: "Given URL should not be null.");
1102: }
1103: String externalForm = url.toExternalForm();
1104: if (externalForm.startsWith("nbfs://")) {
1105: // new nbfsurl format (post 06/2003)
1106: return convertNewNBFSURL(url);
1107: } else {
1108: // old nbfsurl (and non nbfs urls)
1109: return convertOldNBFSURL(url);
1110: }
1111: }
1112:
1113: // radix for new nbfsurl
1114: private final static int radix = 16;
1115:
1116: // new nbfsurl decoder - assumes the external form
1117: // begins with nbfs://
1118: private static String convertNewNBFSURL(URL url) {
1119: String externalForm = url.toExternalForm();
1120: String path;
1121: if (externalForm.startsWith("nbfs://nbhost/")) {
1122: // even newer nbfsurl (hope it does not change soon)
1123: // return path and omit first slash sign
1124: path = url.getPath().substring(1);
1125: } else {
1126: path = externalForm.substring("nbfs://".length());
1127: }
1128: // convert separators (%2f = /, etc.)
1129: StringBuffer sb = new StringBuffer();
1130: int i = 0;
1131: int len = path.length();
1132: while (i < len) {
1133: char ch = path.charAt(i++);
1134: if (ch == '%' && (i + 1) < len) {
1135: char h1 = path.charAt(i++);
1136: char h2 = path.charAt(i++);
1137: // convert d1+d2 hex number to char
1138: ch = (char) Integer.parseInt(new String("" + h1 + h2),
1139: radix);
1140:
1141: }
1142: sb.append(ch);
1143: }
1144: return sb.toString();
1145:
1146: }
1147:
1148: // old nbfsurl decoder
1149: private static String convertOldNBFSURL(URL url) {
1150: String path = url.getFile();
1151: if (url.getProtocol().equals("nbfs")) {
1152: // delete prefix of special Filesystem (e.g. org.netbeans.modules.javacvs.JavaCvsFileSystem)
1153: String prefixFS = "FileSystem ";
1154: if (path.indexOf(prefixFS) > -1) {
1155: path = path.substring(path.indexOf(prefixFS)
1156: + prefixFS.length());
1157: }
1158: // convert separators ("QB="/" etc.)
1159: StringBuffer sb = new StringBuffer();
1160: int i = 0;
1161: int len = path.length();
1162: while (i < len) {
1163: char ch = path.charAt(i++);
1164: if (ch == 'Q' && i < len) {
1165: ch = path.charAt(i++);
1166: switch (ch) {
1167: case 'B':
1168: sb.append('/');
1169: break;
1170: case 'C':
1171: sb.append(':');
1172: break;
1173: case 'D':
1174: sb.append('\\');
1175: break;
1176: case 'E':
1177: sb.append('#');
1178: break;
1179: default:
1180: // not a control sequence
1181: sb.append('Q');
1182: sb.append(ch);
1183: break;
1184: }
1185: } else {
1186: // not Q
1187: sb.append(ch);
1188: }
1189: }
1190: path = sb.toString();
1191: }
1192: return path;
1193: }
1194:
1195: /** Asserts that the object can be garbage collected. Tries to GC ref's referent.
1196: * @param text the text to show when test fails.
1197: * @param ref the referent to object that
1198: * should be GCed
1199: */
1200: public static void assertGC(String text, Reference<?> ref) {
1201: assertGC(text, ref, Collections.emptySet());
1202: }
1203:
1204: /** Asserts that the object can be garbage collected. Tries to GC ref's referent.
1205: * @param text the text to show when test fails.
1206: * @param ref the referent to object that should be GCed
1207: * @param rootsHint a set of objects that should be considered part of the
1208: * rootset for this scan. This is useful if you want to verify that one structure
1209: * (usually long living in real application) is not holding another structure
1210: * in memory, without setting a static reference to the former structure.
1211: * <h3>Example:</h3>
1212: * <pre>
1213: * // test body
1214: * WeakHashMap map = new WeakHashMap();
1215: * Object target = new Object();
1216: * map.put(target, "Val");
1217: *
1218: * // verification step
1219: * Reference ref = new WeakReference(target);
1220: * target = null;
1221: * assertGC("WeakMap does not hold the key", ref, Collections.singleton(map));
1222: * </pre>
1223: */
1224: public static void assertGC(String text, Reference<?> ref,
1225: Set<?> rootsHint) {
1226: List<byte[]> alloc = new ArrayList<byte[]>();
1227: int size = 100000;
1228: for (int i = 0; i < 50; i++) {
1229: if (ref.get() == null) {
1230: return;
1231: }
1232: System.gc();
1233: System.runFinalization();
1234: try {
1235: alloc.add(new byte[size]);
1236: size = (int) (((double) size) * 1.3);
1237: } catch (OutOfMemoryError error) {
1238: size = size / 2;
1239: }
1240: try {
1241: if (i % 3 == 0)
1242: Thread.sleep(321);
1243: } catch (InterruptedException t) {
1244: // ignore
1245: }
1246: }
1247: alloc = null;
1248: String str = null;
1249: try {
1250: str = findRefsFromRoot(ref.get(), rootsHint);
1251: } catch (Exception e) {
1252: throw new AssertionFailedErrorException(e);
1253: }
1254: fail(text + ":\n" + str);
1255: }
1256:
1257: /** Assert size of some structure. Traverses the whole reference
1258: * graph of objects accessible from given root object and check its size
1259: * against the limit.
1260: * @param message the text to show when test fails.
1261: * @param limit maximal allowed heap size of the structure
1262: * @param root the root object from which to traverse
1263: */
1264: public static void assertSize(String message, int limit, Object root) {
1265: assertSize(message, Arrays.asList(new Object[] { root }), limit);
1266: }
1267:
1268: /** Assert size of some structure. Traverses the whole reference
1269: * graph of objects accessible from given roots and check its size
1270: * against the limit.
1271: * @param message the text to show when test fails.
1272: * @param roots the collection of root objects from which to traverse
1273: * @param limit maximal allowed heap size of the structure
1274: */
1275: public static void assertSize(String message, Collection<?> roots,
1276: int limit) {
1277: assertSize(message, roots, limit, new Object[0]);
1278: }
1279:
1280: /** Assert size of some structure. Traverses the whole reference
1281: * graph of objects accessible from given roots and check its size
1282: * against the limit.
1283: * @param message the text to show when test fails.
1284: * @param roots the collection of root objects from which to traverse
1285: * @param limit maximal allowed heap size of the structure
1286: * @param skip Array of objects used as a boundary during heap scanning,
1287: * neither these objects nor references from these objects
1288: * are counted.
1289: */
1290: public static void assertSize(String message, Collection<?> roots,
1291: int limit, Object[] skip) {
1292: org.netbeans.insane.scanner.Filter f = ScannerUtils
1293: .skipObjectsFilter(Arrays.asList(skip), false);
1294: assertSize(message, roots, limit, f);
1295: }
1296:
1297: /** Assert size of some structure. Traverses the whole reference
1298: * graph of objects accessible from given roots and check its size
1299: * against the limit.
1300: * @param message the text to show when test fails.
1301: * @param roots the collection of root objects from which to traverse
1302: * @param limit maximal allowed heap size of the structure
1303: * @param skip custom filter for counted objects
1304: * @return actual size or <code>-1</code> on internal error.
1305: */
1306: public static int assertSize(String message, Collection<?> roots,
1307: int limit, final MemoryFilter skip) {
1308: org.netbeans.insane.scanner.Filter f = new org.netbeans.insane.scanner.Filter() {
1309: public boolean accept(Object o, Object refFrom, Field ref) {
1310: return !skip.reject(o);
1311: }
1312: };
1313: return assertSize(message, roots, limit, f);
1314: }
1315:
1316: private static int assertSize(String message, Collection<?> roots,
1317: int limit, org.netbeans.insane.scanner.Filter f) {
1318: try {
1319: CountingVisitor counter = new CountingVisitor();
1320: ScannerUtils.scan(f, counter, roots, false);
1321: int sum = counter.getTotalSize();
1322: if (sum > limit) {
1323: StringBuffer sb = new StringBuffer(4096);
1324: sb.append(message);
1325: sb.append(": leak " + (sum - limit) + " bytes ");
1326: sb.append(" over limit of ");
1327: sb.append(limit + " bytes");
1328: sb.append('\n');
1329: for (Iterator it = counter.getClasses().iterator(); it
1330: .hasNext();) {
1331: sb.append(" ");
1332: Class cls = (Class) it.next();
1333: if (counter.getCountForClass(cls) == 0)
1334: continue;
1335: sb.append(cls.getName()).append(": ").append(
1336: counter.getCountForClass(cls)).append(", ")
1337: .append(counter.getSizeForClass(cls))
1338: .append("B\n");
1339: }
1340: fail(sb.toString());
1341: }
1342: return sum;
1343: } catch (Exception e) {
1344: fail("Could not traverse reference graph");
1345: }
1346: return -1; // fail throws for sure
1347: }
1348:
1349: /**
1350: * Fails a test with known bug ID.
1351: * @param bugID the bug number according bug report system.
1352: */
1353: public static void failByBug(int bugID) {
1354: throw new AssertionKnownBugError(bugID);
1355: }
1356:
1357: /**
1358: * Fails a test with known bug ID and with the given message.
1359: * @param bugID the bug number according bug report system.
1360: * @param message the text to show when test fails.
1361: */
1362: public static void failByBug(int bugID, String message) {
1363: throw new AssertionKnownBugError(bugID, message);
1364: }
1365:
1366: private static String findRefsFromRoot(final Object target,
1367: final Set<?> rootsHint) throws Exception {
1368: int count = Integer.getInteger("assertgc.paths", 1);
1369: StringBuilder sb = new StringBuilder();
1370: final Map<Object, Boolean> skip = new IdentityHashMap<Object, Boolean>();
1371:
1372: org.netbeans.insane.scanner.Filter knownPath = new org.netbeans.insane.scanner.Filter() {
1373: public boolean accept(Object obj, Object referredFrom,
1374: Field reference) {
1375: return !skip.containsKey(obj);
1376: }
1377: };
1378:
1379: while (count-- > 0) {
1380: @SuppressWarnings("unchecked")
1381: Map m = LiveReferences.fromRoots(Collections
1382: .singleton(target), (Set<Object>) rootsHint, null,
1383: knownPath);
1384: Path p = (Path) m.get(target);
1385: if (p == null)
1386: break;
1387: if (sb.length() > 0)
1388: sb.append("\n\n");
1389: sb.append(p);
1390: for (; p != null; p = p.nextNode()) {
1391: Object o = p.getObject();
1392: if (o != target)
1393: skip.put(o, Boolean.TRUE);
1394: }
1395: }
1396: return sb.length() > 0 ? sb.toString() : "Not found!!!";
1397: }
1398:
1399: }
|