@@ -11,7 +11,7 @@ public class TesseractOcrProviderTests
1111 [ Fact ]
1212 public async Task ExtractTextFromImageAsync_WhenOutputIsMissing_ThrowsFileNotFound ( )
1313 {
14- var processRunner = new FakeProcessRunner ( ) ;
14+ var processRunner = new FakeProcessRunner { NextResult = new ProcessResult ( 1 , "" , "" ) } ;
1515 var environment = new FakeSystemEnvironment { FileExistsImpl = _ => false } ;
1616 var engine = new TesseractOcrProvider ( environment , processRunner ) ;
1717
@@ -21,14 +21,36 @@ public async Task ExtractTextFromImageAsync_WhenOutputIsMissing_ThrowsFileNotFou
2121 new OcrOptions { TesseractPath = "/bin/echo" } ) ;
2222
2323 await act . Should ( ) . ThrowAsync < FileNotFoundException > ( )
24- . WithMessage ( "OCR output file not found. Tesseract execution failed *" )
24+ . WithMessage ( "OCR output file not found ( Tesseract exited with code 1) *" )
2525 . ConfigureAwait ( true ) ;
2626
2727 processRunner . StartInfos . Should ( ) . HaveCount ( 1 ) ;
2828 processRunner . StartInfos [ 0 ] . FileName . Should ( ) . Be ( "/bin/echo" ) ;
2929 processRunner . StartInfos [ 0 ] . Arguments . Should ( ) . Contain ( "input.png" ) ;
3030 }
3131
32+ [ Fact ]
33+ public async Task ExtractTextFromImageAsync_WhenOutputIsMissing_IncludesStderrInExceptionMessage ( )
34+ {
35+ const string tesseractStderr = "Error opening data file /usr/share/tessdata/eng.traineddata" ;
36+ var processRunner = new FakeProcessRunner
37+ {
38+ NextResult = new ProcessResult ( ExitCode : 1 , StandardOutput : "" , StandardError : tesseractStderr )
39+ } ;
40+ var environment = new FakeSystemEnvironment { FileExistsImpl = _ => false } ;
41+ var engine = new TesseractOcrProvider ( environment , processRunner ) ;
42+
43+ var act = ( ) => engine . ExtractTextFromImageAsync (
44+ "input.png" ,
45+ Path . Combine ( Path . GetTempPath ( ) , "missing-output.txt" ) ,
46+ new OcrOptions { TesseractPath = "/bin/echo" } ) ;
47+
48+ await act . Should ( ) . ThrowAsync < FileNotFoundException > ( )
49+ . Where ( e => e . Message . Contains ( tesseractStderr , StringComparison . Ordinal )
50+ && e . Message . Contains ( "exited with code 1" , StringComparison . Ordinal ) )
51+ . ConfigureAwait ( true ) ;
52+ }
53+
3254 [ Theory ]
3355 [ InlineData ( "out.log" , "out.txt" ) ]
3456 [ InlineData ( "out" , "out.txt" ) ]
@@ -207,10 +229,12 @@ private sealed class FakeProcessRunner : IProcessRunner
207229 {
208230 public List < ProcessStartInfo > StartInfos { get ; } = [ ] ;
209231
210- public Task RunAsync ( ProcessStartInfo startInfo , CancellationToken cancellationToken )
232+ public ProcessResult NextResult { get ; set ; } = new ( ExitCode : 0 , StandardOutput : "" , StandardError : "" ) ;
233+
234+ public Task < ProcessResult > RunAsync ( ProcessStartInfo startInfo , CancellationToken cancellationToken )
211235 {
212236 StartInfos . Add ( startInfo ) ;
213- return Task . CompletedTask ;
237+ return Task . FromResult ( NextResult ) ;
214238 }
215239 }
216240}
0 commit comments