Lines 46-72
Link Here
|
46 |
import java.awt.event.ActionEvent; |
46 |
import java.awt.event.ActionEvent; |
47 |
import java.io.FileNotFoundException; |
47 |
import java.io.FileNotFoundException; |
48 |
import java.io.IOException; |
48 |
import java.io.IOException; |
49 |
import java.net.MalformedURLException; |
|
|
50 |
import java.net.URL; |
51 |
import java.util.ArrayList; |
49 |
import java.util.ArrayList; |
52 |
import java.util.List; |
50 |
import java.util.List; |
53 |
import java.util.Stack; |
51 |
import java.util.Stack; |
54 |
import java.util.logging.Level; |
52 |
import java.util.logging.Level; |
55 |
import java.util.logging.Logger; |
53 |
import java.util.logging.Logger; |
|
|
54 |
import java.util.regex.Matcher; |
55 |
import java.util.regex.Pattern; |
56 |
import javax.swing.AbstractAction; |
56 |
import javax.swing.AbstractAction; |
57 |
import javax.swing.Action; |
57 |
import javax.swing.Action; |
|
|
58 |
import org.netbeans.api.java.classpath.GlobalPathRegistry; |
59 |
import org.netbeans.api.project.Project; |
60 |
import org.netbeans.modules.gsf.testrunner.api.CallstackFrameNode; |
61 |
import org.netbeans.modules.gsf.testrunner.api.DiffViewAction; |
62 |
import org.netbeans.modules.gsf.testrunner.api.Manager; |
63 |
import org.netbeans.modules.gsf.testrunner.api.TestMethodNode; |
64 |
import org.netbeans.modules.gsf.testrunner.api.TestRunnerNodeFactory; |
65 |
import org.netbeans.modules.gsf.testrunner.api.TestSession; |
66 |
import org.netbeans.modules.gsf.testrunner.api.TestSuite; |
67 |
import org.netbeans.modules.gsf.testrunner.api.Testcase; |
68 |
import org.netbeans.modules.gsf.testrunner.api.TestsuiteNode; |
69 |
import org.netbeans.modules.gsf.testrunner.api.Trouble; |
58 |
import org.netbeans.modules.hudson.api.ConnectionBuilder; |
70 |
import org.netbeans.modules.hudson.api.ConnectionBuilder; |
59 |
import org.netbeans.modules.hudson.api.HudsonJob; |
71 |
import org.netbeans.modules.hudson.api.HudsonJob; |
60 |
import org.netbeans.modules.hudson.api.HudsonJobBuild; |
72 |
import org.netbeans.modules.hudson.api.HudsonJobBuild; |
61 |
import org.netbeans.modules.hudson.api.HudsonMavenModuleBuild; |
73 |
import org.netbeans.modules.hudson.api.HudsonMavenModuleBuild; |
62 |
import org.openide.awt.HtmlBrowser.URLDisplayer; |
74 |
import org.netbeans.modules.hudson.spi.HudsonLogger; |
|
|
75 |
import org.netbeans.modules.hudson.ui.interfaces.OpenableInBrowser; |
76 |
import org.openide.filesystems.FileObject; |
77 |
import org.openide.filesystems.FileUtil; |
78 |
import org.openide.nodes.Node; |
79 |
import org.openide.util.Lookup; |
63 |
import org.openide.util.NbBundle.Messages; |
80 |
import org.openide.util.NbBundle.Messages; |
64 |
import static org.netbeans.modules.hudson.ui.actions.Bundle.*; |
81 |
import static org.netbeans.modules.hudson.ui.actions.Bundle.*; |
65 |
import org.openide.util.RequestProcessor; |
82 |
import org.openide.util.RequestProcessor; |
|
|
83 |
import org.openide.util.lookup.Lookups; |
66 |
import org.openide.windows.IOProvider; |
84 |
import org.openide.windows.IOProvider; |
67 |
import org.openide.windows.InputOutput; |
85 |
import org.openide.windows.InputOutput; |
68 |
import org.openide.windows.OutputEvent; |
|
|
69 |
import org.openide.windows.OutputListener; |
70 |
import org.openide.windows.OutputWriter; |
86 |
import org.openide.windows.OutputWriter; |
71 |
import org.openide.xml.XMLUtil; |
87 |
import org.openide.xml.XMLUtil; |
72 |
import org.xml.sax.Attributes; |
88 |
import org.xml.sax.Attributes; |
Lines 106-113
Link Here
|
106 |
new RequestProcessor(url + "testReport").post(this); // NOI18N |
122 |
new RequestProcessor(url + "testReport").post(this); // NOI18N |
107 |
} |
123 |
} |
108 |
|
124 |
|
109 |
@Messages({"# {0} - job #build", "ShowFailures.title={0} Test Failures", |
125 |
private static final Pattern ASSERTION_FAILURE = Pattern.compile("(?m)junit[.]framework[.](AssertionFailedError|(Array)?ComparisonFailure)|java[.]lang[.]AssertionError($|: )"); |
110 |
"# {0} - class & method name of failed test", "# {1} - suite name of failed test", "ShowFailures.from_suite={0} (from {1})"}) |
126 |
|
|
|
127 |
@Messages({ |
128 |
"# {0} - job #build", "ShowFailures.title={0} Test Failures", |
129 |
"# {0} - class & method name of failed test", "# {1} - suite name of failed test", "ShowFailures.from_suite={0} (from {1})", |
130 |
"LBL_GotoSource=Go to Source" |
131 |
}) |
111 |
public void run() { |
132 |
public void run() { |
112 |
try { |
133 |
try { |
113 |
XMLReader parser = XMLUtil.createXMLReader(); |
134 |
XMLReader parser = XMLUtil.createXMLReader(); |
Lines 115-125
Link Here
|
115 |
InputOutput io; |
136 |
InputOutput io; |
116 |
StringBuilder buf; |
137 |
StringBuilder buf; |
117 |
Hyperlinker hyperlinker = new Hyperlinker(job); |
138 |
Hyperlinker hyperlinker = new Hyperlinker(job); |
|
|
139 |
TestSession session = new TestSession(displayName, new Project() { |
140 |
public @Override FileObject getProjectDirectory() { |
141 |
return FileUtil.createMemoryFileSystem().getRoot(); |
142 |
} |
143 |
public @Override Lookup getLookup() { |
144 |
return Lookup.EMPTY; |
145 |
} |
146 |
}, TestSession.SessionType.TEST, new TestRunnerNodeFactory() { |
147 |
public @Override TestsuiteNode createTestSuiteNode(String suiteName, boolean filtered) { |
148 |
// XXX could add OpenableInBrowser |
149 |
return new TestsuiteNode(suiteName, filtered); |
150 |
} |
151 |
public @Override Node createTestMethodNode(final Testcase testcase, Project project) { |
152 |
return new TestMethodNode(testcase, project, Lookups.singleton(new OpenableInBrowser() { |
153 |
public @Override String getUrl() { |
154 |
return url + "testReport/" + testcase.getClassName().replaceFirst("[.][^.]+$", "") + "/" + testcase.getClassName().replaceFirst(".+[.]", "") + "/" + testcase.getName() + "/"; |
155 |
} |
156 |
})) { |
157 |
public @Override Action[] getActions(boolean context) { |
158 |
return new Action[] { |
159 |
// XXX singleton disabled since TR window has no activatedNodes (and no other parent of ResultTreeView implements Lookup.Provider) |
160 |
OpenUrlAction.get(OpenUrlAction.class).createContextAwareInstance(Lookups.singleton(this)), |
161 |
new DiffViewAction(testcase), |
162 |
}; |
163 |
} |
164 |
}; |
165 |
} |
166 |
public @Override Node createCallstackFrameNode(String frameInfo, String displayName) { |
167 |
return new CallstackFrameNode(frameInfo, displayName) { |
168 |
public @Override Action getPreferredAction() { |
169 |
return new AbstractAction(LBL_GotoSource()) { |
170 |
FileObject f; |
171 |
int line; |
172 |
{ |
173 |
// XXX should have utility API to parse stack traces |
174 |
Matcher m = Pattern.compile("\tat (.+[.])[^.]+[.][^.]+[(]([^.]+[.]java):([0-9]+)[)]").matcher(frameInfo); |
175 |
if (m.matches()) { |
176 |
String resource = m.group(1).replace('.', '/') + m.group(2); |
177 |
f = GlobalPathRegistry.getDefault().findResource(resource); |
178 |
line = Integer.parseInt(m.group(3)); |
179 |
LOG.log(Level.FINER, "matched {0} -> {1}", new Object[] {resource, f}); |
180 |
} else { |
181 |
LOG.log(Level.FINER, "no match for {0}", frameInfo); |
182 |
} |
183 |
setEnabled(f != null); |
184 |
} |
185 |
public @Override void actionPerformed(ActionEvent e) { |
186 |
if (f != null) { |
187 |
HudsonLogger.Helper.openAt(f, line - 1, -1, true); |
188 |
} |
189 |
} |
190 |
}; |
191 |
} |
192 |
public @Override Action[] getActions(boolean context) { |
193 |
return new Action[] {getPreferredAction()}; |
194 |
} |
195 |
}; |
196 |
} |
197 |
}); |
118 |
private void prepareOutput() { |
198 |
private void prepareOutput() { |
119 |
if (io == null) { |
199 |
if (io == null) { |
120 |
String title = ShowFailures_title(displayName); |
200 |
String title = ShowFailures_title(displayName); |
121 |
io = IOProvider.getDefault().getIO(title, new Action[0]); |
201 |
io = IOProvider.getDefault().getIO(title, new Action[0]); |
122 |
io.select(); |
202 |
io.select(); |
|
|
203 |
Manager.getInstance().testStarted(session); |
123 |
} |
204 |
} |
124 |
} |
205 |
} |
125 |
class Suite { |
206 |
class Suite { |
Lines 128-138
Link Here
|
128 |
String stderr; |
209 |
String stderr; |
129 |
Stack<Case> cases = new Stack<Case>(); |
210 |
Stack<Case> cases = new Stack<Case>(); |
130 |
List<Case> casesDone = new ArrayList<Case>(); |
211 |
List<Case> casesDone = new ArrayList<Case>(); |
|
|
212 |
long duration; |
131 |
} |
213 |
} |
132 |
class Case { |
214 |
class Case { |
133 |
String className; |
215 |
String className; |
134 |
String name; |
216 |
String name; |
135 |
String errorStackTrace; |
217 |
String errorStackTrace; |
|
|
218 |
long duration; |
219 |
} |
220 |
long parseDuration(String d) { |
221 |
if (d == null) { |
222 |
return 0; |
223 |
} |
224 |
try { |
225 |
return (long) (1000 * Float.parseFloat(d)); |
226 |
} catch (NumberFormatException x) { |
227 |
return 0; |
228 |
} |
136 |
} |
229 |
} |
137 |
Stack<Suite> suites = new Stack<Suite>(); |
230 |
Stack<Suite> suites = new Stack<Suite>(); |
138 |
public @Override void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
231 |
public @Override void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
Lines 163-168
Link Here
|
163 |
s.stderr = text; |
256 |
s.stderr = text; |
164 |
} else if (qName.equals("name")) { // NOI18N |
257 |
} else if (qName.equals("name")) { // NOI18N |
165 |
s.name = text; |
258 |
s.name = text; |
|
|
259 |
} else if (qName.equals("duration")) { // NOI18N |
260 |
s.duration = parseDuration(text); |
166 |
} |
261 |
} |
167 |
} else { // case level |
262 |
} else { // case level |
168 |
Case c = s.cases.peek(); |
263 |
Case c = s.cases.peek(); |
Lines 172-177
Link Here
|
172 |
c.name = text; |
267 |
c.name = text; |
173 |
} else if (qName.equals("className")) { // NOI18N |
268 |
} else if (qName.equals("className")) { // NOI18N |
174 |
c.className = text; |
269 |
c.className = text; |
|
|
270 |
} else if (qName.equals("duration")) { // NOI18N |
271 |
c.duration = parseDuration(text); |
175 |
} |
272 |
} |
176 |
} |
273 |
} |
177 |
if (qName.equals("suite")) { // NOI18N |
274 |
if (qName.equals("suite")) { // NOI18N |
Lines 189-224
Link Here
|
189 |
prepareOutput(); |
286 |
prepareOutput(); |
190 |
OutputWriter out = io.getOut(); |
287 |
OutputWriter out = io.getOut(); |
191 |
OutputWriter err = io.getErr(); |
288 |
OutputWriter err = io.getErr(); |
|
|
289 |
TestSuite suite = new TestSuite(s.name); |
290 |
session.addSuite(suite); |
291 |
Manager.getInstance().displaySuiteRunning(session, suite.getName()); |
292 |
if (s.stderr != null) { |
293 |
// XXX TR window does not seem to show only stdio from selected suite |
294 |
Manager.getInstance().displayOutput(session, s.stderr, true); |
295 |
} |
296 |
if (s.stdout != null) { |
297 |
Manager.getInstance().displayOutput(session, s.stdout, false); |
298 |
} |
192 |
for (final Case c : s.casesDone) { |
299 |
for (final Case c : s.casesDone) { |
193 |
if (c.errorStackTrace == null) { |
300 |
if (c.errorStackTrace == null) { |
194 |
continue; |
301 |
continue; |
195 |
} |
302 |
} |
196 |
String name = c.className + "." + c.name; |
303 |
String name = c.className + "." + c.name; |
|
|
304 |
String shortName = c.name; |
197 |
if (s.name != null && !s.name.equals(c.className)) { |
305 |
if (s.name != null && !s.name.equals(c.className)) { |
|
|
306 |
shortName = name; |
198 |
name = ShowFailures_from_suite(name, s.name); |
307 |
name = ShowFailures_from_suite(name, s.name); |
199 |
} |
308 |
} |
200 |
println(); |
309 |
println(); |
201 |
out.println(name, new OutputListener() { |
310 |
out.println("[" + name + "]"); // XXX use color printing to make it stand out? |
202 |
public void outputLineAction(OutputEvent ev) { |
|
|
203 |
try { |
204 |
// XXX try to find name in Java method index and open it instead |
205 |
URLDisplayer.getDefault().showURL(new URL(url + |
206 |
"testReport/" + c.className.replaceFirst("[.][^.]+$", "") + "/" + // NOI18N |
207 |
c.className.replaceFirst(".+[.]", "") + "/" + c.name + "/")); // NOI18Nb |
208 |
} catch (MalformedURLException x) { |
209 |
LOG.log(Level.FINE, null, x); |
210 |
} |
211 |
} |
212 |
public void outputLineSelected(OutputEvent ev) {} |
213 |
public void outputLineCleared(OutputEvent ev) {} |
214 |
}); |
215 |
show(c.errorStackTrace, /* err is too hard to read */ out); |
311 |
show(c.errorStackTrace, /* err is too hard to read */ out); |
|
|
312 |
Testcase test = new Testcase(shortName, null, session); |
313 |
test.setClassName(c.className); |
314 |
Trouble trouble = new Trouble(!ASSERTION_FAILURE.matcher(c.errorStackTrace).lookingAt()); |
315 |
trouble.setStackTrace(c.errorStackTrace.split("\r?\n")); |
316 |
// XXX call setComparisonFailure if matches "expected:<...> but was:<...>" |
317 |
test.setTrouble(trouble); |
318 |
LOG.log(Level.FINE, "got {0} as {1}", new Object[] {name, test.getStatus()}); |
319 |
test.setTimeMillis(c.duration); |
320 |
session.addTestCase(test); |
216 |
} |
321 |
} |
217 |
if (s.stderr != null || s.stdout != null) { |
322 |
if (s.stderr != null || s.stdout != null) { |
218 |
println(); |
323 |
println(); |
219 |
show(s.stderr, err); |
324 |
show(s.stderr, err); |
220 |
show(s.stdout, out); |
325 |
show(s.stdout, out); |
221 |
} |
326 |
} |
|
|
327 |
Manager.getInstance().displayReport(session, session.getReport(s.duration)); |
222 |
} |
328 |
} |
223 |
boolean firstLine = true; |
329 |
boolean firstLine = true; |
224 |
void println() { |
330 |
void println() { |
Lines 240-245
Link Here
|
240 |
if (io != null) { |
346 |
if (io != null) { |
241 |
io.getOut().close(); |
347 |
io.getOut().close(); |
242 |
io.getErr().close(); |
348 |
io.getErr().close(); |
|
|
349 |
Manager.getInstance().sessionFinished(session); |
243 |
} |
350 |
} |
244 |
} |
351 |
} |
245 |
}); |
352 |
}); |