public void TestSimpleStateProgression() { // Start in Ready BuildRequest request = CreateNewBuildRequest(1, new string[1] { "foo" }); BuildRequestConfiguration config = new BuildRequestConfiguration(1, new BuildRequestData("foo", new Dictionary<string, string>(), "foo", new string[0], null), "2.0"); BuildRequestEntry entry = new BuildRequestEntry(request, config); Assert.AreEqual(entry.State, BuildRequestEntryState.Ready); Assert.AreEqual(entry.Request, request); Assert.IsNull(entry.Result); // Move to active. Should not be any results yet. IDictionary<int, BuildResult> results = entry.Continue(); Assert.AreEqual(entry.State, BuildRequestEntryState.Active); Assert.IsNull(entry.Result); Assert.IsNull(results); // Wait for results, move to waiting. BuildRequest waitingRequest = CreateNewBuildRequest(2, new string[1] { "bar" }); entry.WaitForResult(waitingRequest); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); Assert.AreEqual(entry.Request, request); Assert.IsNull(entry.Result); // Provide the results, move to ready. BuildResult requiredResult = new BuildResult(waitingRequest); requiredResult.AddResultsForTarget("bar", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult); Assert.AreEqual(entry.State, BuildRequestEntryState.Ready); Assert.AreEqual(entry.Request, request); Assert.IsNull(entry.Result); // Continue the build, move to active. results = entry.Continue(); Assert.AreEqual(entry.State, BuildRequestEntryState.Active); Assert.IsNull(entry.Result); Assert.AreEqual(results.Count, 1); Assert.IsTrue(results.ContainsKey(requiredResult.NodeRequestId)); Assert.AreEqual(results[requiredResult.NodeRequestId], requiredResult); // Complete the build, move to completed. BuildResult result = new BuildResult(request); result.AddResultsForTarget("foo", TestUtilities.GetEmptySucceedingTargetResult()); entry.Complete(result); Assert.AreEqual(entry.State, BuildRequestEntryState.Complete); Assert.IsNotNull(entry.Result); Assert.AreEqual(entry.Result, result); }
public void TestResultsWithNoMatch1() { BuildRequest request = CreateNewBuildRequest(1, new string[1] { "foo" }); BuildRequestConfiguration config = new BuildRequestConfiguration(1, new BuildRequestData("foo", new Dictionary<string, string>(), "foo", new string[0], null), "2.0"); BuildRequestEntry entry = new BuildRequestEntry(request, config); Assert.AreEqual(entry.State, BuildRequestEntryState.Ready); entry.Continue(); Assert.AreEqual(entry.State, BuildRequestEntryState.Active); BuildRequest waitingRequest1 = CreateNewBuildRequest(2, new string[1] { "bar" }); entry.WaitForResult(waitingRequest1); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); BuildRequest randomRequest = CreateNewBuildRequest(3, new string[0]); BuildResult requiredResult = new BuildResult(randomRequest); requiredResult.AddResultsForTarget("bar", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); }
public void TestMixedWaitingRequests() { BuildRequest request = CreateNewBuildRequest(1, new string[1] { "foo" }); BuildRequestConfiguration config = new BuildRequestConfiguration(1, new BuildRequestData("foo", new Dictionary<string, string>(), "foo", new string[0], null), "2.0"); BuildRequestEntry entry = new BuildRequestEntry(request, config); Assert.AreEqual(entry.State, BuildRequestEntryState.Ready); entry.Continue(); Assert.AreEqual(entry.State, BuildRequestEntryState.Active); BuildRequest waitingRequest1 = CreateNewBuildRequest(2, new string[1] { "bar" }); entry.WaitForResult(waitingRequest1); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); BuildRequest waitingRequest2 = CreateNewBuildRequest(-1, new string[1] { "xor" }); entry.WaitForResult(waitingRequest2); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); Assert.IsNull(entry.GetRequestsToIssueIfReady(), "Entry should not be ready to issue because there are unresolved configurations"); entry.ResolveConfigurationRequest(-1, 3); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); BuildResult requiredResult1 = new BuildResult(waitingRequest1); requiredResult1.AddResultsForTarget("bar", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult1); Assert.AreEqual(entry.State, BuildRequestEntryState.Waiting); BuildResult requiredResult2 = new BuildResult(waitingRequest2); requiredResult2.AddResultsForTarget("xor", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult2); Assert.AreEqual(entry.State, BuildRequestEntryState.Ready); }
public void TestRequestWithReferenceCancelled() { BuildRequestConfiguration configuration = CreateTestProject(1); try { TestTargetBuilder targetBuilder = (TestTargetBuilder)_host.GetComponent(BuildComponentType.TargetBuilder); IConfigCache configCache = (IConfigCache)_host.GetComponent(BuildComponentType.ConfigCache); FullyQualifiedBuildRequest[] newRequest = new FullyQualifiedBuildRequest[1] { new FullyQualifiedBuildRequest(configuration, new string[1] { "testTarget2" }, true) }; targetBuilder.SetNewBuildRequests(newRequest); configCache.AddConfiguration(configuration); BuildRequest request = CreateNewBuildRequest(1, new string[1] { "target1" }); BuildRequestEntry entry = new BuildRequestEntry(request, configuration); BuildResult result = new BuildResult(request); result.AddResultsForTarget("target1", GetEmptySuccessfulTargetResult()); targetBuilder.SetResultsToReturn(result); _requestBuilder.BuildRequest(GetNodeLoggingContext(), entry); WaitForEvent(_newBuildRequestsEvent, "New Build Requests"); Assert.Equal(_newBuildRequests_Entry, entry); ObjectModelHelpers.AssertArrayContentsMatch(_newBuildRequests_FQRequests, newRequest); BuildResult newResult = new BuildResult(_newBuildRequests_BuildRequests[0]); newResult.AddResultsForTarget("testTarget2", GetEmptySuccessfulTargetResult()); entry.ReportResult(newResult); _requestBuilder.ContinueRequest(); Thread.Sleep(500); _requestBuilder.CancelRequest(); WaitForEvent(_buildRequestCompletedEvent, "Build Request Completed"); Assert.Equal(BuildRequestEntryState.Complete, entry.State); Assert.Equal(entry, _buildRequestCompleted_Entry); Assert.Equal(BuildResultCode.Failure, _buildRequestCompleted_Entry.Result.OverallResult); } finally { DeleteTestProject(configuration); } }
/// <summary> /// This method is responsible for evaluating whether we have enough information to make the request of the Build Manager, /// or if we need to obtain additional configuration information. It then issues either configuration /// requests or build requests, or both as needed. /// </summary> /// <param name="issuingEntry">The BuildRequestEntry which is making the request</param> /// <param name="newRequests">The array of "child" build requests to be issued.</param> /// <remarks> /// When we receive a build request, we first have to determine if we already have a configuration which matches the /// one used by the request. We do this because everywhere we deal with requests and results beyond this function, we /// use configuration ids, which are assigned once by the Build Manager and are global to the system. If we do /// not have a global configuration id, we can't check to see if we already have build results for the request, so we /// cannot send the request out. Thus, first we determine the configuration id. /// /// Assuming we don't have the global configuration id locally, we will send the configuration to the Build Manager. /// It will look up or assign the global configuration id and send it back to us. /// /// Once we have the global configuration id, we can then look up results locally. If we have enough results to fulfill /// the request, we give them back to the request, otherwise we have to forward the request to the Build Mangager /// for scheduling. /// </remarks> private void IssueBuildRequests(BuildRequestEntry issuingEntry, FullyQualifiedBuildRequest[] newRequests) { ErrorUtilities.VerifyThrow(_componentHost != null, "No host object set"); // For each request, we need to determine if we have a local configuration in the // configuration cache. If we do, we can issue the build request immediately. // Otherwise, we need to ask the Build Manager for configuration IDs and issue those requests // later. IConfigCache globalConfigCache = (IConfigCache)_componentHost.GetComponent(BuildComponentType.ConfigCache); // We are going to potentially issue several requests. We don't want the state of the request being modified by // other threads while this occurs, so we lock the request for the duration. This lock is the same lock // used by the BuildRequestEntry itself to lock each of its data-modifying methods, effectively preventing // any other thread from modifying the BuildRequestEntry while we hold it. This mechanism also means that it // is not necessary for other threads to take the global lock explicitly if they are just doing single operations // to the entry rather than a series of them. lock (issuingEntry.GlobalLock) { List<BuildResult> existingResultsToReport = new List<BuildResult>(); HashSet<NGen<int>> unresolvedConfigurationsAdded = new HashSet<NGen<int>>(); foreach (FullyQualifiedBuildRequest request in newRequests) { // Do we have a matching configuration? BuildRequestConfiguration matchingConfig = globalConfigCache.GetMatchingConfiguration(request.Config); BuildRequest newRequest = null; if (matchingConfig == null) { // No configuration locally, are we already waiting for it? matchingConfig = _unresolvedConfigurations.GetMatchingConfiguration(request.Config); if (matchingConfig == null) { // Not waiting for it request.Config.ConfigurationId = GetNextUnresolvedConfigurationId(); _unresolvedConfigurations.AddConfiguration(request.Config); unresolvedConfigurationsAdded.Add(request.Config.ConfigurationId); } else { request.Config.ConfigurationId = matchingConfig.ConfigurationId; } // Whether we are already waiting for a configuration or we need to wait for another one // we will add this request as waiting for a configuration. As new configuration resolutions // come in, we will check our requests which are waiting for configurations move them to // waiting for results. It is important that we tell the issuing request to wait for a result // prior to issuing any necessary configuration request so that we don't get into a state where // we receive the configuration response before we enter the wait state. newRequest = new BuildRequest(issuingEntry.Request.SubmissionId, GetNextBuildRequestId(), request.Config.ConfigurationId, request.Targets, issuingEntry.Request.HostServices, issuingEntry.Request.BuildEventContext, issuingEntry.Request); issuingEntry.WaitForResult(newRequest); if (matchingConfig == null) { // Issue the config resolution request TraceEngine("Request {0}({1}) (nr {2}) is waiting on configuration {3} (IBR)", issuingEntry.Request.GlobalRequestId, issuingEntry.Request.ConfigurationId, issuingEntry.Request.NodeRequestId, request.Config.ConfigurationId); issuingEntry.WaitForConfiguration(request.Config); } } else { // We have a configuration, see if we already have results locally. newRequest = new BuildRequest(issuingEntry.Request.SubmissionId, GetNextBuildRequestId(), matchingConfig.ConfigurationId, request.Targets, issuingEntry.Request.HostServices, issuingEntry.Request.BuildEventContext, issuingEntry.Request); IResultsCache resultsCache = (IResultsCache)_componentHost.GetComponent(BuildComponentType.ResultsCache); ResultsCacheResponse response = resultsCache.SatisfyRequest(newRequest, matchingConfig.ProjectInitialTargets, matchingConfig.ProjectDefaultTargets, matchingConfig.GetAfterTargetsForDefaultTargets(newRequest), skippedResultsAreOK: false); if (response.Type == ResultsCacheResponseType.Satisfied) { // We have a result, give it back to this request. issuingEntry.WaitForResult(newRequest); // Log the fact that we handled this from the cache. _nodeLoggingContext.LogRequestHandledFromCache(newRequest, _configCache[newRequest.ConfigurationId], response.Results); // Can't report the result directly here, because that could cause the request to go from // Waiting to Ready. existingResultsToReport.Add(response.Results); } else { // No result, to wait for it. issuingEntry.WaitForResult(newRequest); } } } // If we have any results we had to report, do so now. foreach (BuildResult existingResult in existingResultsToReport) { issuingEntry.ReportResult(existingResult); } // Issue any configuration requests we may still need. List<BuildRequestConfiguration> unresolvedConfigurationsToIssue = issuingEntry.GetUnresolvedConfigurationsToIssue(); if (unresolvedConfigurationsToIssue != null) { foreach (BuildRequestConfiguration unresolvedConfigurationToIssue in unresolvedConfigurationsToIssue) { unresolvedConfigurationsAdded.Remove(unresolvedConfigurationToIssue.ConfigurationId); IssueConfigurationRequest(unresolvedConfigurationToIssue); } } // Remove any configurations we ended up not waiting for, otherwise future requests will think we are still waiting for them // and will never get submitted. foreach (int unresolvedConfigurationId in unresolvedConfigurationsAdded) { _unresolvedConfigurations.RemoveConfiguration(unresolvedConfigurationId); } // Finally, if we can issue build requests, do so. List<BuildRequest> requestsToIssue = issuingEntry.GetRequestsToIssueIfReady(); if (requestsToIssue != null) { BuildRequestBlocker blocker = new BuildRequestBlocker(issuingEntry.Request.GlobalRequestId, issuingEntry.GetActiveTargets(), requestsToIssue.ToArray()); IssueBuildRequest(blocker); } if (issuingEntry.State == BuildRequestEntryState.Ready) { ErrorUtilities.VerifyThrow((requestsToIssue == null) || (requestsToIssue.Count == 0), "Entry shouldn't be ready if we also issued requests."); ActivateBuildRequest(issuingEntry); } } }
public void TestMultipleWaitingRequests() { BuildRequest request = CreateNewBuildRequest(1, new string[1] { "foo" }); BuildRequestData data1 = new BuildRequestData("foo", new Dictionary<string, string>(), "foo", new string[0], null); BuildRequestConfiguration config = new BuildRequestConfiguration(1, data1, "2.0"); BuildRequestEntry entry = new BuildRequestEntry(request, config); entry.Continue(); Assert.Equal(entry.State, BuildRequestEntryState.Active); BuildRequest waitingRequest1 = CreateNewBuildRequest(2, new string[1] { "bar" }); entry.WaitForResult(waitingRequest1); Assert.Equal(entry.State, BuildRequestEntryState.Waiting); BuildRequest waitingRequest2 = CreateNewBuildRequest(2, new string[1] { "xor" }); entry.WaitForResult(waitingRequest2); Assert.Equal(entry.State, BuildRequestEntryState.Waiting); BuildResult requiredResult1 = new BuildResult(waitingRequest1); requiredResult1.AddResultsForTarget("bar", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult1); Assert.Equal(entry.State, BuildRequestEntryState.Waiting); BuildResult requiredResult2 = new BuildResult(waitingRequest2); requiredResult2.AddResultsForTarget("xor", TestUtilities.GetEmptySucceedingTargetResult()); entry.ReportResult(requiredResult2); Assert.Equal(entry.State, BuildRequestEntryState.Ready); }