-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Track subclasses of MSBuild tasks for telemetry #12623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
58ceed6
Initial plan
Copilot 46fe6a3
Add telemetry collection for Microsoft-authored tasks
Copilot e57c5f7
Add integration test and fix warnings in ProjectTelemetry tests
Copilot 48f5560
Change telemetry to track non-sealed subclasses of Microsoft MSBuild …
Copilot 19baac7
Remove sealed check - track all subclasses of Microsoft tasks
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using Microsoft.Build.BackEnd.Logging; | ||
| using Microsoft.Build.Evaluation; | ||
| using Microsoft.Build.Framework; | ||
| using Microsoft.Build.Utilities; | ||
| using Shouldly; | ||
| using Xunit; | ||
|
|
||
| namespace Microsoft.Build.UnitTests.BackEnd | ||
| { | ||
| /// <summary> | ||
| /// Tests for ProjectTelemetry class | ||
| /// </summary> | ||
| public class ProjectTelemetry_Tests | ||
| { | ||
| /// <summary> | ||
| /// Test that TrackTaskSubclassing tracks sealed tasks that derive from Microsoft tasks | ||
| /// </summary> | ||
| [Fact] | ||
| public void TrackTaskSubclassing_TracksSealedTasks() | ||
| { | ||
| var telemetry = new ProjectTelemetry(); | ||
|
|
||
| // Sealed task should be tracked if it derives from Microsoft task | ||
| telemetry.TrackTaskSubclassing(typeof(TestSealedTask), isMicrosoftOwned: false); | ||
|
|
||
| var properties = GetMSBuildTaskSubclassProperties(telemetry); | ||
|
|
||
| // Should track sealed tasks that inherit from Microsoft tasks | ||
| properties.Count.ShouldBe(1); | ||
| properties.ShouldContainKey("Microsoft_Build_Utilities_Task"); | ||
| properties["Microsoft_Build_Utilities_Task"].ShouldBe("1"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that TrackTaskSubclassing tracks subclasses of Microsoft tasks | ||
| /// </summary> | ||
| [Fact] | ||
| public void TrackTaskSubclassing_TracksSubclass() | ||
| { | ||
| var telemetry = new ProjectTelemetry(); | ||
|
|
||
| // User task inheriting from Microsoft.Build.Utilities.Task | ||
| telemetry.TrackTaskSubclassing(typeof(UserTask), isMicrosoftOwned: false); | ||
|
|
||
| var properties = GetMSBuildTaskSubclassProperties(telemetry); | ||
|
|
||
| // Should track the Microsoft.Build.Utilities.Task base class | ||
| properties.Count.ShouldBe(1); | ||
| properties.ShouldContainKey("Microsoft_Build_Utilities_Task"); | ||
| properties["Microsoft_Build_Utilities_Task"].ShouldBe("1"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that TrackTaskSubclassing does not track Microsoft-owned tasks | ||
| /// </summary> | ||
| [Fact] | ||
| public void TrackTaskSubclassing_IgnoresMicrosoftOwnedTasks() | ||
| { | ||
| var telemetry = new ProjectTelemetry(); | ||
|
|
||
| // Microsoft-owned task should not be tracked even if non-sealed | ||
| telemetry.TrackTaskSubclassing(typeof(UserTask), isMicrosoftOwned: true); | ||
|
|
||
| var properties = GetMSBuildTaskSubclassProperties(telemetry); | ||
|
|
||
| // Should not track Microsoft-owned tasks | ||
| properties.Count.ShouldBe(0); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that TrackTaskSubclassing tracks multiple subclasses | ||
| /// </summary> | ||
| [Fact] | ||
| public void TrackTaskSubclassing_TracksMultipleSubclasses() | ||
| { | ||
| var telemetry = new ProjectTelemetry(); | ||
|
|
||
| // Track multiple user tasks | ||
| telemetry.TrackTaskSubclassing(typeof(UserTask), isMicrosoftOwned: false); | ||
| telemetry.TrackTaskSubclassing(typeof(AnotherUserTask), isMicrosoftOwned: false); | ||
|
|
||
| var properties = GetMSBuildTaskSubclassProperties(telemetry); | ||
|
|
||
| // Should aggregate counts for the same base class | ||
| properties.Count.ShouldBe(1); | ||
| properties["Microsoft_Build_Utilities_Task"].ShouldBe("2"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Test that TrackTaskSubclassing handles null gracefully | ||
| /// </summary> | ||
| [Fact] | ||
| public void TrackTaskSubclassing_HandlesNull() | ||
| { | ||
| var telemetry = new ProjectTelemetry(); | ||
|
|
||
| #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type | ||
| telemetry.TrackTaskSubclassing(null, isMicrosoftOwned: false); | ||
| #pragma warning restore CS8625 | ||
|
|
||
| var properties = GetMSBuildTaskSubclassProperties(telemetry); | ||
|
|
||
| properties.Count.ShouldBe(0); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Helper method to get MSBuild task subclass properties from telemetry using reflection | ||
| /// </summary> | ||
| private System.Collections.Generic.Dictionary<string, string> GetMSBuildTaskSubclassProperties(ProjectTelemetry telemetry) | ||
| { | ||
| var method = typeof(ProjectTelemetry).GetMethod("GetMSBuildTaskSubclassProperties", | ||
| System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); | ||
| return (System.Collections.Generic.Dictionary<string, string>)method!.Invoke(telemetry, null)!; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Non-sealed user task that inherits from Microsoft.Build.Utilities.Task | ||
| /// </summary> | ||
| #pragma warning disable CA1852 // Type can be sealed | ||
| private class UserTask : Task | ||
| #pragma warning restore CA1852 | ||
| { | ||
| public override bool Execute() | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Another non-sealed user task that inherits from Microsoft.Build.Utilities.Task | ||
| /// </summary> | ||
| #pragma warning disable CA1852 // Type can be sealed | ||
| private class AnotherUserTask : Task | ||
| #pragma warning restore CA1852 | ||
| { | ||
| public override bool Execute() | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sealed task that inherits from Microsoft.Build.Utilities.Task | ||
| /// </summary> | ||
| private sealed class TestSealedTask : Task | ||
| { | ||
| public override bool Execute() | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Integration test that verifies telemetry is logged during a build with Microsoft tasks | ||
| /// </summary> | ||
| [Fact] | ||
| public void MSBuildTaskTelemetry_IsLoggedDuringBuild() | ||
| { | ||
| string projectContent = @" | ||
| <Project> | ||
| <Target Name='Build'> | ||
| <Message Text='Hello World' Importance='High' /> | ||
| </Target> | ||
| </Project>"; | ||
|
|
||
| var events = new System.Collections.Generic.List<BuildEventArgs>(); | ||
| var logger = new Microsoft.Build.Logging.ConsoleLogger(LoggerVerbosity.Diagnostic); | ||
|
|
||
| using var projectCollection = new ProjectCollection(); | ||
| using var stringReader = new System.IO.StringReader(projectContent); | ||
| using var xmlReader = System.Xml.XmlReader.Create(stringReader); | ||
| var project = new Project(xmlReader, null, null, projectCollection); | ||
|
|
||
| // Build the project | ||
| var result = project.Build(); | ||
|
|
||
| result.ShouldBeTrue(); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.