001: /* ForkTest.java */
002:
003: package org.quilt.frontend.ant;
004:
005: import org.apache.tools.ant.BuildException;
006: import org.apache.tools.ant.Project;
007: import org.apache.tools.ant.Task;
008: import org.apache.tools.ant.taskdefs.Execute;
009: import org.apache.tools.ant.taskdefs.ExecuteWatchdog; // need this
010: import org.apache.tools.ant.taskdefs.LogStreamHandler;
011: import org.apache.tools.ant.types.CommandlineJava;
012: import org.apache.tools.ant.types.EnumeratedAttribute;
013: import org.apache.tools.ant.types.Environment;
014: import org.apache.tools.ant.types.Path;
015: import org.apache.tools.ant.util.FileUtils;
016:
017: import java.io.File;
018: import java.io.FileOutputStream;
019: import java.io.IOException;
020: import java.io.OutputStream;
021:
022: import java.util.Enumeration;
023: import java.util.Hashtable;
024: import java.util.Properties;
025: import java.util.Vector;
026:
027: import junit.framework.AssertionFailedError;
028: import junit.framework.Test;
029: import junit.framework.TestResult;
030:
031: import org.quilt.framework.*;
032: import org.quilt.reports.*;
033:
034: // //////////////////////////////////////////////////////////////////
035: // NEEDS LOTS OF ATTENTION //////////////////////////////////////////
036: // //////////////////////////////////////////////////////////////////
037: //
038: /**
039: * Run an individual test in a separate JVM. Search on Bug 23150;
040: * needs to return correct status code if interrupted or times out.
041: */
042: public class ForkTest {
043:
044: private Project project = null;
045: private Task task = null;
046: private TaskControl tc = null;
047: private QuiltTest qt = null;
048: private boolean mockery = false;
049: private boolean checkingCoverage = false;
050:
051: /** No-arg constructor. */
052: public ForkTest() {
053: }
054:
055: /**
056: * Fork an individual test, running it in a separate Java virtual
057: * machine.
058: *
059: * @param qt Data structure holding test parameters.
060: * @param tc Structure holding parameters for the entire run.
061: * @param watchdog Sets timeout in ms for this test.
062: */
063: protected int execTest(QuiltTest qt, TaskControl tc,
064: ExecuteWatchdog watchdog) throws BuildException {
065:
066: this .tc = tc;
067: task = tc.getTask();
068: project = task.getProject();
069: mockery = qt.getMockTestRun();
070: checkingCoverage = qt.getCheckCoverage();
071:
072: CommandlineJava cmd = (CommandlineJava) tc.getCommandline()
073: .clone();
074:
075: if (mockery) {
076: task.log("ForkTest: setting class name to MockTestRunner");
077: cmd.setClassname("org.quilt.textui.MockTestRunner");
078: } else {
079: cmd.setClassname("org.quilt.textui.TestRunner");
080: }
081: // //////////////////////////////////////////////////////////
082: // THIS INTERFACE MUST BE KEPT IN SYNC WITH textui.TestRunner
083: // and textui.MockTestRunner
084: // //////////////////////////////////////////////////////////
085: cmd.createArgument().setValue(qt.getName());
086: cmd.createArgument().setValue(
087: "checkCoverage=" + checkingCoverage);
088: if (checkingCoverage) {
089: String excluded = qt.getCheckExcludes();
090: String included = qt.getCheckIncludes();
091: if (excluded != null) {
092: cmd.createArgument().setValue(
093: "checkExcludes=" + excluded);
094: }
095: if (included != null) {
096: cmd.createArgument().setValue(
097: "checkIncludes=" + included);
098: }
099: }
100: cmd.createArgument().setValue(
101: "filtertrace=" + qt.getFiltertrace());
102: cmd.createArgument().setValue(
103: "haltOnError=" + qt.getHaltOnError());
104: cmd.createArgument().setValue(
105: "haltOnFailure=" + qt.getHaltOnFailure());
106:
107: if (tc.getIncludeAntRuntime()) {
108: task.log("Adding " + tc.getAntRuntimeClasses()
109: + " to CLASSPATH", Project.MSG_VERBOSE);
110: cmd.createClasspath(project).createPath().append(
111: tc.getAntRuntimeClasses());
112: }
113:
114: if (tc.getSummary()) {
115: task.log("Running " + qt.getName(), Project.MSG_INFO);
116: cmd.createArgument().setValue(
117: "formatter=org.quilt.reports.SummaryFormatter");
118: }
119:
120: cmd.createArgument().setValue(
121: "showoutput=" + String.valueOf(qt.getShowOutput()));
122:
123: StringBuffer formatterArg = new StringBuffer(256);
124: final FmtSelector[] selectors = tc.mergeSelectors(qt);
125: for (int i = 0; i < selectors.length; i++) {
126: FmtSelector fs = selectors[i];
127: formatterArg.append("formatter=");
128: formatterArg.append(fs.getClassname());
129: File outFile = tc.getOutput(fs, qt);
130: if (outFile != null) {
131: formatterArg.append(",");
132: formatterArg.append(outFile);
133: }
134: cmd.createArgument().setValue(formatterArg.toString());
135: formatterArg.setLength(0);
136: }
137:
138: File propsFile = FileUtils.newFileUtils().createTempFile(
139: "quilt", ".properties", project.getBaseDir());
140: cmd.createArgument().setValue(
141: "propsfile=" + propsFile.getAbsolutePath());
142: Hashtable p = project.getProperties();
143: Properties props = new Properties();
144: for (Enumeration e = p.keys(); e.hasMoreElements();) {
145: Object key = e.nextElement();
146: props.put(key, p.get(key));
147: }
148: try {
149: FileOutputStream outstream = new FileOutputStream(propsFile);
150: // props.save() is deprecated
151: props.save(outstream,
152: "Ant QuiltTask generated properties file");
153: outstream.close();
154: } catch (java.io.IOException e) {
155: propsFile.delete();
156: throw new BuildException(
157: "Error creating temporary properties " + "file.",
158: e, task.getLocation());
159: }
160: // prepare to fork the test
161: Execute forker = new Execute(new LogStreamHandler(task,
162: Project.MSG_INFO, Project.MSG_WARN), watchdog);
163: forker.setCommandline(cmd.getCommandline());
164: forker.setAntRun(project);
165: if (tc.getDir() != null) {
166: forker.setWorkingDirectory(tc.getDir());
167: }
168:
169: String[] environment = tc.getEnv().getVariables();
170: if (environment != null) {
171: for (int i = 0; i < environment.length; i++) {
172: task.log("Setting environment variable: "
173: + environment[i], Project.MSG_VERBOSE);
174: }
175: }
176: forker.setNewenvironment(tc.getNewEnvironment());
177: forker.setEnvironment(environment);
178:
179: task.log(cmd.describeCommand(), Project.MSG_VERBOSE);
180:
181: int retVal;
182: try {
183: retVal = forker.execute(); // do the actual fork
184: } catch (IOException e) {
185: throw new BuildException("Error forking test", e, task
186: .getLocation());
187: } finally {
188: if (watchdog != null && watchdog.killedProcess()) {
189: logTimeout(selectors, qt);
190: // see Bug 23150; also needs to be set to 1 if interrrupted
191: retVal = 1;
192: }
193:
194: if (!propsFile.delete()) {
195: throw new BuildException(
196: "Error deleting temporary properties file.");
197: }
198: }
199: return retVal;
200: }
201:
202: // DO WE REALLY WANT TO DO THIS, ONCE PER FORMATTER ?
203:
204: private void logTimeout(FmtSelector[] selectors, QuiltTest qt) {
205:
206: for (int i = 0; i < selectors.length; i++) {
207: FmtSelector fs = selectors[i];
208: File outFile = tc.getOutput(fs, qt);
209: Formatter formatter = fs.createFormatter();
210: if (outFile != null && formatter != null) {
211: try {
212: OutputStream out = new FileOutputStream(outFile);
213: formatter.setOutput(out);
214: formatter.startTestSuite(qt);
215: qt.setCounts(0, 0, 1);
216: Test t = new Test() {
217: public int countTestCases() {
218: return 0;
219: }
220:
221: public void run(TestResult r) {
222: throw new AssertionFailedError(
223: "Timeout during test run.");
224: }
225: };
226: formatter.startTest(t);
227: formatter.addError(t, new AssertionFailedError(
228: "Timeout during test run."));
229:
230: formatter.endTestSuite(qt);
231: } catch (IOException e) {
232: // just ignore any more exceptions
233: }
234: }
235: }
236: }
237: }
|