1+ // Copyright (c) Microsoft Corporation.
2+ // Licensed under the MIT License.
3+
4+ namespace VirtualClient . Actions
5+ {
6+ using System ;
7+ using System . Collections . Generic ;
8+ using System . IO ;
9+ using System . Reflection ;
10+ using System . Threading ;
11+ using System . Threading . Tasks ;
12+ using AutoFixture ;
13+ using VirtualClient ;
14+ using VirtualClient . Common . Telemetry ;
15+ using VirtualClient . Contracts ;
16+ using Microsoft . Extensions . DependencyInjection ;
17+ using Moq ;
18+ using NUnit . Framework ;
19+
20+ [ TestFixture ]
21+ [ Category ( "Unit" ) ]
22+ [ Platform ( Exclude = "Unix,Linux,MacOsX" ) ]
23+ public class DotNetRuntimeExecutorTests
24+ {
25+ private MockFixture fixture ;
26+ private DependencyPath mockPath ;
27+ private DependencyPath currentDirectoryPath ;
28+ private string rawString ;
29+
30+ [ SetUp ]
31+ public void SetUpTests ( )
32+ {
33+ this . fixture = new MockFixture ( ) ;
34+ this . fixture . Setup ( PlatformID . Win32NT ) ;
35+ this . mockPath = this . fixture . Create < DependencyPath > ( ) ;
36+ this . fixture . Parameters = new Dictionary < string , IConvertible >
37+ {
38+ { "PackageName" , "DotNetRuntime" }
39+ } ;
40+
41+ this . SetupDefaultMockBehavior ( ) ;
42+ }
43+
44+ [ Test ]
45+ public async Task DotNetRuntimeExecutorInitializesItsDependenciesAsExpected ( )
46+ {
47+ using ( TestDotNetRuntimeExecutor executor = new TestDotNetRuntimeExecutor ( this . fixture ) )
48+ {
49+ Assert . IsNull ( executor . ExecutablePath ) ;
50+
51+ await executor . InitializeAsync ( EventContext . None , CancellationToken . None )
52+ . ConfigureAwait ( false ) ;
53+
54+ string expectedPath = this . fixture . PlatformSpecifics . Combine (
55+ this . mockPath . Path , "win-x64" , "dotnet.bat" ) ;
56+ Assert . AreEqual ( expectedPath , executor . ExecutablePath ) ;
57+ }
58+ }
59+
60+ [ Test ]
61+ public void DotNetRuntimeExecutorThrowsOnInitializationWhenTheWorkloadPackageIsNotFound ( )
62+ {
63+ this . fixture . PackageManager . OnGetPackage ( ) . ReturnsAsync ( null as DependencyPath ) ;
64+ using ( TestDotNetRuntimeExecutor executor = new TestDotNetRuntimeExecutor ( this . fixture ) )
65+ {
66+ DependencyException exception = Assert . ThrowsAsync < DependencyException > (
67+ ( ) => executor . InitializeAsync ( EventContext . None , CancellationToken . None ) ) ;
68+ Assert . AreEqual ( ErrorReason . WorkloadDependencyMissing , exception . Reason ) ;
69+ }
70+ }
71+
72+ [ Test ]
73+ [ Ignore ( "There is some kind of very unusual and difficult to determine anomaly that causes this method to fail to run due to a call to the IProcessProxy.Kill() method downstream." ) ]
74+ public async Task DotNetRuntimeExecutorExecutesWorkloadAsExpected ( )
75+ {
76+ using ( TestDotNetRuntimeExecutor executor = new TestDotNetRuntimeExecutor ( this . fixture ) )
77+ {
78+ string expectedFilePath = this . fixture . PlatformSpecifics . Combine ( this . mockPath . Path , "runtimes" , "win-x64" , "dotnet.bat" ) ;
79+ int executed = 0 ;
80+ this . fixture . ProcessManager . OnCreateProcess = ( file , arguments , workingDirectory ) =>
81+ {
82+ executed ++ ;
83+ Assert . AreEqual ( expectedFilePath , file ) ;
84+ return this . fixture . Process ;
85+ } ;
86+
87+ await executor . ExecuteAsync ( EventContext . None , CancellationToken . None )
88+ . ConfigureAwait ( false ) ;
89+
90+ Assert . AreEqual ( 1 , executed ) ;
91+ }
92+ }
93+
94+ [ Test ]
95+ [ Ignore ( "There is some kind of very unusual and difficult to determine anomaly that causes this method to fail to run due to a call to the IProcessProxy.Kill() method downstream." ) ]
96+ public void DotNetRuntimeExecutorThrowsWorkloadExceptionWhenTheResultsFileIsNotGenerated ( )
97+ {
98+ using ( TestDotNetRuntimeExecutor executor = new TestDotNetRuntimeExecutor ( this . fixture ) )
99+ {
100+ this . fixture . ProcessManager . OnCreateProcess = ( file , arguments , workingDirectory ) =>
101+ {
102+ this . fixture . FileSystem . Setup ( fe => fe . File . Exists ( executor . ResultsFilePath ) ) . Returns ( false ) ;
103+ return this . fixture . Process ;
104+ } ;
105+
106+ WorkloadException exception = Assert . ThrowsAsync < WorkloadException > (
107+ ( ) => executor . ExecuteAsync ( EventContext . None , CancellationToken . None ) ) ;
108+ Assert . AreEqual ( ErrorReason . WorkloadFailed , exception . Reason ) ;
109+ }
110+ }
111+
112+ private void SetupDefaultMockBehavior ( )
113+ {
114+ string currentDirectory = Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) ;
115+ this . currentDirectoryPath = new DependencyPath ( "DotNetRuntime" , currentDirectory ) ;
116+ string resultsPath = this . fixture . PlatformSpecifics . Combine ( this . currentDirectoryPath . Path , "Examples" , "DotNetRuntimeResultsExample.txt" ) ;
117+ this . rawString = File . ReadAllText ( resultsPath ) ;
118+ this . fixture . FileSystem . Setup ( fe => fe . File . Exists ( It . IsAny < string > ( ) ) ) . Returns ( true ) ;
119+ this . fixture . FileSystem . Setup ( fe => fe . File . Exists ( null ) ) . Returns ( false ) ;
120+ this . fixture . FileSystem . Setup ( fc => fc . File . Copy ( It . IsAny < string > ( ) , It . IsAny < string > ( ) ) ) ;
121+ this . fixture . Directory . Setup ( d => d . Exists ( It . IsAny < string > ( ) ) )
122+ . Returns ( true ) ;
123+
124+ this . fixture . FileSystem . Setup ( rt => rt . File . ReadAllTextAsync ( It . IsAny < string > ( ) , It . IsAny < CancellationToken > ( ) ) )
125+ . ReturnsAsync ( this . rawString ) ;
126+
127+ this . fixture . PackageManager . OnGetPackage ( ) . ReturnsAsync ( this . mockPath ) ;
128+ this . fixture . ProcessManager . OnCreateProcess = ( command , arguments , directory ) => this . fixture . Process ;
129+ }
130+
131+ private class TestDotNetRuntimeExecutor : DotNetRuntimeExecutor
132+ {
133+ public TestDotNetRuntimeExecutor ( MockFixture fixture )
134+ : base ( fixture . Dependencies , fixture . Parameters )
135+ {
136+ }
137+
138+ public TestDotNetRuntimeExecutor ( IServiceCollection dependencies , IDictionary < string , IConvertible > parameters )
139+ : base ( dependencies , parameters )
140+ {
141+ }
142+
143+ public new Task InitializeAsync ( EventContext telemetryContext , CancellationToken cancellationToken )
144+ {
145+ return base . InitializeAsync ( telemetryContext , cancellationToken ) ;
146+ }
147+
148+ public new Task ExecuteAsync ( EventContext context , CancellationToken cancellationToken )
149+ {
150+ this . InitializeAsync ( context , cancellationToken ) . GetAwaiter ( ) . GetResult ( ) ;
151+ return base . ExecuteAsync ( context , cancellationToken ) ;
152+ }
153+ }
154+ }
155+ }
0 commit comments