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 CopyOnWriteArrayList<Failure> failures;
032    private final AtomicLong runTime;
033    private final AtomicLong startTime;
034
035    /** Only set during deserialization process. */
036    private SerializedForm serializedForm;
037
038    public Result() {
039        count = new AtomicInteger();
040        ignoreCount = new AtomicInteger();
041        failures = new CopyOnWriteArrayList<Failure>();
042        runTime = new AtomicLong();
043        startTime = new AtomicLong();
044    }
045
046    private Result(SerializedForm serializedForm) {
047        count = serializedForm.fCount;
048        ignoreCount = serializedForm.fIgnoreCount;
049        failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
050        runTime = new AtomicLong(serializedForm.fRunTime);
051        startTime = new AtomicLong(serializedForm.fStartTime);
052    }
053
054    /**
055     * @return the number of tests run
056     */
057    public int getRunCount() {
058        return count.get();
059    }
060
061    /**
062     * @return the number of tests that failed during the run
063     */
064    public int getFailureCount() {
065        return failures.size();
066    }
067
068    /**
069     * @return the number of milliseconds it took to run the entire suite to run
070     */
071    public long getRunTime() {
072        return runTime.get();
073    }
074
075    /**
076     * @return the {@link Failure}s describing tests that failed and the problems they encountered
077     */
078    public List<Failure> getFailures() {
079        return failures;
080    }
081
082    /**
083     * @return the number of tests ignored during the run
084     */
085    public int getIgnoreCount() {
086        return ignoreCount.get();
087    }
088
089    /**
090     * @return <code>true</code> if all tests succeeded
091     */
092    public boolean wasSuccessful() {
093        return getFailureCount() == 0;
094    }
095
096    private void writeObject(ObjectOutputStream s) throws IOException {
097        SerializedForm serializedForm = new SerializedForm(this);
098        serializedForm.serialize(s);
099    }
100
101    private void readObject(ObjectInputStream s)
102            throws ClassNotFoundException, IOException {
103        serializedForm = SerializedForm.deserialize(s);
104    }
105
106    private Object readResolve()  {
107        return new Result(serializedForm);
108    }
109
110    @RunListener.ThreadSafe
111    private class Listener extends RunListener {
112        @Override
113        public void testRunStarted(Description description) throws Exception {
114            startTime.set(System.currentTimeMillis());
115        }
116
117        @Override
118        public void testRunFinished(Result result) throws Exception {
119            long endTime = System.currentTimeMillis();
120            runTime.addAndGet(endTime - startTime.get());
121        }
122
123        @Override
124        public void testFinished(Description description) throws Exception {
125            count.getAndIncrement();
126        }
127
128        @Override
129        public void testFailure(Failure failure) throws Exception {
130            failures.add(failure);
131        }
132
133        @Override
134        public void testIgnored(Description description) throws Exception {
135            ignoreCount.getAndIncrement();
136        }
137
138        @Override
139        public void testAssumptionFailure(Failure failure) {
140            // do nothing: same as passing (for 4.5; may change in 4.6)
141        }
142    }
143
144    /**
145     * Internal use only.
146     */
147    public RunListener createListener() {
148        return new Listener();
149    }
150
151    /**
152     * Represents the serialized output of {@code Result}. The fields on this
153     * class match the files that {@code Result} had in JUnit 4.11.
154     */
155    private static class SerializedForm implements Serializable {
156        private static final long serialVersionUID = 1L;
157        private final AtomicInteger fCount;
158        private final AtomicInteger fIgnoreCount;
159        private final List<Failure> fFailures;
160        private final long fRunTime;
161        private final long fStartTime;
162
163        public SerializedForm(Result result) {
164            fCount = result.count;
165            fIgnoreCount = result.ignoreCount;
166            fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
167            fRunTime = result.runTime.longValue();
168            fStartTime = result.startTime.longValue();
169        }
170
171        @SuppressWarnings("unchecked")
172        private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
173            fCount = (AtomicInteger) fields.get("fCount", null);
174            fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
175            fFailures = (List<Failure>) fields.get("fFailures", null);
176            fRunTime = fields.get("fRunTime", 0L);
177            fStartTime = fields.get("fStartTime", 0L);
178        }
179
180        public void serialize(ObjectOutputStream s) throws IOException {
181            ObjectOutputStream.PutField fields = s.putFields();
182            fields.put("fCount", fCount);
183            fields.put("fIgnoreCount", fIgnoreCount);
184            fields.put("fFailures", fFailures);
185            fields.put("fRunTime", fRunTime);
186            fields.put("fStartTime", fStartTime);
187            s.writeFields();
188        }
189
190        public static SerializedForm deserialize(ObjectInputStream s)
191                throws ClassNotFoundException, IOException {
192            ObjectInputStream.GetField fields = s.readFields();
193            return new SerializedForm(fields);
194        }
195    }
196}