001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2003-2007 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs;
021:
022: import java.io.File;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.OutputStream;
026:
027: import junit.framework.TestCase;
028: import edu.umd.cs.findbugs.config.UserPreferences;
029:
030: /**
031: * Abstract base class for TestCase classes that need to
032: * run in the context of a FindBugs2 object doing a full
033: * execution. Ensures that things like AnalysisCache,
034: * AnalysisContext, etc. are fully initialized.
035: *
036: * <p> Is this mock objects? Or is this just a hack?
037: * Probably the latter :-)
038: *
039: * @author David Hovemeyer
040: */
041: public abstract class FindBugsTestCase extends TestCase {
042: /**
043: * Data of an empty class in the default package called "Empty".
044: */
045: public static final byte[] EMPTY_CLASS_DATA = { (byte) 0xca,
046: (byte) 0xfe, (byte) 0xba, (byte) 0xbe, (byte) 0x00,
047: (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x00,
048: (byte) 0x0d, (byte) 0x0a, (byte) 0x00, (byte) 0x03,
049: (byte) 0x00, (byte) 0x0a, (byte) 0x07, (byte) 0x00,
050: (byte) 0x0b, (byte) 0x07, (byte) 0x00, (byte) 0x0c,
051: (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x3c,
052: (byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74,
053: (byte) 0x3e, (byte) 0x01, (byte) 0x00, (byte) 0x03,
054: (byte) 0x28, (byte) 0x29, (byte) 0x56, (byte) 0x01,
055: (byte) 0x00, (byte) 0x04, (byte) 0x43, (byte) 0x6f,
056: (byte) 0x64, (byte) 0x65, (byte) 0x01, (byte) 0x00,
057: (byte) 0x0f, (byte) 0x4c, (byte) 0x69, (byte) 0x6e,
058: (byte) 0x65, (byte) 0x4e, (byte) 0x75, (byte) 0x6d,
059: (byte) 0x62, (byte) 0x65, (byte) 0x72, (byte) 0x54,
060: (byte) 0x61, (byte) 0x62, (byte) 0x6c, (byte) 0x65,
061: (byte) 0x01, (byte) 0x00, (byte) 0x0a, (byte) 0x53,
062: (byte) 0x6f, (byte) 0x75, (byte) 0x72, (byte) 0x63,
063: (byte) 0x65, (byte) 0x46, (byte) 0x69, (byte) 0x6c,
064: (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0a,
065: (byte) 0x45, (byte) 0x6d, (byte) 0x70, (byte) 0x74,
066: (byte) 0x79, (byte) 0x2e, (byte) 0x6a, (byte) 0x61,
067: (byte) 0x76, (byte) 0x61, (byte) 0x0c, (byte) 0x00,
068: (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x01,
069: (byte) 0x00, (byte) 0x05, (byte) 0x45, (byte) 0x6d,
070: (byte) 0x70, (byte) 0x74, (byte) 0x79, (byte) 0x01,
071: (byte) 0x00, (byte) 0x10, (byte) 0x6a, (byte) 0x61,
072: (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c,
073: (byte) 0x61, (byte) 0x6e, (byte) 0x67, (byte) 0x2f,
074: (byte) 0x4f, (byte) 0x62, (byte) 0x6a, (byte) 0x65,
075: (byte) 0x63, (byte) 0x74, (byte) 0x00, (byte) 0x21,
076: (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03,
077: (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
078: (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01,
079: (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05,
080: (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06,
081: (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x1d,
082: (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01,
083: (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x05,
084: (byte) 0x2a, (byte) 0xb7, (byte) 0x00, (byte) 0x01,
085: (byte) 0xb1, (byte) 0x00, (byte) 0x00, (byte) 0x00,
086: (byte) 0x01, (byte) 0x00, (byte) 0x07, (byte) 0x00,
087: (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x00,
088: (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
089: (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00,
090: (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
091: (byte) 0x02, (byte) 0x00, (byte) 0x09, };
092:
093: private static final class TestRunnerThread extends Thread {
094: private RunnableWithExceptions runnable;
095: private JUnitDetectorAdapter detectorAdapter;
096:
097: private TestRunnerThread(RunnableWithExceptions runnable) {
098: this .runnable = runnable;
099: }
100:
101: /**
102: * @return Returns the detectorAdapter.
103: */
104: public JUnitDetectorAdapter getDetectorAdapter() {
105: return detectorAdapter;
106: }
107:
108: /* (non-Javadoc)
109: * @see java.lang.Thread#run()
110: */
111: @Override
112: public void run() {
113: try {
114: runTest(runnable);
115: } catch (Exception e) {
116: // Hmm...
117: System.err.println("Exception running test:");
118: e.printStackTrace();
119: }
120: }
121:
122: private void runTest(RunnableWithExceptions runnable)
123: throws IOException, InterruptedException {
124: // Create temporary directory in filesystem
125: File tmpdir = File.createTempFile("fbtest", null);
126: if (!tmpdir.delete() || !tmpdir.mkdir()) {
127: throw new IOException("Could not create temp dir");
128: }
129:
130: File tmpfile = null;
131:
132: try {
133: // Create a class file to analyze
134: tmpfile = createEmptyClassFile(tmpdir);
135:
136: // Unfortunately there's quite a bit of gobbledygook required
137: // to set up a FindBugs2.
138:
139: FindBugs2 engine = new FindBugs2();
140:
141: engine.setBugReporter(new PrintingBugReporter());
142:
143: // Analyze the temporary directory we just created
144: Project project = new Project();
145: project.addFile(tmpdir.getAbsolutePath());
146:
147: engine.setProject(project);
148:
149: DetectorFactoryCollection dfc = new DetectorFactoryCollection();
150: DetectorFactoryCollection.resetInstance(dfc);
151:
152: Plugin fakePlugin = new Plugin(
153: "edu.umd.cs.findbugs.fakeplugin", null);
154: fakePlugin.setEnabled(true);
155:
156: dfc.setPlugins(new Plugin[] { fakePlugin });
157:
158: DetectorFactory detectorFactory = new DetectorFactory(
159: fakePlugin, JUnitDetectorAdapter.class, true,
160: "fast", "", "");
161: fakePlugin.addDetectorFactory(detectorFactory);
162: dfc.registerDetector(detectorFactory);
163: if (!dfc.factoryIterator().hasNext()
164: || !fakePlugin.detectorFactoryIterator()
165: .hasNext()) {
166: throw new IllegalStateException();
167: }
168:
169: engine.setDetectorFactoryCollection(dfc);
170:
171: engine.setUserPreferences(UserPreferences
172: .createDefaultUserPreferences());
173:
174: JUnitDetectorAdapter.setRunnable(runnable);
175:
176: engine.execute();
177:
178: // Get a handle to the JUnitDetectorAdapter, since it is the
179: // object that knows whether or not the test code actually passed
180: // or failed.
181: detectorAdapter = JUnitDetectorAdapter.instance();
182: } catch (Throwable t) {
183: t.printStackTrace();
184: } finally {
185: if (tmpfile != null) {
186: deleteAndLog(tmpfile);
187: }
188: deleteAndLog(tmpdir);
189: }
190: }
191:
192: void deleteAndLog(File f) {
193: if (!f.delete())
194: System.err.println("Could not delete " + f);
195: }
196:
197: /**
198: * @param tmpdir
199: * @throws IOException
200: */
201: private File createEmptyClassFile(File tmpdir)
202: throws IOException {
203: File outFile = new File(tmpdir, "Empty.class");
204: OutputStream out = new FileOutputStream(outFile);
205: try {
206: out.write(EMPTY_CLASS_DATA);
207: } finally {
208: out.close();
209: }
210: return outFile;
211: }
212: }
213:
214: /**
215: * Execute some JUnit test code inside a Detector2 class
216: * running inside a FindBugs2 analysis run.
217: * In theory, any code legal in a FindBugs detector should
218: * work.
219: *
220: * @param runnable a RunnableWithExceptions object whose run() method has some JUnit test code
221: * @throws Throwable
222: */
223: protected void executeFindBugsTest(
224: final RunnableWithExceptions runnable) throws Exception {
225: TestRunnerThread thread = new TestRunnerThread(runnable);
226:
227: thread.start();
228: try {
229: thread.join();
230: } catch (InterruptedException e) {
231: throw new IllegalStateException();
232: }
233:
234: if (thread.getDetectorAdapter() == null) {
235: throw new IllegalStateException(
236: "Test code did not complete");
237: }
238: thread.getDetectorAdapter().finishTest();
239: }
240: }
|