forked from microsoft/vstest
/
ParallelDiscoveryEventsHandler.cs
164 lines (136 loc) · 7.83 KB
/
ParallelDiscoveryEventsHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.Parallel
{
using System.Collections.Generic;
using Microsoft.VisualStudio.TestPlatform.Common.Telemetry;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using CommonResources = Common.Resources.Resources;
/// <summary>
/// ParallelDiscoveryEventsHandler for handling the discovery events in case of parallel discovery
/// </summary>
internal class ParallelDiscoveryEventsHandler : ITestDiscoveryEventsHandler2
{
private IProxyDiscoveryManager proxyDiscoveryManager;
private ITestDiscoveryEventsHandler2 actualDiscoveryEventsHandler;
private IParallelProxyDiscoveryManager parallelProxyDiscoveryManager;
private ParallelDiscoveryDataAggregator discoveryDataAggregator;
private IDataSerializer dataSerializer;
private IRequestData requestData;
public ParallelDiscoveryEventsHandler(IRequestData requestData,
IProxyDiscoveryManager proxyDiscoveryManager,
ITestDiscoveryEventsHandler2 actualDiscoveryEventsHandler,
IParallelProxyDiscoveryManager parallelProxyDiscoveryManager,
ParallelDiscoveryDataAggregator discoveryDataAggregator) :
this(requestData, proxyDiscoveryManager, actualDiscoveryEventsHandler, parallelProxyDiscoveryManager, discoveryDataAggregator, JsonDataSerializer.Instance)
{
}
internal ParallelDiscoveryEventsHandler(IRequestData requestData,
IProxyDiscoveryManager proxyDiscoveryManager,
ITestDiscoveryEventsHandler2 actualDiscoveryEventsHandler,
IParallelProxyDiscoveryManager parallelProxyDiscoveryManager,
ParallelDiscoveryDataAggregator discoveryDataAggregator,
IDataSerializer dataSerializer)
{
this.proxyDiscoveryManager = proxyDiscoveryManager;
this.actualDiscoveryEventsHandler = actualDiscoveryEventsHandler;
this.parallelProxyDiscoveryManager = parallelProxyDiscoveryManager;
this.discoveryDataAggregator = discoveryDataAggregator;
this.dataSerializer = dataSerializer;
this.requestData = requestData;
}
/// <inheritdoc/>
public void HandleDiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable<TestCase> lastChunk)
{
var totalTests = discoveryCompleteEventArgs.TotalCount;
var isAborted = discoveryCompleteEventArgs.IsAborted;
// we get discovery complete events from each host process
// so we cannot "complete" the actual operation until all sources are consumed
// We should not block last chunk results while we aggregate overall discovery data
if (lastChunk != null)
{
ConvertToRawMessageAndSend(MessageType.TestCasesFound, lastChunk);
this.HandleDiscoveredTests(lastChunk);
}
// Aggregate for final discovery complete
discoveryDataAggregator.Aggregate(totalTests, isAborted);
// Aggregate Discovery Data Metrics
discoveryDataAggregator.AggregateDiscoveryDataMetrics(discoveryCompleteEventArgs.Metrics);
// Do not send TestDiscoveryComplete to actual test discovery handler
// We need to see if there are still sources left - let the parallel manager decide
var parallelDiscoveryComplete = this.parallelProxyDiscoveryManager.HandlePartialDiscoveryComplete(
this.proxyDiscoveryManager,
totalTests,
null, // lastChunk should be null as we already sent this data above
isAborted);
if (parallelDiscoveryComplete)
{
// In case of sequential discovery - RawMessage would have contained a 'DiscoveryCompletePayload' object
// To send a raw message - we need to create raw message from an aggregated payload object
var testDiscoveryCompletePayload = new DiscoveryCompletePayload()
{
TotalTests = discoveryDataAggregator.TotalTests,
IsAborted = discoveryDataAggregator.IsAborted,
LastDiscoveredTests = null
};
// Collecting Final Discovery State
this.requestData.MetricsCollection.Add(TelemetryDataConstants.DiscoveryState, isAborted ? "Aborted" : "Completed");
// Collect Aggregated Metrics Data
var aggregatedDiscoveryDataMetrics = discoveryDataAggregator.GetAggregatedDiscoveryDataMetrics();
testDiscoveryCompletePayload.Metrics = aggregatedDiscoveryDataMetrics;
// we have to send raw messages as we block the discovery complete actual raw messages
this.ConvertToRawMessageAndSend(MessageType.DiscoveryComplete, testDiscoveryCompletePayload);
var finalDiscoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(this.discoveryDataAggregator.TotalTests,
this.discoveryDataAggregator.IsAborted);
finalDiscoveryCompleteEventArgs.Metrics = aggregatedDiscoveryDataMetrics;
// send actual test discovery complete to clients
this.actualDiscoveryEventsHandler.HandleDiscoveryComplete(finalDiscoveryCompleteEventArgs, null);
}
}
/// <inheritdoc/>
public void HandleRawMessage(string rawMessage)
{
// In case of parallel - we can send everything but handle complete
// DiscoveryComplete is not true-end of the overall discovery as we only get completion of one host here
// Always aggregate data, deserialize and raw for complete events
var message = this.dataSerializer.DeserializeMessage(rawMessage);
// Do not send CancellationRequested message to Output window in IDE, as it is not useful for user
if (string.Equals(message.MessageType, MessageType.TestMessage)
&& rawMessage.IndexOf(CommonResources.CancellationRequested) >= 0)
{
return;
}
// Do not deserialize further
if (!string.Equals(MessageType.DiscoveryComplete, message.MessageType))
{
this.actualDiscoveryEventsHandler.HandleRawMessage(rawMessage);
}
}
/// <inheritdoc/>
public void HandleDiscoveredTests(IEnumerable<TestCase> discoveredTestCases)
{
this.actualDiscoveryEventsHandler.HandleDiscoveredTests(discoveredTestCases);
}
/// <inheritdoc/>
public void HandleLogMessage(TestMessageLevel level, string message)
{
this.actualDiscoveryEventsHandler.HandleLogMessage(level, message);
}
/// <summary>
/// To send message to IDE output window use HandleRawMessage
/// </summary>
/// <param name="messageType"></param>
/// <param name="payload"></param>
private void ConvertToRawMessageAndSend(string messageType, object payload)
{
var rawMessage = this.dataSerializer.SerializePayload(messageType, payload);
this.actualDiscoveryEventsHandler.HandleRawMessage(rawMessage);
}
}
}