4
4
#nullable disable
5
5
6
6
using System . Diagnostics ;
7
+ using System . Globalization ;
7
8
using System . IO . Pipes ;
8
9
using Microsoft . DotNet . Cli . Commands . Test . IPC ;
9
10
using Microsoft . DotNet . Cli . Commands . Test . IPC . Models ;
10
11
using Microsoft . DotNet . Cli . Commands . Test . IPC . Serializers ;
12
+ using Microsoft . DotNet . Cli . Commands . Test . Terminal ;
11
13
using Microsoft . DotNet . Cli . Utils ;
12
14
13
15
namespace Microsoft . DotNet . Cli . Commands . Test ;
14
16
15
- internal sealed class TestApplication ( TestModule module , BuildOptions buildOptions ) : IDisposable
17
+ internal sealed class TestApplication (
18
+ TestModule module ,
19
+ BuildOptions buildOptions ,
20
+ TestOptions testOptions ,
21
+ TerminalTestReporter output ) : IDisposable
16
22
{
17
23
private readonly BuildOptions _buildOptions = buildOptions ;
24
+ private readonly TestApplicationHandler _handler = new ( output , module , testOptions ) ;
18
25
19
26
private readonly List < string > _outputData = [ ] ;
20
27
private readonly List < string > _errorData = [ ] ;
@@ -25,27 +32,23 @@ internal sealed class TestApplication(TestModule module, BuildOptions buildOptio
25
32
private readonly List < NamedPipeServer > _testAppPipeConnections = [ ] ;
26
33
private readonly Dictionary < NamedPipeServer , HandshakeMessage > _handshakes = new ( ) ;
27
34
28
- public event EventHandler < HandshakeArgs > HandshakeReceived ;
29
35
public event EventHandler < HelpEventArgs > HelpRequested ;
30
- public event EventHandler < DiscoveredTestEventArgs > DiscoveredTestsReceived ;
31
- public event EventHandler < TestResultEventArgs > TestResultsReceived ;
32
- public event EventHandler < FileArtifactEventArgs > FileArtifactsReceived ;
33
- public event EventHandler < SessionEventArgs > SessionEventReceived ;
34
- public event EventHandler < ErrorEventArgs > ErrorReceived ;
35
- public event EventHandler < TestProcessExitEventArgs > TestProcessExited ;
36
36
37
37
public TestModule Module { get ; } = module ;
38
+ public TestOptions TestOptions { get ; } = testOptions ;
38
39
39
40
public bool HasFailureDuringDispose { get ; private set ; }
40
41
41
- public async Task < int > RunAsync ( TestOptions testOptions )
42
+ public async Task < int > RunAsync ( )
42
43
{
43
- if ( testOptions . HasFilterMode && ! ModulePathExists ( ) )
44
+ // TODO: RunAsync is probably expected to be executed exactly once on each TestApplication instance.
45
+ // Consider throwing an exception if it's called more than once.
46
+ if ( TestOptions . HasFilterMode && ! ModulePathExists ( ) )
44
47
{
45
48
return ExitCode . GenericFailure ;
46
49
}
47
50
48
- var processStartInfo = CreateProcessStartInfo ( testOptions ) ;
51
+ var processStartInfo = CreateProcessStartInfo ( ) ;
49
52
50
53
_testAppPipeConnectionLoop = Task . Run ( async ( ) => await WaitConnectionAsync ( _cancellationToken . Token ) , _cancellationToken . Token ) ;
51
54
var testProcessResult = await StartProcess ( processStartInfo ) ;
@@ -55,15 +58,15 @@ public async Task<int> RunAsync(TestOptions testOptions)
55
58
return testProcessResult ;
56
59
}
57
60
58
- private ProcessStartInfo CreateProcessStartInfo ( TestOptions testOptions )
61
+ private ProcessStartInfo CreateProcessStartInfo ( )
59
62
{
60
63
var processStartInfo = new ProcessStartInfo
61
64
{
62
65
// We should get correct RunProperties right away.
63
66
// For the case of dotnet test --test-modules path/to/dll, the TestModulesFilterHandler is responsible
64
67
// for providing the dotnet muxer as RunCommand, and `exec "path/to/dll"` as RunArguments.
65
68
FileName = Module . RunProperties . Command ,
66
- Arguments = GetArguments ( testOptions ) ,
69
+ Arguments = GetArguments ( ) ,
67
70
RedirectStandardOutput = true ,
68
71
RedirectStandardError = true ,
69
72
} ;
@@ -96,7 +99,7 @@ private ProcessStartInfo CreateProcessStartInfo(TestOptions testOptions)
96
99
return processStartInfo ;
97
100
}
98
101
99
- private string GetArguments ( TestOptions testOptions )
102
+ private string GetArguments ( )
100
103
{
101
104
// Keep RunArguments first.
102
105
// In the case of UseAppHost=false, RunArguments is set to `exec $(TargetPath)`:
@@ -107,7 +110,7 @@ private string GetArguments(TestOptions testOptions)
107
110
// In short, it's expected to already be escaped properly.
108
111
StringBuilder builder = new ( Module . RunProperties . Arguments ) ;
109
112
110
- if ( testOptions . IsHelp )
113
+ if ( TestOptions . IsHelp )
111
114
{
112
115
builder . Append ( $ " { TestingPlatformOptions . HelpOption . Name } ") ;
113
116
}
@@ -259,9 +262,9 @@ private static string GetSupportedProtocolVersion(HandshakeMessage handshakeMess
259
262
}
260
263
261
264
private static HandshakeMessage CreateHandshakeMessage ( string version ) =>
262
- new ( new Dictionary < byte , string >
265
+ new ( new Dictionary < byte , string > ( capacity : 5 )
263
266
{
264
- { HandshakeMessagePropertyNames . PID , Process . GetCurrentProcess ( ) . Id . ToString ( ) } ,
267
+ { HandshakeMessagePropertyNames . PID , Environment . ProcessId . ToString ( CultureInfo . InvariantCulture ) } ,
265
268
{ HandshakeMessagePropertyNames . Architecture , RuntimeInformation . ProcessArchitecture . ToString ( ) } ,
266
269
{ HandshakeMessagePropertyNames . Framework , RuntimeInformation . FrameworkDescription } ,
267
270
{ HandshakeMessagePropertyNames . OS , RuntimeInformation . OSDescription } ,
@@ -275,11 +278,11 @@ private async Task<int> StartProcess(ProcessStartInfo processStartInfo)
275
278
Logger . LogTrace ( ( ) => $ "Test application arguments: { processStartInfo . Arguments } ") ;
276
279
}
277
280
278
- var process = Process . Start ( processStartInfo ) ;
281
+ using var process = Process . Start ( processStartInfo ) ;
279
282
StoreOutputAndErrorData ( process ) ;
280
283
await process . WaitForExitAsync ( ) ;
281
284
282
- TestProcessExited ? . Invoke ( this , new TestProcessExitEventArgs { OutputData = _outputData , ErrorData = _errorData , ExitCode = process . ExitCode } ) ;
285
+ _handler . OnTestProcessExited ( process . ExitCode , _outputData , _errorData ) ;
283
286
284
287
return process . ExitCode ;
285
288
}
@@ -310,57 +313,33 @@ private bool ModulePathExists()
310
313
{
311
314
if ( ! File . Exists ( Module . RunProperties . Command ) )
312
315
{
313
- ErrorReceived . Invoke ( this , new ErrorEventArgs { ErrorMessage = $ "Test module '{ Module . RunProperties . Command } ' not found. Build the test application before or run 'dotnet test'." } ) ;
316
+ // TODO: The error should be shown to the user, not just logged to trace.
317
+ Logger . LogTrace ( ( ) => $ "Test module '{ Module . RunProperties . Command } ' not found. Build the test application before or run 'dotnet test'.") ;
318
+
314
319
return false ;
315
320
}
316
321
return true ;
317
322
}
318
323
319
324
public void OnHandshakeMessage ( HandshakeMessage handshakeMessage , bool gotSupportedVersion )
320
- {
321
- HandshakeReceived ? . Invoke ( this , new HandshakeArgs { Handshake = new Handshake ( handshakeMessage . Properties ) , GotSupportedVersion = gotSupportedVersion } ) ;
322
- }
325
+ => _handler . OnHandshakeReceived ( handshakeMessage , gotSupportedVersion ) ;
323
326
324
- public void OnCommandLineOptionMessages ( CommandLineOptionMessages commandLineOptionMessages )
327
+ private void OnCommandLineOptionMessages ( CommandLineOptionMessages commandLineOptionMessages )
325
328
{
326
329
HelpRequested ? . Invoke ( this , new HelpEventArgs { ModulePath = commandLineOptionMessages . ModulePath , CommandLineOptions = [ .. commandLineOptionMessages . CommandLineOptionMessageList . Select ( message => new CommandLineOption ( message . Name , message . Description , message . IsHidden , message . IsBuiltIn ) ) ] } ) ;
327
330
}
328
331
329
- internal void OnDiscoveredTestMessages ( DiscoveredTestMessages discoveredTestMessages )
330
- {
331
- DiscoveredTestsReceived ? . Invoke ( this , new DiscoveredTestEventArgs
332
- {
333
- ExecutionId = discoveredTestMessages . ExecutionId ,
334
- InstanceId = discoveredTestMessages . InstanceId ,
335
- DiscoveredTests = [ .. discoveredTestMessages . DiscoveredMessages . Select ( message => new DiscoveredTest ( message . Uid , message . DisplayName ) ) ]
336
- } ) ;
337
- }
332
+ private void OnDiscoveredTestMessages ( DiscoveredTestMessages discoveredTestMessages )
333
+ => _handler . OnDiscoveredTestsReceived ( discoveredTestMessages ) ;
338
334
339
- internal void OnTestResultMessages ( TestResultMessages testResultMessage )
340
- {
341
- TestResultsReceived ? . Invoke ( this , new TestResultEventArgs
342
- {
343
- ExecutionId = testResultMessage . ExecutionId ,
344
- InstanceId = testResultMessage . InstanceId ,
345
- SuccessfulTestResults = [ .. testResultMessage . SuccessfulTestMessages . Select ( message => new SuccessfulTestResult ( message . Uid , message . DisplayName , message . State , message . Duration , message . Reason , message . StandardOutput , message . ErrorOutput , message . SessionUid ) ) ] ,
346
- FailedTestResults = [ .. testResultMessage . FailedTestMessages . Select ( message => new FailedTestResult ( message . Uid , message . DisplayName , message . State , message . Duration , message . Reason , [ .. message . Exceptions . Select ( e => new FlatException ( e . ErrorMessage , e . ErrorType , e . StackTrace ) ) ] , message . StandardOutput , message . ErrorOutput , message . SessionUid ) ) ]
347
- } ) ;
348
- }
335
+ private void OnTestResultMessages ( TestResultMessages testResultMessage )
336
+ => _handler . OnTestResultsReceived ( testResultMessage ) ;
349
337
350
338
internal void OnFileArtifactMessages ( FileArtifactMessages fileArtifactMessages )
351
- {
352
- FileArtifactsReceived ? . Invoke ( this , new FileArtifactEventArgs
353
- {
354
- ExecutionId = fileArtifactMessages . ExecutionId ,
355
- InstanceId = fileArtifactMessages . InstanceId ,
356
- FileArtifacts = [ .. fileArtifactMessages . FileArtifacts . Select ( message => new FileArtifact ( message . FullPath , message . DisplayName , message . Description , message . TestUid , message . TestDisplayName , message . SessionUid ) ) ]
357
- } ) ;
358
- }
339
+ => _handler . OnFileArtifactsReceived ( fileArtifactMessages ) ;
359
340
360
- internal void OnSessionEvent ( TestSessionEvent sessionEvent )
361
- {
362
- SessionEventReceived ? . Invoke ( this , new SessionEventArgs { SessionEvent = new TestSession ( sessionEvent . SessionType , sessionEvent . SessionUid , sessionEvent . ExecutionId ) } ) ;
363
- }
341
+ private void OnSessionEvent ( TestSessionEvent sessionEvent )
342
+ => _handler . OnSessionEventReceived ( sessionEvent ) ;
364
343
365
344
public override string ToString ( )
366
345
{
0 commit comments