@@ -83,6 +83,73 @@ public void DotNetRun (bool isRelease, string typemapImplementation, AndroidRunt
8383 Assert . IsTrue ( didLaunch , "Activity should have started." ) ;
8484 }
8585
86+ [ Test ]
87+ public void DotNetRunWaitForExit ( )
88+ {
89+ const string logcatMessage = "DOTNET_RUN_TEST_MESSAGE_12345" ;
90+ var proj = new XamarinAndroidApplicationProject ( ) ;
91+
92+ // Enable verbose output from Microsoft.Android.Run for debugging
93+ proj . SetProperty ( "_AndroidRunExtraArgs" , "--verbose" ) ;
94+
95+ // Add a Console.WriteLine that will appear in logcat
96+ proj . MainActivity = proj . DefaultMainActivity . Replace (
97+ "//${AFTER_ONCREATE}" ,
98+ $ "Console.WriteLine (\" { logcatMessage } \" );") ;
99+
100+ using var builder = CreateApkBuilder ( ) ;
101+ builder . Save ( proj ) ;
102+
103+ var dotnet = new DotNetCLI ( Path . Combine ( Root , builder . ProjectDirectory , proj . ProjectFilePath ) ) ;
104+ Assert . IsTrue ( dotnet . Build ( ) , "`dotnet build` should succeed" ) ;
105+
106+ // Start dotnet run with WaitForExit=true, which uses Microsoft.Android.Run
107+ using var process = dotnet . StartRun ( ) ;
108+
109+ var locker = new Lock ( ) ;
110+ var output = new StringBuilder ( ) ;
111+ var outputReceived = new ManualResetEventSlim ( false ) ;
112+ bool foundMessage = false ;
113+
114+ process . OutputDataReceived += ( sender , e ) => {
115+ if ( e . Data != null ) {
116+ lock ( locker ) {
117+ output . AppendLine ( e . Data ) ;
118+ if ( e . Data . Contains ( logcatMessage ) ) {
119+ foundMessage = true ;
120+ outputReceived . Set ( ) ;
121+ }
122+ }
123+ }
124+ } ;
125+ process . ErrorDataReceived += ( sender , e ) => {
126+ if ( e . Data != null ) {
127+ lock ( locker ) {
128+ output . AppendLine ( $ "STDERR: { e . Data } ") ;
129+ }
130+ }
131+ } ;
132+
133+ process . BeginOutputReadLine ( ) ;
134+ process . BeginErrorReadLine ( ) ;
135+
136+ // Wait for the expected message or timeout
137+ bool messageFound = outputReceived . Wait ( TimeSpan . FromSeconds ( 60 ) ) ;
138+
139+ // Kill the process (simulating Ctrl+C)
140+ if ( ! process . HasExited ) {
141+ process . Kill ( entireProcessTree : true ) ;
142+ process . WaitForExit ( ) ;
143+ }
144+
145+ // Write the output to a log file for debugging
146+ string logPath = Path . Combine ( Root , builder . ProjectDirectory , "dotnet-run-output.log" ) ;
147+ File . WriteAllText ( logPath , output . ToString ( ) ) ;
148+ TestContext . AddTestAttachment ( logPath ) ;
149+
150+ Assert . IsTrue ( foundMessage , $ "Expected message '{ logcatMessage } ' was not found in output. See { logPath } for details.") ;
151+ }
152+
86153 [ Test ]
87154 public void DeployToDevice ( [ Values ] bool isRelease , [ Values ] AndroidRuntime runtime )
88155 {
@@ -263,7 +330,6 @@ void Button_ViewTreeObserver_GlobalLayout (object sender, EventArgs e)
263330 } , Path . Combine ( Root , builder . ProjectDirectory , "startup-logcat.log" ) , 60 ) , $ "Output did not contain { expectedLogcatOutput } !") ;
264331 }
265332
266- // TODO: check if AppDomain.CurrentDomain.UnhandledException even works in CoreCLR and NativeAOT
267333 [ Test ]
268334 public void SubscribeToAppDomainUnhandledException ( [ Values ] AndroidRuntime runtime )
269335 {
0 commit comments