-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add test for MSBuild Server #7592
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
rokonec
merged 65 commits into
dotnet:feature/msbuild-server
from
Forgind:server-node-test
Jun 7, 2022
Merged
Changes from 62 commits
Commits
Show all changes
65 commits
Select commit
Hold shift + click to select a range
987eca8
Add giant test for MSBuild Server
Forgind 251a319
Add more tests
Forgind 2321e53
Real fix
Forgind c25c45b
help with debugging test failure
Forgind 6d0c9b2
I'm stupid.
Forgind f61a2a5
More output info
Forgind 2688ec0
VS broke the build
Forgind b2283db
Add comm traces
Forgind 4ef560e
Merge branch 'feature/msbuild-server' of https://github.com/dotnet/ms…
Forgind f0a5420
More info
Forgind ded0746
Up verbosity
Forgind ac33664
Make parsing smarter
Forgind 9cec556
Try making processes
Forgind 0e4c5f5
Make build
Forgind 8eada1f
Temporarily remove test
Forgind 21a31ba
Add giant test for MSBuild Server
Forgind ec3e179
Add more tests
Forgind afb75f9
Real fix
Forgind 02ea730
help with debugging test failure
Forgind e50eb83
I'm stupid.
Forgind 3e0ab59
More output info
Forgind 5fc09be
VS broke the build
Forgind 901a0c3
Add comm traces
Forgind a4f382e
More info
Forgind 758af2e
Up verbosity
Forgind 8bb5299
Make parsing smarter
Forgind 6aab346
Try making processes
Forgind 4dd2afe
Make build
Forgind cf78cbf
Temporarily remove test
Forgind 40d9b7f
Revert "Temporarily remove test"
Forgind 973d8b7
Revert "Make build"
Forgind 0effe6f
Revert "Try making processes"
Forgind 98989de
add extra logging
AR-May 68a87e3
Add sleep.
AR-May 8620192
Revert "Add sleep."
AR-May ec9c7f7
Revert "Revert "Add sleep.""
AR-May d592b19
move sleep
AR-May 29d8163
Revert "move sleep"
AR-May 8a258b2
Add possible fix for missing output.
AR-May 3b3bcbe
Remove extra logging; Add checks.
AR-May 3d0eac6
More logging info.
AR-May 7c01bdf
more logging
AR-May 43800c7
more logging 2
AR-May 3ffa1c3
sync read
AR-May 1560796
Revert "sync read"
AR-May a34bb46
add redirects to client.
AR-May 968d485
add windows native call
AR-May 8bb9fb4
Lots of Task.Run
Forgind 91e2403
Try to use a modified lunch msbuild node func for msbuild server proc…
AR-May 3512dc2
Revert "Lots of Task.Run"
AR-May 02b9561
Revert "Temporarily remove test"
Forgind a20befe
Some cleanup
Forgind c8f5e1f
Try to use a modified lunch msbuild node func for msbuild server proc…
AR-May 0667a62
Merge branch 'feature/msbuild-server' into server-node-test
Forgind 9118409
Make build
Forgind cbb272f
Merge branch 'server-node-test' of https://github.com/Forgind/msbuild…
Forgind 356a8e0
drop diag
Forgind 7efa083
Merge branch 'extra-server-test' of https://github.com/Forgind/msbuil…
Forgind bfd6915
Cleanup
Forgind 4fd0490
Remove changes done to msbuild client.
AR-May 5bd041e
Merge branch 'feature/msbuild-server' into server-node-test
AR-May 7dcc13a
Remove test that uses MSBUILDNOINPROCNODE flag: it checks the wrong b…
AR-May f1234e2
Remove timeout for WaitForExit().
AR-May 4d94117
Add comments about WaitForExit and set a timeout for the process exec…
AR-May 4dd0a0a
Fix KillTree tool sharing.
rokonec 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
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
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,217 @@ | ||
| // 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 System.Diagnostics; | ||
| using System.Reflection; | ||
| using System.Text.RegularExpressions; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Build.Framework; | ||
| using Microsoft.Build.Shared; | ||
| using Microsoft.Build.UnitTests; | ||
| using Microsoft.Build.UnitTests.Shared; | ||
| #if NETFRAMEWORK | ||
| using Microsoft.IO; | ||
| #else | ||
| using System.IO; | ||
| #endif | ||
| using Shouldly; | ||
| using Xunit; | ||
| using Xunit.Abstractions; | ||
|
|
||
| namespace Microsoft.Build.Engine.UnitTests | ||
| { | ||
| public class SleepingTask : Microsoft.Build.Utilities.Task | ||
| { | ||
| public int SleepTime { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Sleep for SleepTime milliseconds. | ||
| /// </summary> | ||
| /// <returns>Success on success.</returns> | ||
| public override bool Execute() | ||
| { | ||
| Thread.Sleep(SleepTime); | ||
| return !Log.HasLoggedErrors; | ||
| } | ||
| } | ||
|
|
||
| public class ProcessIdTask : Microsoft.Build.Utilities.Task | ||
| { | ||
| [Output] | ||
| public int Pid { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Log the id for this process. | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public override bool Execute() | ||
| { | ||
| Pid = Process.GetCurrentProcess().Id; | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| public class MSBuildServer_Tests : IDisposable | ||
| { | ||
| private readonly ITestOutputHelper _output; | ||
| private readonly TestEnvironment _env; | ||
| private static string printPidContents = @$" | ||
| <Project> | ||
| <UsingTask TaskName=""ProcessIdTask"" AssemblyFile=""{Assembly.GetExecutingAssembly().Location}"" /> | ||
| <Target Name='AccessPID'> | ||
| <ProcessIdTask> | ||
| <Output PropertyName=""PID"" TaskParameter=""Pid"" /> | ||
| </ProcessIdTask> | ||
| <Message Text=""Server ID is $(PID)"" Importance=""High"" /> | ||
| </Target> | ||
| </Project>"; | ||
| private static string sleepingTaskContents = @$" | ||
| <Project> | ||
| <UsingTask TaskName=""SleepingTask"" AssemblyFile=""{Assembly.GetExecutingAssembly().Location}"" /> | ||
| <Target Name='Sleep'> | ||
| <SleepingTask SleepTime=""100000"" /> | ||
| </Target> | ||
| </Project>"; | ||
|
|
||
| public MSBuildServer_Tests(ITestOutputHelper output) | ||
| { | ||
| _output = output; | ||
| _env = TestEnvironment.Create(_output); | ||
| } | ||
|
|
||
| public void Dispose() => _env.Dispose(); | ||
|
|
||
| [Fact] | ||
| public void MSBuildServerTest() | ||
| { | ||
| TransientTestFile project = _env.CreateFile("testProject.proj", printPidContents); | ||
| _env.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); | ||
| string output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out bool success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| int pidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| int pidOfServerProcess = ParseNumber(output, "Server ID is "); | ||
| pidOfInitialProcess.ShouldNotBe(pidOfServerProcess, "We started a server node to execute the target rather than running it in-proc, so its pid should be different."); | ||
|
|
||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| int newPidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| newPidOfInitialProcess.ShouldNotBe(pidOfServerProcess, "We started a server node to execute the target rather than running it in-proc, so its pid should be different."); | ||
| newPidOfInitialProcess.ShouldNotBe(pidOfInitialProcess, "Process started by two MSBuild executions should be different."); | ||
| pidOfServerProcess.ShouldBe(ParseNumber(output, "Server ID is "), "Node used by both the first and second build should be the same."); | ||
|
|
||
| // Prep to kill the long-lived task we're about to start. | ||
| Task t = Task.Run(() => | ||
| { | ||
| // Wait for the long-lived task to start | ||
| // If this test seems to fail randomly, increase this time. | ||
| Thread.Sleep(1000); | ||
|
|
||
| // Kill the server | ||
| ProcessExtensions.KillTree(Process.GetProcessById(pidOfServerProcess), 1000); | ||
| }); | ||
|
|
||
| // Start long-lived task execution | ||
| TransientTestFile sleepProject = _env.CreateFile("napProject.proj", sleepingTaskContents); | ||
| RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, sleepProject.Path, out _); | ||
|
|
||
| t.Wait(); | ||
|
|
||
| // Ensure that a new build can still succeed and that its server node is different. | ||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| newPidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| int newServerProcessId = ParseNumber(output, "Server ID is "); | ||
| newPidOfInitialProcess.ShouldNotBe(pidOfInitialProcess, "Process started by two MSBuild executions should be different."); | ||
| newPidOfInitialProcess.ShouldNotBe(newServerProcessId, "We started a server node to execute the target rather than running it in-proc, so its pid should be different."); | ||
| pidOfServerProcess.ShouldNotBe(newServerProcessId, "Node used by both the first and second build should not be the same."); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void VerifyMixedLegacyBehavior() | ||
| { | ||
| TransientTestFile project = _env.CreateFile("testProject.proj", printPidContents); | ||
| _env.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); | ||
|
|
||
| string output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out bool success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| int pidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| int pidOfServerProcess = ParseNumber(output, "Server ID is "); | ||
| pidOfInitialProcess.ShouldNotBe(pidOfServerProcess, "We started a server node to execute the target rather than running it in-proc, so its pid should be different."); | ||
|
|
||
| Environment.SetEnvironmentVariable("MSBUILDUSESERVER", ""); | ||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| pidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| int pidOfNewserverProcess = ParseNumber(output, "Server ID is "); | ||
| pidOfInitialProcess.ShouldBe(pidOfNewserverProcess, "We did not start a server node to execute the target, so its pid should be the same."); | ||
|
|
||
| Environment.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); | ||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| pidOfInitialProcess = ParseNumber(output, "Process ID is "); | ||
| pidOfNewserverProcess = ParseNumber(output, "Server ID is "); | ||
| pidOfInitialProcess.ShouldNotBe(pidOfNewserverProcess, "We started a server node to execute the target rather than running it in-proc, so its pid should be different."); | ||
| pidOfServerProcess.ShouldBe(pidOfNewserverProcess, "Server node should be the same as from earlier."); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void BuildsWhileBuildIsRunningOnServer() | ||
| { | ||
| _env.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); | ||
| TransientTestFile project = _env.CreateFile("testProject.proj", printPidContents); | ||
| TransientTestFile sleepProject = _env.CreateFile("napProject.proj", sleepingTaskContents); | ||
|
|
||
| int pidOfServerProcess = -1; | ||
| Task? t = null; | ||
| try | ||
| { | ||
| // Start a server node and find its PID. | ||
| string output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out bool success, false, _output); | ||
| pidOfServerProcess = ParseNumber(output, "Server ID is "); | ||
|
|
||
| t = Task.Run(() => | ||
| { | ||
| RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, sleepProject.Path, out _, false, _output); | ||
| }); | ||
|
|
||
| // The server will soon be in use; make sure we don't try to use it before that happens. | ||
| Thread.Sleep(1000); | ||
|
|
||
| Environment.SetEnvironmentVariable("MSBUILDUSESERVER", "0"); | ||
|
|
||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| ParseNumber(output, "Server ID is ").ShouldBe(ParseNumber(output, "Process ID is "), "There should not be a server node for this build."); | ||
|
|
||
| Environment.SetEnvironmentVariable("MSBUILDUSESERVER", "1"); | ||
|
|
||
| output = RunnerUtilities.ExecMSBuild(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, project.Path, out success, false, _output); | ||
| success.ShouldBeTrue(); | ||
| pidOfServerProcess.ShouldNotBe(ParseNumber(output, "Server ID is "), "The server should be otherwise occupied."); | ||
| pidOfServerProcess.ShouldNotBe(ParseNumber(output, "Process ID is "), "There should not be a server node for this build."); | ||
| ParseNumber(output, "Server ID is ").ShouldBe(ParseNumber(output, "Process ID is "), "Process ID and Server ID should coincide."); | ||
| } | ||
| finally | ||
| { | ||
| if (pidOfServerProcess > -1) | ||
| { | ||
| ProcessExtensions.KillTree(Process.GetProcessById(pidOfServerProcess), 1000); | ||
| } | ||
|
|
||
| if (t is not null) | ||
| { | ||
| t.Wait(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private int ParseNumber(string searchString, string toFind) | ||
| { | ||
| Regex regex = new(@$"{toFind}(\d+)"); | ||
| Match match = regex.Match(searchString); | ||
| return int.Parse(match.Groups[1].Value); | ||
| } | ||
| } | ||
| } |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm likely not understanding the test, but process IDs get reused, and the algorithm used is not stable, so if a server process exits the next one can potentially get the same id?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests should check the logic of msbuild server. That is:
The task itself writes down the id of the process that is used for the build, and here we write down the process id of the msbuild process that was created for building a project. If msbuild server was used, these ids would be different (first one would be the id of msbuild server and the second one would be the id of msbuild client). If not used, they would be the same. Thus, we check how the build was executed, in or out the server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, so long as the first process has not exited, then yes of course they have different PID's. Thanks. Otherwise of course you have to check process start time.