001: package org.hansel;
002:
003: import java.lang.annotation.ElementType;
004: import java.lang.annotation.Retention;
005: import java.lang.annotation.RetentionPolicy;
006: import java.lang.annotation.Target;
007: import java.util.HashSet;
008:
009: import org.junit.internal.runners.InitializationError;
010: import org.junit.runner.Description;
011: import org.junit.runner.notification.Failure;
012: import org.junit.runner.notification.RunNotifier;
013: import org.junit.runners.Suite;
014:
015: public class CoverageRunner extends Suite {
016: private ProbeTable probeTable;
017: private Class[] coveredClasses;
018: private Description desc;
019: private Description coverageDesc;
020: private boolean initialized;
021:
022: /**
023: * The <code>CoverClasses</code> annotation specifies the classes to be covered
024: * when the test suite is run.
025: */
026: @Retention(RetentionPolicy.RUNTIME)
027: @Target(ElementType.TYPE)
028: public @interface CoverClasses {
029: public Class[] value();
030: }
031:
032: /**
033: * Internal use only.
034: */
035: public CoverageRunner(Class<?> klass) throws InitializationError {
036: super (klass);
037: this .probeTable = new ProbeTable(null);
038: this .coveredClasses = getCoveredClasses(klass);
039: this .initialized = false;
040: }
041:
042: private static Class[] getCoveredClasses(Class<?> klass)
043: throws InitializationError {
044: CoverClasses annotation = klass
045: .getAnnotation(CoverClasses.class);
046: if (annotation == null)
047: throw new InitializationError(String.format(
048: "class '%s' must have a CoverClasses annotation",
049: klass.getName()));
050:
051: return annotation.value();
052: }
053:
054: private HashSet<String> toStringSet(Class[] classes) {
055: HashSet<String> coveredClasses = new HashSet<String>();
056: for (Class clazz : classes) {
057: coveredClasses.add(clazz.getName());
058: }
059: return coveredClasses;
060:
061: }
062:
063: @Override
064: public void run(RunNotifier notifier) {
065: init();
066:
067: CheckFailureRunListener listener = new CheckFailureRunListener();
068: notifier.addListener(listener);
069: super .run(notifier);
070:
071: shutdown(notifier, listener.hasFailures());
072: }
073:
074: protected void init() {
075: if (initialized) {
076: return;
077: }
078:
079: try {
080: ProbeTable.setProbeTable(probeTable);
081: Startup.init(toStringSet(coveredClasses));
082: initialized = true;
083: } catch (Exception e) {
084: throw new IllegalStateException(e);
085: }
086: }
087:
088: protected void shutdown(RunNotifier result, boolean hasErrors) {
089: try {
090: loadClasses();
091: Startup.tearDown();
092:
093: result.fireTestStarted(getCoverageDescription());
094: probeTable.run(result, getCoverageDescription(), hasErrors);
095: } catch (Exception e) {
096: result.fireTestFailure(new Failure(
097: getCoverageDescription(), e));
098: } finally {
099: result.fireTestFinished(getCoverageDescription());
100: }
101: }
102:
103: private void loadClasses() {
104: // Make sure all classes are linked, so all probes get created.
105: for (Class clazz : coveredClasses) {
106: clazz.getDeclaredFields();
107: }
108: }
109:
110: public Description getCoverageDescription() {
111: if (coverageDesc == null) {
112: coverageDesc = Description
113: .createSuiteDescription("Coverage");
114: }
115:
116: return coverageDesc;
117: }
118:
119: @Override
120: public Description getDescription() {
121: init();
122:
123: if (this .desc == null) {
124: desc = super .getDescription();
125:
126: desc.addChild(getCoverageDescription());
127:
128: try {
129: probeTable
130: .addProbeDescriptions(getCoverageDescription());
131: } catch (ClassNotFoundException e) {
132: throw new IllegalStateException(e);
133: }
134: }
135:
136: return desc;
137: }
138:
139: }
|