Consolidate STJ polymorphism tests into a shared abstract suite#129755
Conversation
Moves PolymorphicTests.CustomTypeHierarchies.cs and PolymorphicTests.TypeClassifier.cs from tests/System.Text.Json.Tests/Serialization/ into tests/Common/ so they run under both reflection (11 wrappers) and source-gen (4 wrappers) via the SerializerWrapper pattern. Removes the duplicate PolymorphismTests.cs in the source-gen test project and wires the consolidated suite through a new tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs. Test-only refactoring in preparation for #129041. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
Pull request overview
This PR refactors System.Text.Json polymorphism tests into a shared tests/Common abstract suite so the same scenarios can be exercised across both the reflection-based test wrappers and the source-generated wrappers.
Changes:
- Consolidates polymorphism test implementations into
src/libraries/System.Text.Json/tests/Common/PolymorphicTests*.csand links them into both test projects. - Replaces the reflection project’s
Serialization/PolymorphicTests.cswith wrapper-only derived test classes. - Removes the source-gen project’s duplicate
PolymorphismTests.csand wires the shared suite via a newSerialization/PolymorphicTests.cswith source-gen contexts.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj | Links the consolidated Common/PolymorphicTests*.cs into the reflection test project and removes project-local duplicates. |
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs | Leaves only wrapper-specific derived classes that run the shared abstract suite. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets | Links the consolidated Common/PolymorphicTests*.cs into the source-gen test project and swaps in the new Serialization/PolymorphicTests.cs. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs | Adds source-gen wrappers and contexts to execute the shared polymorphism suite under metadata/default source-gen modes. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphismTests.cs | Removes now-redundant source-gen-only polymorphism tests. |
| src/libraries/System.Text.Json/tests/Common/SerializerTests.cs | Makes GenericPoco<T> internal to support broader reuse from the consolidated suites. |
| src/libraries/System.Text.Json/tests/Common/PolymorphicTests.cs | New shared base suite (previously reflection-only) that now runs under both wrapper families. |
| src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs | Adjusts model accessibility and adds reflection-only guards for scenarios not supported/representable in source-gen. |
| src/libraries/System.Text.Json/tests/Common/PolymorphicTests.TypeClassifier.cs | Expands shared classifier/polymorphism-option parity coverage and uses wrapper helpers for option creation/modification. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…e polymorphic configs; drop transient region marker
- Convert 11 'if (Serializer.IsSourceGeneratedSerializer) return;' guards
to 'throw new SkipTestException(...)' so xunit reports them as Skipped
rather than silently Passed. Decorate the affected tests with
[ConditionalFact] so the runtime SkipTestException is honored.
- Remove the '#region Generation parity (folded from source-gen suite)'
marker in PolymorphicTests.TypeClassifier.cs; git blame retains the
history.
Verified:
- Reflection (11 wrappers): 6039 total, 0 failed, 0 skipped.
- Source-gen (4 wrappers): 2196 total, 0 failed, 40 skipped
(10 guards x 4 wrappers).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
The JsonNode context branch was calling JsonValue.Create<TValue>(value) which internally uses JsonSerializerOptions.Default. In test projects with JsonSerializerIsReflectionEnabledByDefault=false (i.e. the SG tests), that resolver is EmptyJsonTypeInfoResolver and fails for any non-primitive TValue. This latent bug was exposed by moving the polymorphic tests into Common, where they now run under source-gen wrappers. Resolve the TypeInfo via the wrapper (which uses the SG context's options when available) and pass it explicitly to JsonValue.Create. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/libraries/System.Text.Json/tests/Common/SerializerTests.cs:206
- In the JsonNode branch, JsonTypeInfo/JsonValue are created before the expected-exception check. If GetTypeInfo(...) or JsonValue.Create(...) throws (which is likely for scenarios where expectedExceptionType != null), the exception escapes the test helper and won't be validated by Assert.ThrowsAsync, causing spurious test failures.
Move the typeInfo/jsonObject construction inside the ThrowsAsync lambda for the expected-exception case (and keep the current fast path for the success case).
if (contexts.HasFlag(SerializedValueContext.JsonNode))
{
const string key = "key";
JsonTypeInfo<TValue> typeInfo = Serializer.GetTypeInfo<TValue>(options);
var jsonObject = new JsonObject { [key] = JsonValue.Create(value, typeInfo) };
if (expectedExceptionType != null)
{
await Assert.ThrowsAsync(expectedExceptionType, () => Serializer.SerializeWrapper(jsonObject, options));
}
Copilot Code ReviewHolistic AssessmentMotivation: Justified — consolidating polymorphism tests into the shared Approach: Sound. Follows the established Summary: ✅ LGTM. Clean, mechanical test-only refactoring with no production code changes. The 4 tests from the deleted Detailed FindingsDetailed Findings✅ Test absorption — All deleted tests properly migratedAll 4 tests from the old ✅ CustomPolymorphismResolver refactoring — Correct composition patternChanging from ✅ SerializerTests.cs changes — Correct API adaptation
✅ Source-gen skip guards — Properly implementedAll 11 ✅ Helper method refactoring — Consistent use of Serializer wrapper
✅ Context registrations — ComprehensiveThe Note This review was generated by GitHub Copilot.
|
…lymorphicTests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route migrated static JsonSerializer.* calls through the Serializer wrapper abstraction so the source-gen resolver is injected, convert the affected [Fact] void tests to async Task, and register the newly needed types (UsaCustomer, MyClass, MyThingCollection, MyThingDictionary) in both source-gen contexts. ReadPrimitivesFail now uses ThrowsAnyAsync<JsonException> since the Element/Document/Node round-trip wrappers surface the internal JsonReaderException subclass. Fixes 276 source-gen CI failures that threw InvalidOperationException (reflection disabled). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I think this change might break nativeaot-outerloop: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1480580&view=ms.vss-test-web.build-test-results-tab |
|
I'm actually surprised to see System.Text.Json.SourceGeneration tests being run using Native AOT. At least in the past some of them were using unsupported reflection (e.g. because the source generator outputs were being compared against the reflection baseline). |
|
ObjectConverterFactory looks to be marked RequiresDynamicCode. It would be nice if the source generated tests could stick to source generated code only. We have means to enforce this statically, but I had to disable it for the source generator tests because they still have tons of trim/AOT safety warnings: |
See my earlier comment
I think that's generally a desirable trait for source generator tests. If we want to run tests using AOT we should consider using a representative subset using smoke testing. |
These tests regularly find some obscure stress issue. Just last week: #129010 We have an active issue too: runtime/src/libraries/tests.proj Lines 385 to 387 in 818cfd5 The tests are so huge they're running into Apple linker bugs. |
|
(The stress issues are also representative of real customer code. I've seen not one, not two,... more customer apps where System.Text.Json serialization code is more than half of their app.) |
|
I don't think we can ever avoid the source gen tests having a huge footprint. They generally need to able to test every conceivable type shape under the sun, so thousands upon thousands of POCOs. We can have a conversation about customer apps being large, but I think that's largely a distinct issue. |
|
If you want this test suite to be a source of stress testing for the AOT toolchain, that's fine, but it's clear there exist conflicting goals here. Perhaps having a stress suite that generates synthetic types might be a good alternative? |
The original motivation was things like #73431 (comment) I thought you were on board with that when you did the work to make these compatible in the first place: #86975. I would be personally happy if we stopped running these because it's a huge pain to troubleshoot any bugs this finds due to the sheer size of the tests and it taking 7+ minutes to AOT compile. However this did find many product bugs that we didn't see anywhere else. I can't think of any other test that found so many issues (here's another from the past 6 months or so: #122582). So I'm conflicted. It would be good for me. But it would be worse for the product and customers. |
Test-only refactoring to expand test coverage ahead of #129041 being implemented.
Moves
PolymorphicTests.CustomTypeHierarchies.csandPolymorphicTests.TypeClassifier.csfromSystem.Text.Json.Tests/Serialization/intotests/Common/so they run under both reflection (11 wrappers) and source-gen (4 wrappers) via theSerializerWrapperpattern, removes the duplicatePolymorphismTests.csin the source-gen test project, and wires the consolidated suite through a new context file underSystem.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs.The test-only nature should make this straightforward to review.