Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 95 additions & 16 deletions src/Build.UnitTests/BackEnd/Scheduler_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@

namespace Microsoft.Build.UnitTests.BackEnd
{
using System.Linq;
using FluentAssertions;
using Microsoft.Build.Unittest;

/// <summary>
/// Tests of the scheduler.
/// </summary>
// Ignore: Causing issues with other tests
// NOTE: marked as "internal" to disable the entire test class, as was done for MSTest.
public class Scheduler_Tests : IDisposable
{
/// <summary>
Expand Down Expand Up @@ -57,6 +57,11 @@ public class Scheduler_Tests : IDisposable
/// </summary>
private BuildParameters _parameters;

/// <summary>
/// Configuration ID.
/// </summary>
private const int DefaultConfigId = 99;

/// <summary>
/// Set up
/// </summary>
Expand All @@ -70,8 +75,8 @@ public Scheduler_Tests()
_host = new MockHost();
_scheduler = new Scheduler();
_scheduler.InitializeComponent(_host);
CreateConfiguration(99, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, 99, Array.Empty<string>(), null);
CreateConfiguration(DefaultConfigId, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, DefaultConfigId, Array.Empty<string>(), null);

// Set up the scheduler with one node to start with.
_scheduler.ReportNodesCreated(new NodeInfo[] { new NodeInfo(1, NodeProviderType.InProc) });
Expand Down Expand Up @@ -386,8 +391,8 @@ public void VerifyRequestOrderingDoesNotAffectNodeCreationCountWithInProcAndAnyR
_parameters.ShutdownInProcNodeOnBuildFinish = true;
_buildManager = new BuildManager();

CreateConfiguration(99, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, 99, Array.Empty<string>(), null);
CreateConfiguration(DefaultConfigId, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, DefaultConfigId, Array.Empty<string>(), null);

CreateConfiguration(1, "foo.proj");
BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, NodeAffinity.Any, _defaultParentRequest);
Expand Down Expand Up @@ -578,8 +583,8 @@ public void VerifyNoOverCreationOfNodesWithBuildLoop()
_parameters.ShutdownInProcNodeOnBuildFinish = true;
_buildManager = new BuildManager();

CreateConfiguration(99, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, 99, Array.Empty<string>(), null);
CreateConfiguration(DefaultConfigId, "parent.proj");
_defaultParentRequest = CreateBuildRequest(99, DefaultConfigId, Array.Empty<string>(), null);

CreateConfiguration(1, "foo.proj");
BuildRequest request1 = CreateBuildRequest(1, 1, new string[] { "foo" }, NodeAffinity.OutOfProc, _defaultParentRequest);
Expand Down Expand Up @@ -768,12 +773,13 @@ private BuildResult CacheBuildResult(BuildRequest request, string target, WorkUn
}

/// <summary>
/// Creates a build result for a request
/// Creates a build result for a request.
/// </summary>
private BuildResult CreateBuildResult(BuildRequest request, string target, WorkUnitResult workUnitResult)
{
BuildResult result = new BuildResult(request);
result.AddResultsForTarget(target, new TargetResult(Array.Empty<TaskItem>(), workUnitResult));

return result;
}

Expand All @@ -788,23 +794,30 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId)
/// <summary>
/// Creates a build request.
/// </summary>
private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets)
private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags.None)
{
return CreateBuildRequest(nodeRequestId, configId, targets, _defaultParentRequest);
return CreateBuildRequest(nodeRequestId, configId, targets, _defaultParentRequest, buildRequestDataFlags);
}

/// <summary>
/// Creates a build request.
/// </summary>
private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, BuildRequest parentRequest)
private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, BuildRequest parentRequest, BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags.None)
{
return CreateBuildRequest(nodeRequestId, configId, targets, NodeAffinity.Any, parentRequest);
return CreateBuildRequest(nodeRequestId, configId, targets, NodeAffinity.Any, parentRequest, buildRequestDataFlags: buildRequestDataFlags);
}

/// <summary>
/// Creates a build request.
/// </summary>
private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, NodeAffinity nodeAffinity, BuildRequest parentRequest, ProxyTargets proxyTargets = null)
private BuildRequest CreateBuildRequest(
int nodeRequestId,
int configId,
string[] targets,
NodeAffinity nodeAffinity,
BuildRequest parentRequest,
ProxyTargets proxyTargets = null,
BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags.None)
{
(targets == null ^ proxyTargets == null).ShouldBeTrue();

Expand All @@ -825,16 +838,19 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[
targets,
hostServices,
BuildEventContext.Invalid,
parentRequest);
parentRequest,
buildRequestDataFlags: buildRequestDataFlags);
}

