5151import java .util .Collections ;
5252import java .util .Comparator ;
5353import java .util .List ;
54+ import java .util .logging .Level ;
5455import java .util .logging .Logger ;
5556
5657import static java .util .Collections .emptyList ;
5758import static java .util .Collections .singletonList ;
5859
60+ import javax .xml .stream .XMLStreamException ;
61+ import javax .xml .stream .XMLStreamReader ;
62+
5963/**
6064 * One test result.
6165 *
6569 */
6670public class CaseResult extends TestResult implements Comparable <CaseResult > {
6771 private static final Logger LOGGER = Logger .getLogger (CaseResult .class .getName ());
68- private final float duration ;
72+ private float duration ;
6973 /**
7074 * Start time in epoch milliseconds - default is -1 for unset
7175 */
@@ -74,17 +78,18 @@ public class CaseResult extends TestResult implements Comparable<CaseResult> {
7478 * In JUnit, a test is a method of a class. This field holds the fully qualified class name
7579 * that the test was in.
7680 */
77- private final String className ;
81+ private String className ;
7882 /**
7983 * This field retains the method name.
8084 */
81- private final String testName ;
85+ private String testName ;
8286 private transient String safeName ;
83- private final boolean skipped ;
84- private final String skippedMessage ;
85- private final String errorStackTrace ;
86- private final String errorDetails ;
87- private final Map <String , String > properties ;
87+ private boolean skipped ;
88+ private boolean keepTestNames ;
89+ private String skippedMessage ;
90+ private String errorStackTrace ;
91+ private String errorDetails ;
92+ private Map <String , String > properties ;
8893 @ SuppressFBWarnings (value = "SE_TRANSIENT_FIELD_NOT_RESTORED" , justification = "Specific method to restore it" )
8994 private transient SuiteResult parent ;
9095
@@ -98,14 +103,14 @@ public class CaseResult extends TestResult implements Comparable<CaseResult> {
98103 * If these information are reported at the test case level, these fields are set,
99104 * otherwise null, in which case {@link SuiteResult#stdout}.
100105 */
101- private final String stdout ,stderr ;
106+ private String stdout ,stderr ;
102107
103108 /**
104109 * This test has been failing since this build number (not id.)
105110 *
106111 * If {@link #isPassed() passing}, this field is left unused to 0.
107112 */
108- private /*final*/ int failedSince ;
113+ private int failedSince ;
109114
110115 private static float parseTime (Element testCase ) {
111116 String time = testCase .attributeValue ("time" );
@@ -138,6 +143,7 @@ public CaseResult(SuiteResult parent, String testName, String errorStackTrace, S
138143 this .skipped = false ;
139144 this .skippedMessage = null ;
140145 this .properties = Collections .emptyMap ();
146+ this .keepTestNames = false ;
141147 }
142148
143149 @ Restricted (Beta .class )
@@ -165,14 +171,15 @@ public CaseResult(
165171 this .skipped = skippedMessage != null ;
166172 this .skippedMessage = skippedMessage ;
167173 this .properties = Collections .emptyMap ();
174+ this .keepTestNames = false ;
168175 }
169176
170177 @ Deprecated
171- CaseResult (SuiteResult parent , Element testCase , String testClassName , boolean keepLongStdio , boolean keepProperties ) {
172- this (parent , testCase , testClassName , StdioRetention .fromKeepLongStdio (keepLongStdio ), keepProperties );
178+ CaseResult (SuiteResult parent , Element testCase , String testClassName , boolean keepLongStdio , boolean keepProperties , boolean keepTestNames ) {
179+ this (parent , testCase , testClassName , StdioRetention .fromKeepLongStdio (keepLongStdio ), keepProperties , keepTestNames );
173180 }
174181
175- CaseResult (SuiteResult parent , Element testCase , String testClassName , StdioRetention stdioRetention , boolean keepProperties ) {
182+ CaseResult (SuiteResult parent , Element testCase , String testClassName , StdioRetention stdioRetention , boolean keepProperties , boolean keepTestNames ) {
176183 // schema for JUnit report XML format is not available in Ant,
177184 // so I don't know for sure what means what.
178185 // reports in http://www.nabble.com/difference-in-junit-publisher-and-ant-junitreport-tf4308604.html#a12265700
@@ -200,7 +207,7 @@ public CaseResult(
200207 errorStackTrace = getError (testCase );
201208 errorDetails = getErrorMessage (testCase );
202209 this .parent = parent ;
203- duration = parseTime (testCase );
210+ duration = clampDuration ( parseTime (testCase ) );
204211 this .startTime = -1 ;
205212 skipped = isMarkedAsSkipped (testCase );
206213 skippedMessage = getSkippedMessage (testCase );
@@ -226,6 +233,138 @@ public CaseResult(
226233 }
227234 }
228235 this .properties = properties ;
236+ this .keepTestNames = keepTestNames ;
237+ }
238+
239+ public CaseResult (CaseResult src ) {
240+ this .duration = src .duration ;
241+ this .className = src .className ;
242+ this .testName = src .testName ;
243+ this .skippedMessage = src .skippedMessage ;
244+ this .skipped = src .skipped ;
245+ this .keepTestNames = src .keepTestNames ;
246+ this .errorStackTrace = src .errorStackTrace ;
247+ this .errorDetails = src .errorDetails ;
248+ this .failedSince = src .failedSince ;
249+ this .stdout = src .stdout ;
250+ this .stderr = src .stderr ;
251+ this .properties = new HashMap <>();
252+ this .properties .putAll (src .properties );
253+ }
254+
255+ public static float clampDuration (float d ) {
256+ return Math .min (365.0f * 24 * 60 * 60 , Math .max (0.0f , d ));
257+ }
258+
259+ public static CaseResult parse (SuiteResult parent , final XMLStreamReader reader , String ver ) throws XMLStreamException {
260+ CaseResult r = new CaseResult (parent , null , null , null );
261+ while (reader .hasNext ()) {
262+ final int event = reader .next ();
263+ if (event == XMLStreamReader .END_ELEMENT && reader .getLocalName ().equals ("case" )) {
264+ return r ;
265+ }
266+ if (event == XMLStreamReader .START_ELEMENT ) {
267+ final String elementName = reader .getLocalName ();
268+ switch (elementName ) {
269+ case "duration" :
270+ r .duration = clampDuration (new TimeToFloat (reader .getElementText ()).parse ());
271+ break ;
272+ case "startTime" :
273+ r .startTime = Long .parseLong (reader .getElementText ());
274+ break ;
275+ case "className" :
276+ r .className = reader .getElementText ();
277+ break ;
278+ case "testName" :
279+ r .testName = reader .getElementText ();
280+ break ;
281+ case "skippedMessage" :
282+ r .skippedMessage = reader .getElementText ();
283+ break ;
284+ case "skipped" :
285+ r .skipped = Boolean .parseBoolean (reader .getElementText ());
286+ break ;
287+ case "keepTestNames" :
288+ r .keepTestNames = Boolean .parseBoolean (reader .getElementText ());
289+ break ;
290+ case "errorStackTrace" :
291+ r .errorStackTrace = reader .getElementText ();
292+ break ;
293+ case "errorDetails" :
294+ r .errorDetails = reader .getElementText ();
295+ break ;
296+ case "failedSince" :
297+ r .failedSince = Integer .parseInt (reader .getElementText ());
298+ break ;
299+ case "stdout" :
300+ r .stdout = reader .getElementText ();
301+ break ;
302+ case "stderr" :
303+ r .stderr = reader .getElementText ();
304+ break ;
305+ case "properties" :
306+ r .properties = new HashMap <>();
307+ parseProperties (r .properties , reader , ver );
308+ break ;
309+ default :
310+ if (LOGGER .isLoggable (Level .FINEST )) {
311+ LOGGER .finest ("CaseResult.parse encountered an unknown field: " + elementName );
312+ }
313+ }
314+ }
315+ }
316+ return r ;
317+ }
318+
319+ public static void parseProperties (Map <String , String > r , final XMLStreamReader reader , String ver ) throws XMLStreamException {
320+ while (reader .hasNext ()) {
321+ final int event = reader .next ();
322+ if (event == XMLStreamReader .END_ELEMENT && reader .getLocalName ().equals ("properties" )) {
323+ return ;
324+ }
325+ if (event == XMLStreamReader .START_ELEMENT ) {
326+ final String elementName = reader .getLocalName ();
327+ switch (elementName ) {
328+ case "entry" :
329+ parseProperty (r , reader , ver );
330+ break ;
331+ default :
332+ if (LOGGER .isLoggable (Level .FINEST )) {
333+ LOGGER .finest ("CaseResult.parseProperties encountered an unknown field: " + elementName );
334+ }
335+ }
336+ }
337+ }
338+ }
339+
340+ public static void parseProperty (Map <String , String > r , final XMLStreamReader reader , String ver ) throws XMLStreamException {
341+ String name = null , value = null ;
342+ while (reader .hasNext ()) {
343+ final int event = reader .next ();
344+ if (event == XMLStreamReader .END_ELEMENT && reader .getLocalName ().equals ("entry" )) {
345+ if (name != null && value != null ) {
346+ r .put (name , value );
347+ }
348+ return ;
349+ }
350+ if (event == XMLStreamReader .START_ELEMENT ) {
351+ final String elementName = reader .getLocalName ();
352+ switch (elementName ) {
353+ case "string" :
354+ if (name == null ) {
355+ name = reader .getElementText ();
356+ } else {
357+ value = reader .getElementText ();
358+ }
359+ break ;
360+ default :
361+ if (LOGGER .isLoggable (Level .FINEST )) {
362+ LOGGER .finest ("CaseResult.parseProperty encountered an unknown field: " + elementName );
363+ }
364+ }
365+ }
366+ }
367+
229368 }
230369
231370 static String possiblyTrimStdio (Collection <CaseResult > results , StdioRetention stdioRetention , String stdio ) { // HUDSON-6516
@@ -354,7 +493,7 @@ public String getDisplayName() {
354493 private String getNameWithEnclosingBlocks (String rawName ) {
355494 // Only prepend the enclosing flow node names if there are any and the run this is in has multiple blocks directly containing
356495 // test results.
357- if (!getEnclosingFlowNodeNames ().isEmpty ()) {
496+ if (!keepTestNames && ! getEnclosingFlowNodeNames ().isEmpty ()) {
358497 Run <?, ?> r = getRun ();
359498 if (r != null ) {
360499 TestResultAction action = r .getAction (TestResultAction .class );
@@ -579,15 +718,27 @@ public String getStderr() {
579718 return getSuiteResult ().getStderr ();
580719 }
581720
721+ static int PREVIOUS_TEST_RESULT_BACKTRACK_BUILDS_MAX =
722+ Integer .parseInt (System .getProperty (History .HistoryTableResult .class .getName () + ".PREVIOUS_TEST_RESULT_BACKTRACK_BUILDS_MAX" ,"25" ));
723+
582724 @ Override
583725 public CaseResult getPreviousResult () {
584726 if (parent == null ) return null ;
585727
586- TestResult previousResult = parent .getParent ().getPreviousResult ();
587- if (previousResult == null ) return null ;
588- if (previousResult instanceof hudson .tasks .junit .TestResult ) {
589- hudson .tasks .junit .TestResult pr = (hudson .tasks .junit .TestResult ) previousResult ;
590- return pr .getCase (parent .getName (), getTransformedFullDisplayName ());
728+ TestResult previousResult = parent .getParent ();
729+ int n = 0 ;
730+ while (previousResult != null && n < PREVIOUS_TEST_RESULT_BACKTRACK_BUILDS_MAX ) {
731+ previousResult = previousResult .getPreviousResult ();
732+ if (previousResult == null )
733+ return null ;
734+ if (previousResult instanceof hudson .tasks .junit .TestResult ) {
735+ hudson .tasks .junit .TestResult pr = (hudson .tasks .junit .TestResult ) previousResult ;
736+ CaseResult cr = pr .getCase (parent .getName (), getTransformedFullDisplayName ());
737+ if (cr != null ) {
738+ return cr ;
739+ }
740+ }
741+ ++n ;
591742 }
592743 return null ;
593744 }
0 commit comments