public void Clone() { BuildRequest request = CreateNewBuildRequest(1, new string[0]); BuildResult result1 = new BuildResult(request); result1.ResultsByTarget.Add("FOO", TestUtilities.GetEmptySucceedingTargetResult()); Assert.IsTrue(result1.ResultsByTarget.ContainsKey("foo")); // test comparer BuildResult result2 = result1.Clone(); result1.ResultsByTarget.Add("BAR", TestUtilities.GetEmptySucceedingTargetResult()); Assert.IsTrue(result1.ResultsByTarget.ContainsKey("foo")); // test comparer Assert.IsTrue(result1.ResultsByTarget.ContainsKey("bar")); Assert.AreEqual(result1.SubmissionId, result2.SubmissionId); Assert.AreEqual(result1.ConfigurationId, result2.ConfigurationId); Assert.AreEqual(result1.GlobalRequestId, result2.GlobalRequestId); Assert.AreEqual(result1.ParentGlobalRequestId, result2.ParentGlobalRequestId); Assert.AreEqual(result1.NodeRequestId, result2.NodeRequestId); Assert.AreEqual(result1.CircularDependency, result2.CircularDependency); Assert.AreEqual(result1.ResultsByTarget["foo"], result2.ResultsByTarget["foo"]); Assert.AreEqual(result1.OverallResult, result2.OverallResult); }
/// <summary> /// Informs the scheduler of a specific result. /// </summary> public IEnumerable<ScheduleResponse> ReportResult(int nodeId, BuildResult result) { _schedulingData.EventTime = DateTime.UtcNow; List<ScheduleResponse> responses = new List<ScheduleResponse>(); TraceScheduler("Reporting result from node {0} for request {1}, parent {2}.", nodeId, result.GlobalRequestId, result.ParentGlobalRequestId); // Record these results to the cache. _resultsCache.AddResult(result); if (result.NodeRequestId == BuildRequest.ResultsTransferNodeRequestId) { // We are transferring results. The node to which they should be sent has already been recorded by the // HandleRequestBlockedOnResultsTransfer method in the configuration. BuildRequestConfiguration config = _configCache[result.ConfigurationId]; ScheduleResponse response = ScheduleResponse.CreateReportResultResponse(config.ResultsNodeId, result); responses.Add(response); } else { // Tell the request to which this result belongs than it is done. SchedulableRequest request = _schedulingData.GetExecutingRequest(result.GlobalRequestId); request.Complete(result); // Report results to our parent, or report submission complete as necessary. if (request.Parent != null) { // responses.Add(new ScheduleResponse(request.Parent.AssignedNode, new BuildRequestUnblocker(request.Parent.BuildRequest.GlobalRequestId, result))); ErrorUtilities.VerifyThrow(result.ParentGlobalRequestId == request.Parent.BuildRequest.GlobalRequestId, "Result's parent doesn't match request's parent."); // When adding the result to the cache we merge the result with what ever is already in the cache this may cause // the result to have more target outputs in it than was was requested. To fix this we can ask the cache itself for the result we just added. // When results are returned from the cache we filter them based on the targets we requested. This causes our result to only // include the targets we requested rather than the merged result. // Note: In this case we do not need to log that we got the results from the cache because we are only using the cache // for filtering the targets for the result instead rather than using the cache as the location where this result came from. ScheduleResponse response = TrySatisfyRequestFromCache(request.Parent.AssignedNode, request.BuildRequest, skippedResultsAreOK: false); // response may be null if the result was never added to the cache. This can happen if the result has an exception in it // or the results could not be satisfied because the initial or default targets have been skipped. If that is the case // we need to report the result directly since it contains an exception if (response == null) { response = ScheduleResponse.CreateReportResultResponse(request.Parent.AssignedNode, result.Clone()); } responses.Add(response); } else { // This was root request, we can report submission complete. // responses.Add(new ScheduleResponse(result)); responses.Add(ScheduleResponse.CreateSubmissionCompleteResponse(result)); if (result.OverallResult != BuildResultCode.Failure) { WriteSchedulingPlan(result.SubmissionId); } } // This result may apply to a number of other unscheduled requests which are blocking active requests. Report to them as well. List<SchedulableRequest> unscheduledRequests = new List<SchedulableRequest>(_schedulingData.UnscheduledRequests); foreach (SchedulableRequest unscheduledRequest in unscheduledRequests) { if (unscheduledRequest.BuildRequest.GlobalRequestId == result.GlobalRequestId) { TraceScheduler("Request {0} (node request {1}) also satisfied by result.", unscheduledRequest.BuildRequest.GlobalRequestId, unscheduledRequest.BuildRequest.NodeRequestId); BuildResult newResult = new BuildResult(unscheduledRequest.BuildRequest, result, null); // Report results to the parent. int parentNode = (unscheduledRequest.Parent == null) ? InvalidNodeId : unscheduledRequest.Parent.AssignedNode; // There are other requests which we can satisfy based on this result, lets pull the result out of the cache // and satisfy those requests. Normally a skipped result would lead to the cache refusing to satisfy the // request, because the correct response in that case would be to attempt to rebuild the target in case there // are state changes that would cause it to now excute. At this point, however, we already know that the parent // request has completed, and we already know that this request has the same global request ID, which means that // its configuration and set of targets are identical -- from MSBuild's perspective, it's the same. So since // we're not going to attempt to re-execute it, if there are skipped targets in the result, that's fine. We just // need to know what the target results are so that we can log them. ScheduleResponse response = TrySatisfyRequestFromCache(parentNode, unscheduledRequest.BuildRequest, skippedResultsAreOK: true); // If we have a response we need to tell the loggers that we satisified that request from the cache. if (response != null) { LogRequestHandledFromCache(unscheduledRequest.BuildRequest, response.BuildResult); } else { // Response may be null if the result was never added to the cache. This can happen if the result has // an exception in it. If that is the case, we should report the result directly so that the // build manager knows that it needs to shut down logging manually. response = GetResponseForResult(parentNode, unscheduledRequest.BuildRequest, newResult.Clone()); } responses.Add(response); // Mark the request as complete (and the parent is no longer blocked by this request.) unscheduledRequest.Complete(newResult); } } } // This node may now be free, so run the scheduler. ScheduleUnassignedRequests(responses); return responses; }