Skip to content

Commit

Permalink
Make serialization of Result compatible with 4.11
Browse files Browse the repository at this point in the history
  • Loading branch information
kcooney committed Sep 30, 2014
1 parent 597977e commit f6149a9
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 9 deletions.
106 changes: 97 additions & 9 deletions src/main/java/org/junit/runner/Result.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,55 @@
package org.junit.runner;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;

/**
* A <code>Result</code> collects and summarizes information from running multiple tests.
* All tests are counted -- additional information is collected from tests that fail.
*
* @since 4.0
*/
public class Result implements Serializable {
private static final long serialVersionUID = 2L;
private final AtomicInteger count = new AtomicInteger();
private final AtomicInteger ignoreCount = new AtomicInteger();
private final CopyOnWriteArrayList<Failure> failures = new CopyOnWriteArrayList<Failure>();
private final AtomicLong runTime = new AtomicLong();
private final AtomicLong startTime = new AtomicLong();
private static final long serialVersionUID = 1L;
private static final ObjectStreamField[] serialPersistentFields =
ObjectStreamClass.lookup(SerializedForm.class).getFields();
private final AtomicInteger count;
private final AtomicInteger ignoreCount;
private final CopyOnWriteArrayList<Failure> failures;
private final AtomicLong runTime;
private final AtomicLong startTime;

/** Only set during deserialization process. */
private SerializedForm serializedForm;

public Result() {
count = new AtomicInteger();
ignoreCount = new AtomicInteger();
failures = new CopyOnWriteArrayList<Failure>();
runTime = new AtomicLong();
startTime = new AtomicLong();
}

private Result(SerializedForm serializedForm) {
count = serializedForm.fCount;
ignoreCount = serializedForm.fIgnoreCount;
failures = new CopyOnWriteArrayList<Failure>(serializedForm.fFailures);
runTime = new AtomicLong(serializedForm.fRunTime);
startTime = new AtomicLong(serializedForm.fStartTime);
}

/**
* @return the number of tests run
Expand Down Expand Up @@ -65,6 +93,20 @@ public boolean wasSuccessful() {
return getFailureCount() == 0;
}

private void writeObject(ObjectOutputStream s) throws IOException {
SerializedForm serializedForm = new SerializedForm(this);
serializedForm.serialize(s);
}

private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
serializedForm = SerializedForm.deserialize(s);
}

private Object readResolve() {
return new Result(serializedForm);
}

@RunListener.ThreadSafe
private class Listener extends RunListener {
@Override
Expand Down Expand Up @@ -105,4 +147,50 @@ public void testAssumptionFailure(Failure failure) {
public RunListener createListener() {
return new Listener();
}

/**
* Represents the serialized output of {@code Result}. The fields on this
* class match the files that {@code Result} had in JUnit 4.11.
*/
private static class SerializedForm implements Serializable {
private static final long serialVersionUID = 1L;
private final AtomicInteger fCount;
private final AtomicInteger fIgnoreCount;
private final List<Failure> fFailures;
private final long fRunTime;
private final long fStartTime;

public SerializedForm(Result result) {
fCount = result.count;
fIgnoreCount = result.ignoreCount;
fFailures = Collections.synchronizedList(new ArrayList<Failure>(result.failures));
fRunTime = result.runTime.longValue();
fStartTime = result.startTime.longValue();
}

@SuppressWarnings("unchecked")
private SerializedForm(ObjectInputStream.GetField fields) throws IOException {
fCount = (AtomicInteger) fields.get("fCount", null);
fIgnoreCount = (AtomicInteger) fields.get("fIgnoreCount", null);
fFailures = (List<Failure>) fields.get("fFailures", null);
fRunTime = fields.get("fRunTime", 0L);
fStartTime = fields.get("fStartTime", 0L);
}

public void serialize(ObjectOutputStream s) throws IOException {
ObjectOutputStream.PutField fields = s.putFields();
fields.put("fCount", fCount);
fields.put("fIgnoreCount", fIgnoreCount);
fields.put("fFailures", fFailures);
fields.put("fRunTime", fRunTime);
fields.put("fStartTime", fStartTime);
s.writeFields();
}

public static SerializedForm deserialize(ObjectInputStream s)
throws ClassNotFoundException, IOException {
ObjectInputStream.GetField fields = s.readFields();
return new SerializedForm(fields);
}
}
}
70 changes: 70 additions & 0 deletions src/test/java/junit/tests/runner/ResultTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

import junit.framework.TestCase;
import junit.tests.framework.Success;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.tests.running.methods.AnnotationTest;

public class ResultTest extends TestCase {
Expand All @@ -32,6 +35,73 @@ private void assertResultSerializable(Result result) throws IOException, ClassNo
byte[] bytes = byteArrayOutputStream.toByteArray();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
Result fromStream = (Result) objectInputStream.readObject();
assertSerializedCorrectly(result, fromStream);

InputStream resource = getClass().getResourceAsStream(getName());
assertNotNull("Could not read resource " + getName(), resource);
objectInputStream = new ObjectInputStream(resource);
fromStream = (Result) objectInputStream.readObject();

assertSerializedCorrectly(new ResultWithFixedRunTime(result), fromStream);
}

/**
* A version of {@code Result} that returns a hard-coded runtime.
* This makes values returned by the methods deterministic.
*/
private static class ResultWithFixedRunTime extends Result {
private final Result delegate;

public ResultWithFixedRunTime(Result delegate) {
this.delegate = delegate;
}

@Override
public int getRunCount() {
return delegate.getRunCount();
}

@Override
public int getFailureCount() {
return delegate.getFailureCount();
}

@Override
public long getRunTime() {
return 2;
}

@Override
public List<Failure> getFailures() {
return delegate.getFailures();
}

@Override
public int getIgnoreCount() {
return delegate.getIgnoreCount();
}
}

private void assertSerializedCorrectly(Result result, Result fromStream) {
assertNotNull(fromStream);

// Exceptions don't implement equals() so we need to compare field by field
assertEquals("failureCount", result.getFailureCount(), fromStream.getFailureCount());
assertEquals("ignoreCount", result.getIgnoreCount(), fromStream.getIgnoreCount());
assertEquals("runTime", result.getRunTime(), fromStream.getRunTime());
assertEquals("failures", result.getFailures().size(), fromStream.getFailures().size());
int index = 0;
for (Failure failure : result.getFailures()) {
Failure failureFromStream = fromStream.getFailures().get(index);
String messagePrefix = String.format("failures[%d]", index++);
assertEquals(messagePrefix + ".description",
failure.getDescription(), failureFromStream.getDescription());
Throwable exception = failure.getException();
Throwable exceptionFromStream = failureFromStream.getException();
assertEquals(messagePrefix + ".exception",
exception.getClass(), exceptionFromStream.getClass());
assertEquals(messagePrefix + ".exception",
exception.getMessage(), exceptionFromStream.getMessage());
}
}
}
Binary file not shown.
Binary file not shown.

0 comments on commit f6149a9

Please sign in to comment.