parentRequest.ShouldBeNull();

return new BuildRequest(
submissionId: 1,
nodeRequestId,
configId,
proxyTargets,
hostServices);
hostServices,
buildRequestDataFlags: buildRequestDataFlags);
}

private BuildRequest CreateProxyBuildRequest(int nodeRequestId, int configId, ProxyTargets proxyTargets, BuildRequest parentRequest)
Expand All @@ -848,6 +864,69 @@ private BuildRequest CreateProxyBuildRequest(int nodeRequestId, int configId, Pr
proxyTargets);
}

/// <summary>
/// The test checks how scheduler handles the duplicated requests and cache MISS for this case.
/// It's expected to have the duplicated request rescheduled for the execution.
/// </summary>
[Fact]
public void ReportResultTest_NoCacheHitForDupes()
{
// Create a duplicate of the existing _defaultParentRequest, but with a different build request flag, so we can't get the result from the cache.
BuildRequest duplicateRequest = CreateBuildRequest(2, configId: DefaultConfigId, Array.Empty<string>(), parentRequest: null, BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild);

// Schedule the duplicate request -> it goes to unscheduled request due to duplicated configId
_scheduler.ReportRequestBlocked(2, new BuildRequestBlocker(-1, Array.Empty<string>(), [duplicateRequest]));

// try to get a result for the parent request and see if we get a result for the duplicate request
var results = _scheduler.ReportResult(1, CreateBuildResult(_defaultParentRequest, "", BuildResultUtilities.GetSuccessResult()))
.ToList();

results.ShouldNotBeNull();
results.Count.ShouldBe(2);

// Completed _defaultParentRequest
results[0].BuildResult.ShouldNotBeNull();
results[0].BuildResult.BuildRequestDataFlags.ShouldBe(BuildRequestDataFlags.None);
results[0].Action.ShouldBe(ScheduleActionType.SubmissionComplete);

// The automatically scheduled duplicated request.
results[1].BuildResult.ShouldBeNull();
results[1].NodeId.Should().Be(1);
results[1].Action.ShouldBe(ScheduleActionType.Schedule);
results[1].BuildRequest.BuildRequestDataFlags.ShouldBe(BuildRequestDataFlags.ProvideSubsetOfStateAfterBuild);
}

/// <summary>
/// The test checks how scheduler handles the duplicated requests and cache HIT for this case.
/// It's expected to have an immediate result for the duplicated request.
/// </summary>
[Fact]
public void ReportResultTest_CacheHitForDupes()
{
// Create a duplicate of the existing _defaultParentRequest.
BuildRequest duplicateRequest = CreateBuildRequest(2, configId: DefaultConfigId, Array.Empty<string>(), parentRequest: null, BuildRequestDataFlags.None);

// Schedule the duplicate request -> it goes to unscheduled request due to duplicated configId
_scheduler.ReportRequestBlocked(1, new BuildRequestBlocker(-1, Array.Empty<string>(), [duplicateRequest]));

// try to get a result for the parent request and see if we get a result for the duplicate request.
var results = _scheduler.ReportResult(1, CreateBuildResult(duplicateRequest, "", BuildResultUtilities.GetSuccessResult()))
.ToList();

results.ShouldNotBeNull();
results.Count.ShouldBe(2);

// Completed _defaultParentRequest
results[0].BuildResult.ShouldNotBeNull();
results[0].BuildResult.BuildRequestDataFlags.ShouldBe(BuildRequestDataFlags.None);
results[0].Action.ShouldBe(ScheduleActionType.SubmissionComplete);

// We hit cache and completed the duplicate request.
results[1].BuildResult.ShouldNotBeNull();
results[1].BuildResult.BuildRequestDataFlags.ShouldBe(BuildRequestDataFlags.None);
results[1].Action.ShouldBe(ScheduleActionType.SubmissionComplete);
}

/// <summary>
/// Method that fakes the actions done by BuildManager.PerformSchedulingActions
/// </summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Build/BackEnd/BuildManager/BuildManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2427,8 +2427,8 @@ private void HandleNewRequest(int node, BuildRequestBlocker blocker)
}
}

IEnumerable<ScheduleResponse> response = _scheduler!.ReportRequestBlocked(node, blocker);
PerformSchedulingActions(response);
IEnumerable<ScheduleResponse> responses = _scheduler!.ReportRequestBlocked(node, blocker);
PerformSchedulingActions(responses);
}

