Commit 5226f3c
Make TranslationLayer Native AOT-compatible (#16045)
* Make TranslationLayer NativeAOT-compatible
The `Microsoft.TestPlatform.VsTestConsole.TranslationLayer` and
`Microsoft.TestPlatform.CommunicationUtilities` assemblies are loaded
in-process by NativeAOT consumers (e.g. C# Dev Kit). NativeAOT disables
reflection-based `System.Text.Json` serialization by default, causing
the vstest wire protocol to fail silently during the TCP handshake and
test discovery.
## Changes
### Source-generated `JsonSerializerContext`
Add `TestPlatformJsonContext` with `[JsonSerializable]` attributes for
every type that crosses the wire: payload DTOs, envelope DTOs, collection
types, and `PayloadedMessage<T>` for each concrete `T` used by
`VsTestConsoleRequestSender`. Set `TypeInfoResolver` on the base
`JsonSerializerOptions` so STJ has compile-time metadata available
without runtime reflection.
### AOT-safe serialization converters
- **`TestObjectBaseConverterFactory`**: replace `MakeGenericType` +
`Activator.CreateInstance` with a singleton non-generic converter.
- **`ObjectConverter.Write`**, **`ObjectDictionaryConverter.Write`**,
**`TestObjectConverter.Write`**, **`TestCaseConverterV2.Write`**,
**`TestResultConverterV2.Write`**: replace
`JsonSerializer.Serialize(writer, value, value.GetType(), options)`
with direct primitive writes via a centralized `WritePropertyValue()`
helper that handles string, int, long, double, float, bool,
DateTimeOffset, DateTime, Guid, Uri, with `ToString()` fallback.
### Envelope DTO fixes
- Change `MessageEnvelope.Payload` and `VersionedMessageEnvelope.Payload`
from `object?` to `JsonElement?` so pre-serialized payloads are embedded
as nested JSON objects rather than double-encoded strings.
- Make `MessageEnvelope`, `VersionedMessageEnvelope`,
`VersionedMessageForSerialization`, and `PayloadedMessage<T>`
`internal` (were `private`) so the source generator can reference them.
- Use `JsonSerializer.SerializeToElement(payload, payload.GetType(), options)`
to serialize payloads with runtime type dispatch before embedding in envelopes.
### AOT/trim analyzers enabled
Set `IsAotCompatible=true`, `EnableTrimAnalyzer=true`, and
`EnableAotAnalyzer=true` on the `net8.0` TFM for both
`TranslationLayer` and `CommunicationUtilities` projects. Both build
with zero IL2xxx/IL3xxx warnings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix AoT compatibility issues in serialization layer
- Chain source-gen context with DefaultJsonTypeInfoResolver so types not
covered by the source-gen context fall back to reflection in non-AoT
builds, while NativeAOT consumers use the source-gen metadata.
- Pass JsonSerializerOptions through WritePropertyValue so the default
case uses the resolver chain instead of bare reflection.
- Add ExceptionConverter to handle Exception de/serialization properly
since source-gen uses the parameterless constructor which cannot
populate the read-only Message/StackTrace properties.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix TestObjectBaseConverter to instantiate the requested type
The converter was always creating a TestCase regardless of typeToConvert,
which caused InvalidCastException when deserializing other TestObject
subclasses. Now uses Activator.CreateInstance(typeToConvert) for concrete
types, falling back to TestCase for abstract types.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Suppress IL2026/IL3050 AoT warnings via StjSafe wrapper
All JsonSerializer.Serialize/Deserialize calls in converters and
JsonDataSerializer now go through StjSafe, which centralizes the
[UnconditionalSuppressMessage] attributes. This prevents the NativeAOT
linker in consuming projects (e.g., C# DevKit) from emitting IL2026
trimming warnings for these call sites.
The suppressions are safe because all JsonSerializerOptions instances
are configured with TestPlatformJsonContext (source-gen) as the primary
TypeInfoResolver.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback
- ExceptionConverter: restore HResult and Source in Read (writable
properties), document type-erasure and StackTrace limitations.
- WritePropertyValue: add missing primitive cases for short, ushort,
uint, ulong, byte, sbyte, decimal, char, and enums to avoid falling
through to SerializeToElement for types not in the source-gen context.
- TestObjectBaseConverter: revert Activator.CreateInstance to new
TestCase() to avoid reflection incompatible with NativeAOT; document
that this path only handles generic property bags on the wire.
- SerializePayloadCore: document why the fast path was intentionally
collapsed (object? Payload is incompatible with AoT).
- TestPlatformJsonContext: add maintenance checklist for new payload
types, clarify why Dictionary<string,object> entries are needed
alongside the custom converters.
- StjSafe: add DEBUG assert verifying options.TypeInfoResolver is
configured, to catch misuse as the codebase evolves.
- Update TestObjectConverterTests to deserialize as TestObject (not
TestableTestObject) and verify custom properties by ID rather than
counting all properties (TestCase carrier has built-in properties).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Preserve exception type name and stack trace via RemoteException
Introduce RemoteException that carries the original ClassName and
StackTraceString from the remote process. ToString() renders the full
original diagnostic output (type name, message, stack trace, inner
exception chain) so that callers see the same information they would
from the original exception.
The ExceptionConverter Write path also handles round-tripping: if the
exception is already a RemoteException, it preserves the stored
ClassName and RemoteStackTrace rather than emitting the wrapper type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Collapse duplicate SerializePayloadCore branches
The fast-path branch was already doing the same thing as the slow-path
after the AoT changes (both serialize to JsonElement then embed in
envelope). Merge into a single code path and remove the now-unused
DisableFastJson field and Utilities using.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add NativeAOT compatibility integration test
Adds a test asset (NativeAotTranslationLayerConsumer) that is a minimal
console app referencing the TranslationLayer with PublishAot=true.
The NativeAotCompatibilityTests test publishes this app with NativeAOT
and asserts that no IL2026/IL3050 linker warnings originate from the
CommunicationUtilities.Serialization namespace. Pre-existing warnings
from Jsonite, ObjectModel, and DefaultJsonTypeInfoResolver are excluded.
Marked with [TestCategory("Compatibility")] so it doesn't run in
every PR build (NativeAOT publish takes ~4 minutes).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address second round of PR review feedback
- Narrow TestObjectBaseConverter.CanConvert to only typeof(TestObject),
not all TestObject subtypes. TestCase/TestResult have their own
converters and no other subtypes flow through the wire protocol.
- Remove Enum special case from WritePropertyValue — let enums fall
through to SerializeToElement which respects JsonSerializerOptions
(avoids ulong overflow and bypassing custom enum converters).
- Test asset: remove unused usings, touch VsTestConsoleWrapper type and
payload types so the linker analyzes the full TranslationLayer graph.
- Tests: use Assert.IsNotNull before TestProperty.Find instead of
null-forgiving operator.
- Tests: serialize as TestObject (declared wire type) not TestableTestObject.
- Integration test: add second WaitForExit() call to drain async output.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Jsonite deserialization of abstract TestObject type
The Jsonite ConvertTo method excluded typeof(TestObject) from the
DeserializeTestObject handler, causing it to fall through to the
generic CreateInstance path which throws MemberAccessException for
abstract types.
Fix by removing the exclusion and using TestCase as a concrete
property-bag carrier when the target type is abstract, matching
the approach used in the STJ TestObjectBaseConverter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add round-trip test for TestObject serialization
Verifies that custom properties survive a serialize-as-concrete-subtype
then deserialize-as-abstract-TestObject round trip. Covers both the STJ
path (net11.0) and Jsonite path (net481).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add explicit WritePropertyValue cases for enums, collections, and dictionaries
Reduces reliance on the StjSafe.SerializeToElement fallback which requires
runtime type metadata that may not be available under NativeAOT. All known
property value types used in the wire protocol now have explicit handlers:
- Enum (as numeric value)
- TimeSpan (as string)
- string[] (as JSON string array)
- KeyValuePair<string, string>[] (as JSON array of Key/Value objects)
- IDictionary (as JSON object)
- IEnumerable (as JSON array)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 78539ec commit 5226f3c
24 files changed
Lines changed: 802 additions & 107 deletions
File tree
- src
- Microsoft.TestPlatform.CommunicationUtilities
- Serialization
- Microsoft.TestPlatform.VsTestConsole.TranslationLayer
- test
- Microsoft.TestPlatform.CommunicationUtilities.UnitTests
- Serialization
- TestAssets/NativeAotTranslationLayerConsumer
Lines changed: 31 additions & 29 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | | - | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | | - | |
22 | | - | |
23 | 22 | | |
24 | 23 | | |
25 | | - | |
| 24 | + | |
26 | 25 | | |
27 | 26 | | |
28 | 27 | | |
| |||
41 | 40 | | |
42 | 41 | | |
43 | 42 | | |
| 43 | + | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| 65 | + | |
65 | 66 | | |
66 | 67 | | |
67 | 68 | | |
| |||
74 | 75 | | |
75 | 76 | | |
76 | 77 | | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
77 | 83 | | |
78 | 84 | | |
79 | 85 | | |
| |||
105 | 111 | | |
106 | 112 | | |
107 | 113 | | |
108 | | - | |
| 114 | + | |
109 | 115 | | |
110 | 116 | | |
111 | 117 | | |
| |||
180 | 186 | | |
181 | 187 | | |
182 | 188 | | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
183 | 192 | | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | 193 | | |
191 | | - | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
192 | 199 | | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
199 | | - | |
200 | | - | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
201 | 203 | | |
202 | 204 | | |
203 | 205 | | |
| |||
208 | 210 | | |
209 | 211 | | |
210 | 212 | | |
211 | | - | |
| 213 | + | |
212 | 214 | | |
213 | 215 | | |
214 | 216 | | |
| |||
220 | 222 | | |
221 | 223 | | |
222 | 224 | | |
223 | | - | |
| 225 | + | |
224 | 226 | | |
225 | 227 | | |
226 | 228 | | |
| |||
232 | 234 | | |
233 | 235 | | |
234 | 236 | | |
235 | | - | |
| 237 | + | |
236 | 238 | | |
237 | 239 | | |
238 | 240 | | |
| |||
258 | 260 | | |
259 | 261 | | |
260 | 262 | | |
261 | | - | |
| 263 | + | |
262 | 264 | | |
263 | 265 | | |
264 | 266 | | |
| |||
267 | 269 | | |
268 | 270 | | |
269 | 271 | | |
270 | | - | |
| 272 | + | |
271 | 273 | | |
272 | 274 | | |
273 | 275 | | |
274 | 276 | | |
275 | | - | |
| 277 | + | |
276 | 278 | | |
277 | 279 | | |
278 | 280 | | |
279 | 281 | | |
280 | 282 | | |
281 | 283 | | |
282 | | - | |
| 284 | + | |
283 | 285 | | |
284 | 286 | | |
285 | 287 | | |
286 | 288 | | |
287 | 289 | | |
288 | | - | |
| 290 | + | |
289 | 291 | | |
290 | 292 | | |
291 | 293 | | |
292 | 294 | | |
293 | 295 | | |
294 | | - | |
| 296 | + | |
295 | 297 | | |
296 | 298 | | |
297 | 299 | | |
| |||
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
8 | 18 | | |
9 | 19 | | |
10 | 20 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
| 55 | + | |
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | | - | |
| 78 | + | |
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
87 | | - | |
| 87 | + | |
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
| |||
Lines changed: 150 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
0 commit comments