001    package org.junit.runner;
002    
003    import java.io.IOException;
004    import java.io.ObjectInputStream;
005    import java.io.ObjectOutputStream;
006    import java.io.ObjectStreamClass;
007    import java.io.ObjectStreamField;
008    import java.io.Serializable;
009    import java.util.ArrayList;
010    import java.util.Collections;
011    import java.util.List;
012    import java.util.concurrent.CopyOnWriteArrayList;
013    import java.util.concurrent.atomic.AtomicInteger;
014    import java.util.concurrent.atomic.AtomicLong;
015    
016    import org.junit.runner.notification.Failure;
017    import 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     */
025    public 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    }