File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\tasks\test\TestObject.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5 * Tom Huybrechts, Yahoo!, Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 package hudson.tasks.test;
26
27 import hudson.Util;
28 import hudson.Functions;
29 import hudson.model.*;
30 import hudson.tasks.junit.History;
31 import hudson.tasks.junit.TestAction;
32 import hudson.tasks.junit.TestResultAction;
33 import jenkins.model.Jenkins;
34
35 import org.kohsuke.stapler.*;
36 import org.kohsuke.stapler.export.ExportedBean;
37
38 import com.google.common.collect.MapMaker;
39
40 import javax.servlet.ServletException;
41 import java.io.IOException;
42 import java.util.*;
43 import java.util.logging.Logger;
44
45 /**
46 * Base class for all test result objects.
47 * For compatibility with code that expects this class to be in hudson.tasks.junit,
48 * we've created a pure-abstract class, hudson.tasks.junit.TestObject. That
49 * stub class is deprecated; instead, people should use this class.
50 *
51 * @author Kohsuke Kawaguchi
52 */
53 @ExportedBean
54 public abstract class TestObject extends hudson.tasks.junit.TestObject {
55
56 private static final Logger LOGGER = Logger.getLogger(TestObject.class.getName());
57 private volatile transient String id;
58
59 public abstract AbstractBuild<?, ?> getOwner();
60
61 /**
62 * Reverse pointer of {@link TabulatedResult#getChildren()}.
63 */
64 public abstract TestObject getParent();
65
66 @Override
67 public final String getId() {
68 if (id == null) {
69 StringBuilder buf = new StringBuilder();
70 buf.append(getSafeName());
71
72 TestObject parent = getParent();
73 if (parent != null) {
74 String parentId = parent.getId();
75 if ((parentId != null) && (parentId.length() > 0)) {
76 buf.insert(0, '/');
77 buf.insert(0, parent.getId());
78 }
79 }
80 id = buf.toString();
81 }
82 return id;
83 }
84
85 /**
86 * Returns url relative to TestResult
87 */
88 @Override
89 public String getUrl() {
90 return '/' + getId();
91 }
92
93 /**
94 * Returns the top level test result data.
95 *
96 * @deprecated This method returns a JUnit specific class. Use
97 * {@link #getTopLevelTestResult()} instead for a more general interface.
98 * @return
99 */
100 @Override
101 public hudson.tasks.junit.TestResult getTestResult() {
102 TestObject parent = getParent();
103
104 return (parent == null ? null : getParent().getTestResult());
105 }
106
107 /**
108 * Returns the top level test result data.
109 *
110 * @return
111 */
112 public TestResult getTopLevelTestResult() {
113 TestObject parent = getParent();
114
115 return (parent == null ? null : getParent().getTopLevelTestResult());
116 }
117
118 /**
119 * Computes the relative path to get to this test object from <code>it</code>. If
120 * <code>it</code> does not appear in the parent chain for this object, a
121 * relative path from the server root will be returned.
122 *
123 * @return A relative path to this object, potentially from the top of the
124 * Hudson object model
125 */
126 public String getRelativePathFrom(TestObject it) {
127
128
129 // if (it is one of my ancestors) {
130 // return a relative path from it
131 // } else {
132 // return a complete path starting with "/"
133 // }
134 if (it==this) {
135 return ".";
136 }
137
138 StringBuilder buf = new StringBuilder();
139 TestObject next = this;
140 TestObject cur = this;
141 // Walk up my ancesotors from leaf to root, looking for "it"
142 // and accumulating a relative url as I go
143 while (next!=null && it!=next) {
144 cur = next;
145 buf.insert(0,'/');
146 buf.insert(0,cur.getSafeName());
147 next = cur.getParent();
148 }
149 if (it==next) {
150 return buf.toString();
151 } else {
152 // Keep adding on to the string we've built so far
153
154 // Start with the test result action
155 AbstractTestResultAction action = getTestResultAction();
156 if (action==null) {
157 LOGGER.warning("trying to get relative path, but we can't determine the action that owns this result.");
158 return ""; // this won't take us to the right place, but it also won't 404.
159 }
160 buf.insert(0,'/');
161 buf.insert(0,action.getUrlName());
162
163 // Now the build
164 AbstractBuild<?,?> myBuild = cur.getOwner();
165 if (myBuild ==null) {
166 LOGGER.warning("trying to get relative path, but we can't determine the build that owns this result.");
167 return ""; // this won't take us to the right place, but it also won't 404.
168 }
169 buf.insert(0,'/');
170 buf.insert(0,myBuild.getUrl());
171
172 // If we're inside a stapler request, just delegate to Hudson.Functions to get the relative path!
173 StaplerRequest req = Stapler.getCurrentRequest();
174 if (req!=null && myBuild instanceof Item) {
175 buf.insert(0, '/');
176 // Ugly but I don't see how else to convince the compiler that myBuild is an Item
177 Item myBuildAsItem = (Item) myBuild;
178 buf.insert(0, Functions.getRelativeLinkTo(myBuildAsItem));
179 } else {
180 // We're not in a stapler request. Okay, give up.
181 LOGGER.info("trying to get relative path, but it is not my ancestor, and we're not in a stapler request. Trying absolute hudson url...");
182 String hudsonRootUrl = Jenkins.getInstance().getRootUrl();
183 if (hudsonRootUrl==null||hudsonRootUrl.length()==0) {
184 LOGGER.warning("Can't find anything like a decent hudson url. Punting, returning empty string.");
185 return "";
186
187 }
188 buf.insert(0, '/');
189 buf.insert(0, hudsonRootUrl);
190 }
191
192 LOGGER.info("Here's our relative path: " + buf.toString());
193 return buf.toString();
194 }
195
196 }
197
198 /**
199 * Subclasses may override this method if they are
200 * associated with a particular subclass of
201 * AbstractTestResultAction.
202 *
203 * @return the test result action that connects this test result to a particular build
204 */
205 @Override
206 public AbstractTestResultAction getTestResultAction() {
207 AbstractBuild<?, ?> owner = getOwner();
208 if (owner != null) {
209 return owner.getAction(AbstractTestResultAction.class);
210 } else {
211 LOGGER.warning("owner is null when trying to getTestResultAction.");
212 return null;
213 }
214 }
215
216 /**
217 * Get a list of all TestActions associated with this TestObject.
218 * @return
219 */
220 @Override
221 public List<TestAction> getTestActions() {
222 AbstractTestResultAction atra = getTestResultAction();
223 if ((atra != null) && (atra instanceof TestResultAction)) {
224 TestResultAction tra = (TestResultAction) atra;
225 return tra.getActions(this);
226 } else {
227 return new ArrayList<TestAction>();
228 }
229 }
230
231 /**
232 * Gets a test action of the class passed in.
233 * @param klazz
234 * @param <T> an instance of the class passed in
235 * @return
236 */
237 @Override
238 public <T> T getTestAction(Class<T> klazz) {
239 for (TestAction action : getTestActions()) {
240 if (klazz.isAssignableFrom(action.getClass())) {
241 return klazz.cast(action);
242 }
243 }
244 return null;
245 }
246
247 /**
248 * Gets the counterpart of this {@link TestResult} in the previous run.
249 *
250 * @return null if no such counter part exists.
251 */
252 public abstract TestResult getPreviousResult();
253
254 /**
255 * Gets the counterpart of this {@link TestResult} in the specified run.
256 *
257 * @return null if no such counter part exists.
258 */
259 public abstract TestResult getResultInBuild(AbstractBuild<?, ?> build);
260
261 /**
262 * Find the test result corresponding to the one identified by <code>id></code>
263 * withint this test result.
264 *
265 * @param id The path to the original test result
266 * @return A corresponding test result, or null if there is no corresponding
267 * result.
268 */
269 public abstract TestResult findCorrespondingResult(String id);
270
271 /**
272 * Time took to run this test. In seconds.
273 */
274 public abstract float getDuration();
275
276 /**
277 * Returns the string representation of the {@link #getDuration()}, in a
278 * human readable format.
279 */
280 @Override
281 public String getDurationString() {
282 return Util.getTimeSpanString((long) (getDuration() * 1000));
283 }
284
285 @Override
286 public String getDescription() {
287 AbstractTestResultAction action = getTestResultAction();
288 if (action != null) {
289 return action.getDescription(this);
290 }
291 return "";
292 }
293
294 @Override
295 public void setDescription(String description) {
296 AbstractTestResultAction action = getTestResultAction();
297 if (action != null) {
298 action.setDescription(this, description);
299 }
300 }
301
302 /**
303 * Exposes this object through the remote API.
304 */
305 @Override
306 public Api getApi() {
307 return new Api(this);
308 }
309
310 /**
311 * Gets the name of this object.
312 */
313 @Override
314 public/* abstract */ String getName() {
315 return "";
316 }
317
318 /**
319 * Gets the version of {@link #getName()} that's URL-safe.
320 */
321 @Override
322 public String getSafeName() {
323 return safe(getName());
324 }
325
326 @Override
327 public String getSearchUrl() {
328 return getSafeName();
329 }
330
331 /**
332 * #2988: uniquifies a {@link #getSafeName} amongst children of the parent.
333 */
334 protected final String uniquifyName(Collection<? extends TestObject> siblings, String base) {
335 synchronized (UNIQUIFIED_NAMES) {
336 String uniquified = base;
337 Map<TestObject,Void> taken = UNIQUIFIED_NAMES.get(base);
338 if (taken == null) {
339 taken = new WeakHashMap<TestObject,Void>();
340 UNIQUIFIED_NAMES.put(base, taken);
341 } else {
342 Set<TestObject> similars = new HashSet<TestObject>(taken.keySet());
343 similars.retainAll(new HashSet<TestObject>(siblings));
344 if (!similars.isEmpty()) {
345 uniquified = base + '_' + (similars.size() + 1);
346 }
347 }
348 taken.put(this, null);
349 return uniquified;
350 }
351 }
352 private static final Map<String,Map<TestObject,Void>> UNIQUIFIED_NAMES = new MapMaker().makeMap();
353
354 /**
355 * Replaces URL-unsafe characters.
356 */
357 public static String safe(String s) {
358 // this still seems to be a bit faster than a single replace with regexp
359 return s.replace('/', '_').replace('\\', '_').replace(':', '_').replace('?', '_').replace('#', '_');
360
361 // Note: we probably should some helpers like Commons URIEscapeUtils here to escape all invalid URL chars, but then we
362 // still would have to escape /, ? and so on
363 }
364
365 /**
366 * Gets the total number of passed tests.
367 */
368 public abstract int getPassCount();
369
370 /**
371 * Gets the total number of failed tests.
372 */
373 public abstract int getFailCount();
374
375 /**
376 * Gets the total number of skipped tests.
377 */
378 public abstract int getSkipCount();
379
380 /**
381 * Gets the total number of tests.
382 */
383 @Override
384 public int getTotalCount() {
385 return getPassCount() + getFailCount() + getSkipCount();
386 }
387
388 @Override
389 public History getHistory() {
390 return new History(this);
391 }
392
393 public Object getDynamic(String token, StaplerRequest req,
394 StaplerResponse rsp) {
395 for (Action a : getTestActions()) {
396 if (a == null) {
397 continue; // be defensive
398 }
399 String urlName = a.getUrlName();
400 if (urlName == null) {
401 continue;
402 }
403 if (urlName.equals(token)) {
404 return a;
405 }
406 }
407 return null;
408 }
409
410 public synchronized HttpResponse doSubmitDescription(
411 @QueryParameter String description) throws IOException,
412 ServletException {
413 if (getOwner() == null) {
414 LOGGER.severe("getOwner() is null, can't save description.");
415 } else {
416 getOwner().checkPermission(Run.UPDATE);
417 setDescription(description);
418 getOwner().save();
419 }
420
421 return new HttpRedirect(".");
422 }
423 private static final long serialVersionUID = 1L;
424 }
425