001package org.junit.runner;
002
003import java.io.IOException;
004import java.io.ObjectInputStream;
005import java.io.ObjectOutputStream;
006import java.io.ObjectStreamClass;
007import java.io.ObjectStreamField;
008import java.io.Serializable;
009import java.util.ArrayList;
010import java.util.Collections;
011import java.util.List;
012import java.util.concurrent.CopyOnWriteArrayList;
013import java.util.concurrent.atomic.AtomicInteger;
014import java.util.concurrent.atomic.AtomicLong;
015
016import org.junit.runner.notification.Failure;
017import org.junit.runner.notification.RunListener;
018
019/**
020 * A <code>Result</code> collects and summarizes information from running multiple tests.
021 * All tests are counted -- additional information is collected from tests that fail.
022 *
023 * @since 4.0
024 */
025public class Result implements Serializable {
026    private static final long serialVersionUID = 1L;
027    private static final ObjectStreamField[] serialPersistentFields =
028            ObjectStreamClass.lookup(SerializedForm.class).getFields();
029    private final AtomicInteger count;
030    private final AtomicInteger ignoreCount;
031    private final AtomicInteger assumptionFailureCount;
032    private final CopyOnWriteArrayList<Failure> failures;
033    private final AtomicLong runTime;
034    private final AtomicLong startTime;
035
036    /** Only set during deserialization process. */
037    private SerializedForm serializedForm;
038
039    public Result() {
040        count = new AtomicInteger();
041        ignoreCount = new AtomicInteger();
042        assumptionFailureCount = new AtomicInteger();
043        failures = new CopyOnWriteArrayList<Failure>();
044        runTime = new AtomicLong();
045        startTime = new AtomicLong();
046    }
047
048    private Result(SerializedForm serializedForm) {
049        count = serializedForm.fCount;
050        ignoreCount = serializedForm.fIgnoreCount;
051        assumptionFailureCount = serializedForm.assumptionFailureCount;
052        failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
053        runTime = new AtomicLong(serializedForm.fRunTime);
054        startTime = new AtomicLong(serializedForm.fStartTime);
055    }
056
057    /**
058     * Returns the number of tests run
059     */
060    public int getRunCount() {
061        return count.get();
062    }
063
064    /**
065     * Returns the number of tests that failed during the run
066     */
067    public int getFailureCount() {
068        return failures.size();
069    }
070
071    /**
072     * Returns the number of milliseconds it took to run the entire suite to run
073     */
074    public long getRunTime() {
075        return runTime.get();
076    }
077
078    /**
079     * Returns the {@link Failure}s describing tests that failed and the problems they encountered
080     */
081    public List<Failure> getFailures() {
082        return failures;
083    }
084
085    /**
086     * @return the number of tests ignored during the run
087     */
088    public int getIgnoreCount() {
089        return ignoreCount.get();
090    }
091
092    /**
093     * Returns the number of tests skipped because of an assumption failure
094     *
095     * @throws UnsupportedOperationException if the result was serialized in a version before JUnit 4.13
096     * @since 4.13
097     */
098    public int getAssumptionFailureCount() {
099        if (assumptionFailureCount == null) {
100            throw new UnsupportedOperationException(
101                    "Result was serialized from a version of JUnit that doesn't support this method");
102        }
103        return assumptionFailureCount.get();
104    }
105
106    /**
107     * @return <code>true</code> if all tests succeeded
108     */
109    public boolean wasSuccessful() {
110        return getFailureCount() == 0;
111    }
112
113    private void writeObject(ObjectOutputStream s) throws IOException {
114        SerializedForm serializedForm = new SerializedForm(this);
115        serializedForm.serialize(s);
116    }
117
118    private void readObject(ObjectInputStream s)
119            throws ClassNotFoundException, IOException {
120        serializedForm = SerializedForm.deserialize(s);
121    }
122
123    private Object readResolve()  {
124        return new Result(serializedForm);
125    }
126
127    @RunListener.ThreadSafe
128    private class Listener extends RunListener {
129        @Override
130        public void testRunStarted(Description description) throws Exception {
131            startTime.set(System.currentTimeMillis());
132        }
133
134        @Override
135        public void testRunFinished(Result result) throws Exception {
136            long endTime = System.currentTimeMillis();
137            runTime.addAndGet(endTime - startTime.get());
138        }
139
140        @Override
141        public void testFinished(Description description) throws Exception {
142            count.getAndIncrement();
143        }
144
145        @Override
146        public void testFailure(Failure failure) throws Exception {
147            failures.add(failure);
148        }
149
150        @Override
151        public void testIgnored(Description description) throws Exception {
152            ignoreCount.getAndIncrement();
153        }
154
155        @Override
156        public void testAssumptionFailure(Failure failure) {
157            assumptionFailureCount.getAndIncrement();
158        }
159    }
160
161    /**
162     * Internal use only.
163     */
164    public RunListener createListener() {
165        return new Listener();
166    }
167
168    /**
169     * Represents the serialized output of {@code Result}. The fields on this
170     * class match the files that {@code Result} had in JUnit 4.11.
171     */
172    private static class SerializedForm implements Serializable {
173        private static final long serialVersionUID = 1L;
174        private final AtomicInteger fCount;
175        private final AtomicInteger fIgnoreCount;
176        private final AtomicInteger assumptionFailureCount;
177        private final List<Failure> fFailures;
178        private final long fRunTime;
179        private final long fStartTime;
180
181        public SerializedForm(Result result) {
182            fCount = result.count;
183            fIgnoreCount = result.ignoreCount;
184            assumptionFailureCount = result.assumptionFailureCount;
185            fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
186            fRunTime = result.runTime.longValue();
187            fStartTime = result.startTime.longValue();
188        }
189
190        @SuppressWarnings("unchecked")
191        private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
192            fCount = (AtomicInteger) fields.get("fCount", null);
193            fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
194            assumptionFailureCount = (AtomicInteger) fields.get("assumptionFailureCount", null);
195            fFailures = (List<Failure>) fields.get("fFailures", null);
196            fRunTime = fields.get("fRunTime", 0L);
197            fStartTime = fields.get("fStartTime", 0L);
198        }
199
200        public void serialize(ObjectOutputStream s) throws IOException {
201            ObjectOutputStream.PutField fields = s.putFields();
202            fields.put("fCount", fCount);
203            fields.put("fIgnoreCount", fIgnoreCount);
204            fields.put("fFailures", fFailures);
205            fields.put("fRunTime", fRunTime);
206            fields.put("fStartTime", fStartTime);
207            fields.put("assumptionFailureCount", assumptionFailureCount);
208            s.writeFields();
209        }
210
211        public static SerializedForm deserialize(ObjectInputStream s)
212                throws ClassNotFoundException, IOException {
213            ObjectInputStream.GetField fields = s.readFields();
214            return new SerializedForm(fields);
215        }
216    }
217}