Skip to content

Commit 37eb419

Browse files
authored
[8.0.4xx] Fix serialization of TelemetryEventArgs data packets across MSBuild worker nodes (#10464)
* Telemetry strings could be null, so handle that case
1 parent 0c86109 commit 37eb419

File tree

4 files changed

+88
-8
lines changed

4 files changed

+88
-8
lines changed

eng/Versions.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the MIT license. See License.txt in the project root for full license information. -->
33
<Project>
44
<PropertyGroup>
5-
<VersionPrefix>17.11.3</VersionPrefix><DotNetFinalVersionKind>release</DotNetFinalVersionKind>
5+
<VersionPrefix>17.11.4</VersionPrefix><DotNetFinalVersionKind>release</DotNetFinalVersionKind>
66
<PackageValidationBaselineVersion>17.10.4</PackageValidationBaselineVersion>
77
<AssemblyVersion>15.1.0.0</AssemblyVersion>
88
<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>

src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ public void PropertyInitialValueEventArgs()
873873
e => e.HelpKeyword,
874874
e => e.SenderName);
875875
}
876+
876877
[Fact]
877878
public void ReadingCorruptedStreamThrows()
878879
{

src/Framework.UnitTests/CustomEventArgSerialization_Tests.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,5 +974,82 @@ private static void VerifyTaskFinished(TaskFinishedEventArgs genericEvent, TaskF
974974
newGenericEvent.TaskFile.ShouldBe(genericEvent.TaskFile, StringCompareShould.IgnoreCase); // "Expected TaskFile to Match"
975975
newGenericEvent.TaskName.ShouldBe(genericEvent.TaskName, StringCompareShould.IgnoreCase); // "Expected TaskName to Match"
976976
}
977+
978+
979+
[Fact]
980+
public void TestTelemetryEventArgs_AllProperties()
981+
{
982+
// Test using reasonable values
983+
TelemetryEventArgs genericEvent = new TelemetryEventArgs { EventName = "Good", Properties = new Dictionary<string, string> { { "Key", "Value" } } };
984+
genericEvent.BuildEventContext = new BuildEventContext(5, 4, 3, 2);
985+
986+
TelemetryEventArgs newGenericEvent = RoundTrip(genericEvent);
987+
988+
VerifyGenericEventArg(genericEvent, newGenericEvent);
989+
VerifyTelemetryEvent(genericEvent, newGenericEvent);
990+
}
991+
992+
[Fact]
993+
public void TestTelemetryEventArgs_NullProperties()
994+
{
995+
// Test using reasonable values
996+
TelemetryEventArgs genericEvent = new TelemetryEventArgs { EventName = "Good", Properties = null };
997+
genericEvent.BuildEventContext = new BuildEventContext(5, 4, 3, 2);
998+
999+
TelemetryEventArgs newGenericEvent = RoundTrip(genericEvent);
1000+
1001+
// quirk - the properties dict is initialized to an empty dictionary by the default constructor, so it's not _really_ round-trippable.
1002+
// so we modify the source event for easier comparison here.
1003+
genericEvent.Properties = new Dictionary<string, string>();
1004+
1005+
VerifyGenericEventArg(genericEvent, newGenericEvent);
1006+
VerifyTelemetryEvent(genericEvent, newGenericEvent);
1007+
}
1008+
1009+
[Fact]
1010+
public void TestTelemetryEventArgs_NullEventName()
1011+
{
1012+
// Test using null event name
1013+
TelemetryEventArgs genericEvent = new TelemetryEventArgs { EventName = null, Properties = new Dictionary<string, string> { { "Key", "Value" } } };
1014+
genericEvent.BuildEventContext = new BuildEventContext(5, 4, 3, 2);
1015+
1016+
TelemetryEventArgs newGenericEvent = RoundTrip(genericEvent);
1017+
1018+
VerifyGenericEventArg(genericEvent, newGenericEvent);
1019+
VerifyTelemetryEvent(genericEvent, newGenericEvent);
1020+
}
1021+
1022+
[Fact]
1023+
public void TestTelemetryEventArgs_NullPropertyValue()
1024+
{
1025+
// Test using null property value name
1026+
TelemetryEventArgs genericEvent = new TelemetryEventArgs { EventName = "Good", Properties = new Dictionary<string, string> { { "Key", null } } };
1027+
genericEvent.BuildEventContext = new BuildEventContext(5, 4, 3, 2);
1028+
1029+
TelemetryEventArgs newGenericEvent = RoundTrip(genericEvent);
1030+
1031+
VerifyGenericEventArg(genericEvent, newGenericEvent);
1032+
VerifyTelemetryEvent(genericEvent, newGenericEvent);
1033+
}
1034+
1035+
private T RoundTrip<T>(T original)
1036+
where T : BuildEventArgs, new()
1037+
{
1038+
_stream.Position = 0;
1039+
original.WriteToStream(_writer);
1040+
long streamWriteEndPosition = _stream.Position;
1041+
1042+
_stream.Position = 0;
1043+
var actual = new T();
1044+
actual.CreateFromStream(_reader, _eventArgVersion);
1045+
_stream.Position.ShouldBe(streamWriteEndPosition); // "Stream End Positions Should Match"
1046+
return actual;
1047+
}
1048+
1049+
private static void VerifyTelemetryEvent(TelemetryEventArgs expected, TelemetryEventArgs actual)
1050+
{
1051+
actual.EventName.ShouldBe(expected.EventName);
1052+
actual.Properties.ShouldBe(expected.Properties);
1053+
}
9771054
}
9781055
}

src/Framework/TelemetryEventArgs.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
using System.IO;
77
using Microsoft.Build.Shared;
88

9-
#nullable disable
10-
119
namespace Microsoft.Build.Framework
1210
{
1311
/// <summary>
@@ -19,12 +17,12 @@ public sealed class TelemetryEventArgs : BuildEventArgs
1917
/// <summary>
2018
/// Gets or sets the name of the event.
2119
/// </summary>
22-
public string EventName { get; set; }
20+
public string? EventName { get; set; }
2321

2422
/// <summary>
2523
/// Gets or sets a list of properties associated with the event.
2624
/// </summary>
27-
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
25+
public IDictionary<string, string?> Properties { get; set; } = new Dictionary<string, string?>();
2826

2927
internal override void WriteToStream(BinaryWriter writer)
3028
{
@@ -34,13 +32,17 @@ internal override void WriteToStream(BinaryWriter writer)
3432
int count = Properties?.Count ?? 0;
3533
writer.Write7BitEncodedInt(count);
3634

35+
if (Properties == null)
36+
{
37+
return;
38+
}
39+
3740
foreach (var kvp in Properties)
3841
{
3942
writer.Write(kvp.Key);
40-
writer.Write(kvp.Value);
43+
writer.WriteOptionalString(kvp.Value);
4144
}
4245
}
43-
4446
internal override void CreateFromStream(BinaryReader reader, int version)
4547
{
4648
base.CreateFromStream(reader, version);
@@ -51,7 +53,7 @@ internal override void CreateFromStream(BinaryReader reader, int version)
5153
for (int i = 0; i < count; i++)
5254
{
5355
string key = reader.ReadString();
54-
string value = reader.ReadString();
56+
string? value = reader.ReadOptionalString();
5557
Properties.Add(key, value);
5658
}
5759
}

0 commit comments

Comments
 (0)