1 /***
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Exoffice Technologies. For written permission,
18 * please contact tma@netspace.net.au.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Exoffice Technologies. Exolab is a registered
23 * trademark of Exoffice Technologies.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2001-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: TestCoverage.java,v 1.5 2004/01/31 13:44:24 tanderson Exp $
44 */
45 package org.exolab.jmscts.core;
46
47 import java.util.ArrayList;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.List;
52
53 import org.apache.log4j.Category;
54
55 import junit.framework.Test;
56
57 import org.exolab.jmscts.report.Context;
58 import org.exolab.jmscts.report.Coverage;
59 import org.exolab.jmscts.report.CurrentTest;
60 import org.exolab.jmscts.report.Failure;
61 import org.exolab.jmscts.report.ReportHelper;
62 import org.exolab.jmscts.report.RequirementCoverage;
63 import org.exolab.jmscts.report.TestRun;
64 import org.exolab.jmscts.report.TestRuns;
65 import org.exolab.jmscts.requirements.Requirement;
66 import org.exolab.jmscts.requirements.Requirements;
67
68
69 /***
70 * This class maintains the state of the test suite's coverage of requirements
71 *
72 * @version $Revision: 1.5 $ $Date: 2004/01/31 13:44:24 $
73 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
74 * @see Coverage
75 * @see Requirements
76 */
77 public class TestCoverage {
78
79 /***
80 * A map of requirement id's to {@link Coverage} instances
81 */
82 private HashMap _coverage = new HashMap();
83
84 /***
85 * A map of test names to their corresponding TestRuns instances
86 */
87 private HashMap _testRuns = new HashMap();
88
89 /***
90 * A list of failures not associated with any test case
91 */
92 private List _failures = new ArrayList();
93
94 /***
95 * The requirements
96 */
97 private Requirements _requirements;
98
99 /***
100 * The current executing test case
101 */
102 private JMSTestCase _current;
103
104 /***
105 * The current test run
106 */
107 private TestRun _currentRun;
108
109 /***
110 * The logger
111 */
112 private static final Category log =
113 Category.getInstance(TestCoverage.class);
114
115
116 /***
117 * Construct an instance with the set of requirements
118 *
119 * @param requirements the requirements
120 */
121 public TestCoverage(Requirements requirements) {
122 if (requirements == null) {
123 throw new IllegalArgumentException(
124 "Argument 'requirements' is null");
125 }
126 _requirements = requirements;
127 }
128
129 /***
130 * Log the start of a test.</br>
131 * NOTE: {@link #begin} may not be invoked if the test setup fails. In
132 * this instance, only {@link #failed} will be invoked
133 *
134 * @param test the test case
135 */
136 public synchronized void begin(JMSTestCase test) {
137 String[] requirements = test.getRequirements(test.getName());
138 if (requirements == null || requirements.length == 0) {
139 log.error("Test=" + ReportHelper.getName(test) + " started but "
140 + "is not associated with any requirements");
141 } else {
142 _current = test;
143 _currentRun = getTest(test, test.getContext());
144 for (int i = 0; i < requirements.length; ++i) {
145 addCoverage(requirements[i], test);
146 }
147 }
148 }
149
150 /***
151 * Log the end of a test
152 *
153 * @param test the test that has completed
154 */
155 public synchronized void end(JMSTestCase test) {
156 _current = null;
157 _currentRun = null;
158 }
159
160 /***
161 * Log a test case failure
162 *
163 * @param test the test case
164 * @param cause the reason for the failure
165 * @param rootCause the root cause of the failure. May be null
166 */
167 public synchronized void failed(Test test, Throwable cause,
168 Throwable rootCause) {
169 if (test instanceof JMSTestCase) {
170 addFailure((JMSTestCase) test, cause, rootCause);
171 } else if (test instanceof TestRunner) {
172 addFailure((TestRunner) test, cause, rootCause);
173 } else {
174 addFailure(cause, rootCause);
175 }
176 }
177
178 /***
179 * Set a requirement as being unsupported
180 *
181 * @param requirementId the requirement identifier
182 */
183 public synchronized void setUnsupported(String requirementId) {
184 Coverage coverage = (Coverage) _coverage.get(requirementId);
185 if (coverage == null) {
186 log.error("Invalid requirement identifier=" + requirementId);
187 } else {
188 coverage.setSupported(false);
189 }
190 }
191
192 /***
193 * Returns the coverage of requirements by the test suite
194 *
195 * @return the coverage of requirements by the test suite
196 */
197 public synchronized RequirementCoverage getCoverage() {
198 RequirementCoverage result = new RequirementCoverage();
199
200 if (_current != null) {
201 // if a test case is currently executing, return its state
202 CurrentTest current = new CurrentTest();
203 current.setTest(ReportHelper.getName(_current));
204 current.setTestRun(_currentRun);
205 String[] requirements = _current.getRequirements(
206 _current.getName());
207 for (int i = 0; i < requirements.length; ++i) {
208 Requirement requirement = _requirements.getRequirement(
209 requirements[i]);
210 if (requirement != null) {
211 current.addRequirementId(requirements[i]);
212 }
213 }
214 result.setCurrentTest(current);
215 }
216
217 HashSet covered = new HashSet();
218 HashSet requirementIds = new HashSet(
219 _requirements.getRequirements().keySet());
220
221 // add the requirements covered by the test suite
222 Iterator iter = _coverage.values().iterator();
223 while (iter.hasNext()) {
224 Coverage coverage = (Coverage) iter.next();
225 result.addCoverage(coverage);
226 covered.add(coverage.getRequirementId());
227 }
228
229 // add the list of all requirements not covered by the test suite
230 requirementIds.removeAll(covered);
231 iter = requirementIds.iterator();
232 while (iter.hasNext()) {
233 String requirementId = (String) iter.next();
234 Coverage coverage = createCoverage(requirementId);
235 result.addCoverage(coverage);
236 }
237
238 // add the test details
239 iter = _testRuns.values().iterator();
240 while (iter.hasNext()) {
241 TestRuns runs = (TestRuns) iter.next();
242 result.addTestRuns(runs);
243 }
244
245 // add failures not associated with any test case
246 iter = _failures.iterator();
247 while (iter.hasNext()) {
248 result.addFailure((Failure) iter.next());
249 }
250 return result;
251 }
252
253 /***
254 * Returns the test state for a particular test, creating it if it
255 * doesn't exist
256 *
257 * @param test the test case
258 * @param context the test context. May be null.
259 * @return the state of the test
260 */
261 private TestRun getTest(JMSTestCase test, TestContext context) {
262 TestRun result = null;
263 String name = ReportHelper.getName(test);
264 TestRuns testRuns = (TestRuns) _testRuns.get(name);
265 if (testRuns == null) {
266 testRuns = ReportHelper.getTestRuns(test);
267 _testRuns.put(name, testRuns);
268 } else if (context != null) {
269 // see if it exists already
270 Context mapped = ReportHelper.getContext(context);
271 TestRun[] runs = testRuns.getTestRun();
272 for (int i = 0; i < runs.length; ++i) {
273 if (runs[i].getContext().equals(mapped)) {
274 result = runs[i];
275 break;
276 }
277 }
278 }
279 if (result == null) {
280 result = ReportHelper.getTestRun(test, context);
281 testRuns.addTestRun(result);
282 }
283 return result;
284 }
285
286 /***
287 * Add requirement coverage
288 *
289 * @param requirementId the requirement identifier
290 * @param test the test case
291 */
292 private void addCoverage(String requirementId, JMSTestCase test) {
293 Coverage coverage = getCoverage(requirementId, test);
294
295 if (coverage != null) {
296 String name = ReportHelper.getName(test);
297 String[] tests = coverage.getTest();
298 boolean found = false;
299 for (int i = 0; i < tests.length; ++i) {
300 if (tests[i].equals(name)) {
301 found = true;
302 break;
303 }
304 }
305 if (!found) {
306 coverage.addTest(name);
307 }
308 int runs = coverage.getRuns();
309 coverage.setRuns(runs + 1);
310 }
311 }
312
313 /***
314 * Add a test case failure
315 *
316 * @param test the test case
317 * @param cause the reason for the failure
318 * @param rootCause the root cause of the failure. May be null
319 */
320 private void addFailure(JMSTestCase test, Throwable cause,
321 Throwable rootCause) {
322 addFailure(test, test.getContext(), cause, rootCause);
323 }
324
325 /***
326 * Add a test case failure
327 *
328 * @param test the test case
329 * @param context the test context. May be null.
330 * @param cause the reason for the failure
331 * @param rootCause the root cause of the failure. May be null
332 */
333 private void addFailure(JMSTestCase test, TestContext context,
334 Throwable cause, Throwable rootCause) {
335 TestRun run = getTest(test, context);
336 run.setFailure(ReportHelper.getFailure(cause, rootCause));
337
338 // get the requirements associated with the test
339 String[] requirements = test.getRequirements(test.getName());
340 if (requirements == null || requirements.length == 0) {
341 log.error("Test=" + ReportHelper.getName(test)
342 + " is not associated with any requirements");
343 } else {
344 // increment the failure count against each requirement
345 // associated with the test
346 for (int i = 0; i < requirements.length; ++i) {
347 Coverage coverage = getCoverage(requirements[i], test);
348 if (coverage != null) {
349 int failures = coverage.getFailures();
350 coverage.setFailures(failures + 1);
351 }
352 }
353 }
354 }
355
356 /***
357 * Add a failure occuring during setUp/tearDown of a test
358 *
359 * @param runner the test runner
360 * @param cause the reason for the failure
361 * @param rootCause the root cause of the failure. May be null
362 */
363 private void addFailure(TestRunner runner, Throwable cause,
364 Throwable rootCause) {
365 // locate the test case
366 Test test = runner.getTest();
367 while (test instanceof TestRunner) {
368 test = ((TestRunner) test).getTest();
369 }
370 if (test instanceof JMSTestCase) {
371 // log the failure, using the context associated with the runner
372 addFailure((JMSTestCase) test, runner.getContext(), cause,
373 rootCause);
374 } else {
375 // shouldn't happen
376 addFailure(cause, rootCause);
377 }
378 }
379
380 /***
381 * Add a failure for a test not associated with any requirements
382 *
383 * @param cause the reason for the failure
384 * @param rootCause the root cause of the failure. May be null
385 */
386 private void addFailure(Throwable cause, Throwable rootCause) {
387 _failures.add(ReportHelper.getFailure(cause, rootCause));
388 }
389
390 /***
391 * Returns a coverage instance for the specified requirement identifier.
392 * If none exists, it will be created.
393 *
394 * @param requirementId the requirement identifier
395 * @param test the test case
396 * @return a coverage instance for the specified requirement identifier,
397 * or <code>null</code> if <code>requirementId</code> is invalid
398 */
399 private Coverage getCoverage(String requirementId, JMSTestCase test) {
400 Coverage coverage = (Coverage) _coverage.get(requirementId);
401 if (coverage == null) {
402 Requirement requirement = _requirements.getRequirement(
403 requirementId);
404 if (requirement != null) {
405 coverage = createCoverage(requirementId);
406 _coverage.put(requirementId, coverage);
407 } else {
408 log.error("Invalid requirement identifier="
409 + requirementId + " used by test="
410 + ReportHelper.getName(test));
411 }
412 }
413 return coverage;
414 }
415
416 /***
417 * Create a new coverage instance
418 *
419 * @param requirementId the requirement identifier
420 * @return a new coverage instance
421 */
422 private Coverage createCoverage(String requirementId) {
423 Coverage coverage = new Coverage();
424 coverage.setRequirementId(requirementId);
425 coverage.setRuns(0);
426 coverage.setFailures(0);
427 return coverage;
428 }
429
430 }
This page was automatically generated by Maven