-
Notifications
You must be signed in to change notification settings - Fork 858
Expand file tree
/
Copy pathTestConsole.fs
More file actions
95 lines (78 loc) · 3.24 KB
/
TestConsole.fs
File metadata and controls
95 lines (78 loc) · 3.24 KB
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
namespace FSharp.Test
open System
open System.IO
open System.Text
open System.Threading
module TestConsole =
/// Redirects reads performed on different async execution contexts to the relevant TextReader held by AsyncLocal.
type private RedirectingTextReader() =
inherit TextReader()
let holder = AsyncLocal<_>()
member _.Reader
with get() = holder.Value |> ValueOption.defaultValue TextReader.Null
and set v = holder.Value <- ValueSome v
override this.Peek() = this.Reader.Peek()
override this.Read() = this.Reader.Read()
/// Redirects writes performed on different async execution contexts to the relevant TextWriter held by AsyncLocal.
type private RedirectingTextWriter() =
inherit TextWriter()
let holder = AsyncLocal<_>()
let original = Console.Out
member _.Writer
with get() = holder.Value |> ValueOption.defaultValue TextWriter.Null
and set v = holder.Value <- ValueSome v
override _.Encoding = Encoding.UTF8
override this.Write(value: char) =
this.Writer.Write(value)
original.Write(value)
let private localIn = new RedirectingTextReader()
let private localOut = new RedirectingTextWriter()
let private localError = new RedirectingTextWriter()
let private isInstalled = ref 0
/// Installs console redirection. Idempotent and thread-safe.
let install () =
if Interlocked.CompareExchange(isInstalled, 1, 0) = 0 then
Console.SetIn localIn
Console.SetOut localOut
Console.SetError localError
// Taps into the redirected console stream.
type private CapturingWriter(redirecting: RedirectingTextWriter) as this =
inherit StringWriter()
let wrapped = redirecting.Writer
do redirecting.Writer <- this
override _.Encoding = Encoding.UTF8
override _.Write(value: char) =
wrapped.Write(value)
base.Write(value)
override _.Dispose (disposing: bool) =
redirecting.Writer <- wrapped
base.Dispose(disposing: bool)
/// Captures console streams for the current async execution context.
/// Each simultaneously executing test case runs in a separate async execution context.
///
/// Can be used to capture just a single compilation or eval as well as the whole test case execution output.
type ExecutionCapture() =
do
install ()
Console.Out.Flush()
Console.Error.Flush()
let output = new CapturingWriter(localOut)
let error = new CapturingWriter(localError)
member _.Dispose() =
output.Dispose()
error.Dispose()
interface IDisposable with
member this.Dispose (): unit = this.Dispose()
member _.OutText =
Console.Out.Flush()
string output
member _.ErrorText =
Console.Error.Flush()
string error
type ProvideInput(input: string) =
do install ()
let oldIn = localIn.Reader
do
localIn.Reader <- new StringReader(input)
interface IDisposable with
member this.Dispose (): unit = localIn.Reader <- oldIn