Skip to content

Commit ad0ee62

Browse files
Remove TestIdentifier.SerializedForm (#4913)
This commit removes the custom serialization support for `TestIdentifier` which was added to preserve backwards compatibility which is no longer necessary for 6.0. Resolves #4882. --------- Co-authored-by: Marc Philipp <mail@marcphilipp.de>
1 parent 09012e6 commit ad0ee62

File tree

4 files changed

+57
-117
lines changed

4 files changed

+57
-117
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-RC3.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ JUnit repository on GitHub.
2121
[[release-notes-6.0.0-RC3-junit-platform-deprecations-and-breaking-changes]]
2222
==== Deprecations and Breaking Changes
2323

24-
* ❓
24+
* Change serialization of `TestIdentifier` in a backwards-incompatible way to simplify
25+
its implementation
2526

2627
[[release-notes-6.0.0-RC3-junit-platform-new-features-and-improvements]]
2728
==== New Features and Improvements

junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java

Lines changed: 11 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616
import static org.apiguardian.api.API.Status.STABLE;
1717
import static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;
1818

19-
import java.io.IOException;
20-
import java.io.ObjectInputStream;
21-
import java.io.ObjectOutputStream;
22-
import java.io.ObjectStreamClass;
23-
import java.io.ObjectStreamField;
2419
import java.io.Serial;
2520
import java.io.Serializable;
2621
import java.util.LinkedHashSet;
@@ -49,23 +44,21 @@
4944
public final class TestIdentifier implements Serializable {
5045

5146
@Serial
52-
private static final long serialVersionUID = 1L;
53-
@Serial
54-
@SuppressWarnings("UnusedVariable")
55-
private static final ObjectStreamField[] serialPersistentFields = ObjectStreamClass.lookup(
56-
SerializedForm.class).getFields();
47+
private static final long serialVersionUID = 2L;
48+
49+
private final UniqueId uniqueId;
50+
51+
private final @Nullable UniqueId parentId;
5752

58-
// These are effectively final but not technically due to late initialization when deserializing
59-
private /* final */ UniqueId uniqueId;
53+
private final String displayName;
54+
private final String legacyReportingName;
6055

61-
private /* final */ @Nullable UniqueId parentId;
56+
private final @Nullable TestSource source;
6257

63-
private /* final */ String displayName;
64-
private /* final */ String legacyReportingName;
58+
@SuppressWarnings("serial") // Declared type is Set (not Serializable); actual instances are Serializable.
59+
private final Set<TestTag> tags;
6560

66-
private /* final */ @Nullable TestSource source;
67-
private /* final */ Set<TestTag> tags;
68-
private /* final */ Type type;
61+
private final Type type;
6962

7063
/**
7164
* Factory for creating a new {@link TestIdentifier} from a {@link TestDescriptor}.
@@ -272,87 +265,4 @@ public String toString() {
272265
// @formatter:on
273266
}
274267

275-
@Serial
276-
private void writeObject(ObjectOutputStream s) throws IOException {
277-
SerializedForm serializedForm = new SerializedForm(this);
278-
serializedForm.serialize(s);
279-
}
280-
281-
@Serial
282-
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
283-
SerializedForm serializedForm = SerializedForm.deserialize(s);
284-
uniqueId = UniqueId.parse(serializedForm.uniqueId);
285-
displayName = serializedForm.displayName;
286-
source = serializedForm.source;
287-
tags = serializedForm.tags;
288-
type = serializedForm.type;
289-
String parentId = serializedForm.parentId;
290-
this.parentId = parentId == null ? null : UniqueId.parse(parentId);
291-
legacyReportingName = serializedForm.legacyReportingName;
292-
}
293-
294-
/**
295-
* Represents the serialized output of {@code TestIdentifier}. The fields on this
296-
* class match the fields that {@code TestIdentifier} had prior to 1.8.
297-
*/
298-
private static class SerializedForm implements Serializable {
299-
300-
@Serial
301-
private static final long serialVersionUID = 1L;
302-
303-
private final String uniqueId;
304-
305-
@Nullable
306-
private final String parentId;
307-
308-
private final String displayName;
309-
private final String legacyReportingName;
310-
311-
@Nullable
312-
private final TestSource source;
313-
314-
@SuppressWarnings({ "serial", "RedundantSuppression" }) // always used with serializable implementation (see TestIdentifier#copyOf())
315-
private final Set<TestTag> tags;
316-
private final Type type;
317-
318-
SerializedForm(TestIdentifier testIdentifier) {
319-
this.uniqueId = testIdentifier.uniqueId.toString();
320-
UniqueId parentId = testIdentifier.parentId;
321-
this.parentId = parentId == null ? null : parentId.toString();
322-
this.displayName = testIdentifier.displayName;
323-
this.legacyReportingName = testIdentifier.legacyReportingName;
324-
this.source = testIdentifier.source;
325-
this.tags = testIdentifier.tags;
326-
this.type = testIdentifier.type;
327-
}
328-
329-
@SuppressWarnings("unchecked")
330-
private SerializedForm(ObjectInputStream.GetField fields) throws IOException, ClassNotFoundException {
331-
this.uniqueId = (String) fields.get("uniqueId", null);
332-
this.parentId = (String) fields.get("parentId", null);
333-
this.displayName = (String) fields.get("displayName", null);
334-
this.legacyReportingName = (String) fields.get("legacyReportingName", null);
335-
this.source = (TestSource) fields.get("source", null);
336-
this.tags = (Set<TestTag>) fields.get("tags", null);
337-
this.type = (Type) fields.get("type", null);
338-
}
339-
340-
void serialize(ObjectOutputStream s) throws IOException {
341-
ObjectOutputStream.PutField fields = s.putFields();
342-
fields.put("uniqueId", uniqueId);
343-
fields.put("parentId", parentId);
344-
fields.put("displayName", displayName);
345-
fields.put("legacyReportingName", legacyReportingName);
346-
fields.put("source", source);
347-
fields.put("tags", tags);
348-
fields.put("type", type);
349-
s.writeFields();
350-
}
351-
352-
static SerializedForm deserialize(ObjectInputStream s) throws IOException, ClassNotFoundException {
353-
ObjectInputStream.GetField fields = s.readFields();
354-
return new SerializedForm(fields);
355-
}
356-
}
357-
358268
}

platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,25 @@
1010

1111
package org.junit.platform.launcher;
1212

13+
import static java.util.stream.Collectors.collectingAndThen;
14+
import static java.util.stream.Collectors.toSet;
15+
import static org.assertj.core.api.Assertions.assertThat;
1316
import static org.junit.jupiter.api.Assertions.assertEquals;
1417
import static org.junit.jupiter.api.Assertions.assertFalse;
1518
import static org.junit.jupiter.api.Assertions.assertTrue;
1619
import static org.junit.platform.commons.util.SerializationUtils.deserialize;
1720
import static org.junit.platform.commons.util.SerializationUtils.serialize;
1821

22+
import java.io.Serializable;
23+
import java.util.AbstractSet;
24+
import java.util.Iterator;
1925
import java.util.Set;
26+
import java.util.stream.IntStream;
2027

28+
import org.jspecify.annotations.NullMarked;
2129
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.params.ParameterizedTest;
31+
import org.junit.jupiter.params.provider.ValueSource;
2232
import org.junit.platform.engine.TestDescriptor;
2333
import org.junit.platform.engine.TestTag;
2434
import org.junit.platform.engine.UniqueId;
@@ -30,6 +40,7 @@
3040
/**
3141
* @since 1.0
3242
*/
43+
@NullMarked
3344
class TestIdentifierTests {
3445

3546
@Test
@@ -56,20 +67,37 @@ void inheritsTypeFromDescriptor() {
5667
assertTrue(identifier.isContainer());
5768
}
5869

59-
@Test
60-
void currentVersionCanBeSerializedAndDeserialized() throws Exception {
61-
var originalIdentifier = createOriginalTestIdentifier();
62-
var deserializedIdentifier = (TestIdentifier) deserialize(serialize(originalIdentifier));
63-
assertDeepEquals(originalIdentifier, deserializedIdentifier);
70+
@ParameterizedTest
71+
@ValueSource(ints = { 0, 1, 2 })
72+
void currentVersionCanBeSerializedAndDeserialized(int tagCount) throws Exception {
73+
var tags = IntStream.range(0, tagCount) //
74+
.mapToObj(i -> TestTag.create("tag-" + i)) //
75+
.collect(collectingAndThen(toSet(), TestIdentifierTests::unserializableSet));
76+
77+
var original = createOriginalTestIdentifier(tags);
78+
79+
byte[] bytes = serialize(original);
80+
var roundTripped = (TestIdentifier) deserialize(bytes);
81+
82+
assertDeepEquals(original, roundTripped);
83+
assertThat(original.getTags()).isInstanceOf(Serializable.class);
6484
}
6585

66-
@Test
67-
void initialVersionCanBeDeserialized() throws Exception {
68-
try (var inputStream = getClass().getResourceAsStream("/serialized-test-identifier")) {
69-
var bytes = inputStream.readAllBytes();
70-
var deserializedIdentifier = (TestIdentifier) deserialize(bytes);
71-
assertDeepEquals(createOriginalTestIdentifier(), deserializedIdentifier);
72-
}
86+
private static <T> Set<T> unserializableSet(Set<T> delegate) {
87+
var wrapper = new AbstractSet<T>() {
88+
89+
@Override
90+
public Iterator<T> iterator() {
91+
return delegate.iterator();
92+
}
93+
94+
@Override
95+
public int size() {
96+
return delegate.size();
97+
}
98+
};
99+
assertThat(wrapper).isNotInstanceOf(Serializable.class);
100+
return wrapper;
73101
}
74102

75103
@Test
@@ -100,12 +128,12 @@ private static void assertDeepEquals(TestIdentifier first, TestIdentifier second
100128
assertEquals(first.getParentIdObject(), second.getParentIdObject());
101129
}
102130

103-
private static TestIdentifier createOriginalTestIdentifier() {
131+
private static TestIdentifier createOriginalTestIdentifier(Set<TestTag> tags) {
104132
var engineDescriptor = new EngineDescriptor(UniqueId.forEngine("engine"), "Engine");
105133
var uniqueId = engineDescriptor.getUniqueId().append("child", "child");
106134
var testSource = ClassSource.from(TestIdentifierTests.class);
107-
var testDescriptor = new AbstractTestDescriptor(uniqueId, "displayName", testSource) {
108135

136+
var testDescriptor = new AbstractTestDescriptor(uniqueId, "displayName", testSource) {
109137
@Override
110138
public Type getType() {
111139
return Type.TEST;
@@ -118,9 +146,10 @@ public String getLegacyReportingName() {
118146

119147
@Override
120148
public Set<TestTag> getTags() {
121-
return Set.of(TestTag.create("aTag"));
149+
return tags;
122150
}
123151
};
152+
124153
engineDescriptor.addChild(testDescriptor);
125154
return TestIdentifier.from(testDescriptor);
126155
}
-1.05 KB
Binary file not shown.

0 commit comments

Comments
 (0)