/// <summary>
Expand Down
15 changes: 7 additions & 8 deletions src/Build/BackEnd/BuildManager/BuildParameters.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
ο»Ώ// Licensed to the .NET Foundation under one or more agreements.
// 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.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Threading;
Expand Down Expand Up @@ -129,7 +129,7 @@ public class BuildParameters : ITranslatable
/// <summary>
/// The original process environment.
/// </summary>
private Dictionary<string, string> _buildProcessEnvironment;
private FrozenDictionary<string, string> _buildProcessEnvironment;

/// <summary>
/// The environment properties for the build.
Expand Down Expand Up @@ -282,9 +282,7 @@ internal BuildParameters(BuildParameters other, bool resetEnvironment = false)
_enableRarNode = other._enableRarNode;
_buildProcessEnvironment = resetEnvironment
? CommunicationsUtilities.GetEnvironmentVariables()
: other._buildProcessEnvironment != null
? new Dictionary<string, string>(other._buildProcessEnvironment)
: null;
: other._buildProcessEnvironment;
_environmentProperties = other._environmentProperties != null ? new PropertyDictionary<ProjectPropertyInstance>(other._environmentProperties) : null;
_forwardingLoggers = other._forwardingLoggers != null ? new List<ForwardingLoggerRecord>(other._forwardingLoggers) : null;
_globalProperties = other._globalProperties != null ? new PropertyDictionary<ProjectPropertyInstance>(other._globalProperties) : null;
Expand Down Expand Up @@ -356,8 +354,7 @@ public bool UseSynchronousLogging
/// <summary>
/// Gets the environment variables which were set when this build was created.
/// </summary>
public IDictionary<string, string> BuildProcessEnvironment => new ReadOnlyDictionary<string, string>(
_buildProcessEnvironment ?? new Dictionary<string, string>(0));
public IDictionary<string, string> BuildProcessEnvironment => BuildProcessEnvironmentInternal;

/// <summary>
/// The name of the culture to use during the build.
Expand Down Expand Up @@ -720,6 +717,8 @@ internal int BuildId
set => _buildId = value;
}

internal FrozenDictionary<string, string> BuildProcessEnvironmentInternal => _buildProcessEnvironment ?? FrozenDictionary<string, string>.Empty;

/// <summary>
/// Gets or sets the environment properties.
/// </summary>
Expand Down
11 changes: 6 additions & 5 deletions src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
Expand Down Expand Up @@ -1378,7 +1379,7 @@ private void InitializeOperatingEnvironment()
else
{
// Restore the original build environment variables.
SetEnvironmentVariableBlock(_componentHost.BuildParameters.BuildProcessEnvironment);
SetEnvironmentVariableBlock(_componentHost.BuildParameters.BuildProcessEnvironmentInternal);
}
}

Expand All @@ -1401,17 +1402,17 @@ private void RestoreOperatingEnvironment()
/// <summary>
/// Sets the environment block to the set of saved variables.
/// </summary>
private void SetEnvironmentVariableBlock(IDictionary<string, string> savedEnvironment)
private void SetEnvironmentVariableBlock(FrozenDictionary<string, string> savedEnvironment)
{
IDictionary<string, string> currentEnvironment = CommunicationsUtilities.GetEnvironmentVariables();
FrozenDictionary<string, string> currentEnvironment = CommunicationsUtilities.GetEnvironmentVariables();
ClearVariablesNotInEnvironment(savedEnvironment, currentEnvironment);
UpdateEnvironmentVariables(savedEnvironment, currentEnvironment);
}

/// <summary>
/// Clears from the current environment any variables which do not exist in the saved environment
/// </summary>
private void ClearVariablesNotInEnvironment(IDictionary<string, string> savedEnvironment, IDictionary<string, string> currentEnvironment)
private void ClearVariablesNotInEnvironment(FrozenDictionary<string, string> savedEnvironment, FrozenDictionary<string, string> currentEnvironment)
{
foreach (KeyValuePair<string, string> entry in currentEnvironment)
{
Expand All @@ -1425,7 +1426,7 @@ private void ClearVariablesNotInEnvironment(IDictionary<string, string> savedEnv
/// <summary>
/// Updates the current environment with values in the saved environment which differ or are not yet set.
/// </summary>
private void UpdateEnvironmentVariables(IDictionary<string, string> savedEnvironment, IDictionary<string, string> currentEnvironment)
private void UpdateEnvironmentVariables(FrozenDictionary<string, string> savedEnvironment, FrozenDictionary<string, string> currentEnvironment)
{
foreach (KeyValuePair<string, string> entry in savedEnvironment)
{
Expand Down
Loading