Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions tests/FSharp.Test.Utilities/CompilerAssert.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ module AssemblyResolver =
match found() with
| None -> Unchecked.defaultof<Assembly>
| Some name -> Assembly.Load(name) )

do addResolver()
#endif

type ExecutionOutcome =
Expand Down
33 changes: 16 additions & 17 deletions tests/FSharp.Test.Utilities/TestConsole.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,22 @@ module TestConsole =
type private RedirectingTextReader() =
inherit TextReader()
let holder = AsyncLocal<_>()
do holder.Value <- TextReader.Null

override _.Peek() = holder.Value.Peek()
override _.Read() = holder.Value.Read()
member _.Value = holder.Value
member _.Set (reader: TextReader) = holder.Value <- reader
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<TextWriter>()
do holder.Value <- TextWriter.Null
let holder = AsyncLocal<_>()
member _.Writer
with get() = holder.Value |> ValueOption.defaultValue TextWriter.Null
and set v = holder.Value <- ValueSome v

override _.Encoding = Encoding.UTF8
override _.Write(value: char) = holder.Value.Write(value)
member _.Value = holder.Value
member _.Set (writer: TextWriter) = holder.Value <- writer
override this.Write(value: char) = this.Writer.Write(value)

let private localIn = new RedirectingTextReader()
let private localOut = new RedirectingTextWriter()
Expand All @@ -41,12 +40,12 @@ module TestConsole =
// Taps into the redirected console stream.
type private CapturingWriter(redirecting: RedirectingTextWriter) as this =
inherit StringWriter()
let wrapped = redirecting.Value
do redirecting.Set this
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.Set wrapped
redirecting.Writer <- wrapped
base.Dispose(disposing: bool)

/// Captures console streams for the current async execution context.
Expand Down Expand Up @@ -77,9 +76,9 @@ module TestConsole =
string error

type ProvideInput(input: string) =
let oldIn = localIn.Value
let oldIn = localIn.Reader
do
new StringReader(input) |> localIn.Set
localIn.Reader <- new StringReader(input)

interface IDisposable with
member this.Dispose (): unit = localIn.Set oldIn
member this.Dispose (): unit = localIn.Reader <- oldIn
42 changes: 27 additions & 15 deletions tests/FSharp.Test.Utilities/XunitHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -190,33 +190,45 @@ type OpenTelemetryExport(testRunName, enable) =
member this.Dispose() =
for p in providers do p.Dispose()

// In some situations, VS can invoke CreateExecutor and RunTestCases many times during testhost lifetime.
// For example when executing "run until failure" command in Test Explorer.
// However, we want to ensure that OneTimeSetup is called only once per test run.
module OneTimeSetup =

let init =
lazy
#if !NETCOREAPP
// We need AssemblyResolver already here, because OpenTelemetry loads some assemblies dynamically.
log "Adding AssemblyResolver"
AssemblyResolver.addResolver ()
#endif
log "Overriding cache capacity"
Cache.OverrideCapacityForTesting()
log "Installing TestConsole redirection"
TestConsole.install()

logConfig initialConfig

let EnsureInitialized() =
// Ensure that the initialization is done only once per test run.
init.Force()

/// `XunitTestFramework` providing parallel console support and conditionally enabling optional xUnit customizations.
type FSharpXunitFramework(sink: IMessageSink) =
inherit XunitTestFramework(sink)

do OneTimeSetup.EnsureInitialized()

override this.CreateExecutor (assemblyName) =
{ new XunitTestFrameworkExecutor(assemblyName, this.SourceInformationProvider, this.DiagnosticMessageSink) with

// Because xUnit v2 lacks assembly fixture, this is a good place to ensure things get called right at the start of the test run.
// This gets executed once per test assembly.
override x.RunTestCases(testCases, executionMessageSink, executionOptions) =

#if !NETCOREAPP
// We need AssemblyResolver already here, because OpenTelemetry loads some assemblies dynamically.
AssemblyResolver.addResolver ()
#endif

// Override cache capacity to reduce memory usage in CI.
Cache.OverrideCapacityForTesting()

let testRunName = $"RunTests_{assemblyName.Name} {Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"

use _ = new OpenTelemetryExport(testRunName, Environment.GetEnvironmentVariable("FSHARP_OTEL_EXPORT") <> null)

logConfig initialConfig
log "Installing TestConsole redirection"
TestConsole.install()

use _ = new OpenTelemetryExport(testRunName, Environment.GetEnvironmentVariable("FSHARP_OTEL_EXPORT") <> null)

begin
use _ = Activity.startNoTags testRunName
// We can't just call base.RunTestCases here, because it's implementation is async void.
Expand Down
2 changes: 2 additions & 0 deletions tests/fsharp/single-test.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ open FSharp.Compiler.IO

let testConfig = testConfig __SOURCE_DIRECTORY__

let log = printfn

type Permutation =
#if NETCOREAPP
| FSC_NETCORE of optimized: bool * buildOnly: bool
Expand Down
2 changes: 2 additions & 0 deletions tests/fsharp/tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ let FSI = FSI_NETFX
#endif
// ^^^^^^^^^^^^ To run these tests in F# Interactive , 'build net40', then send this chunk, then evaluate body of a test ^^^^^^^^^^^^

let log = printfn

module CoreTests =


Expand Down
9 changes: 6 additions & 3 deletions tests/scripts/scriptlib.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ module Scripting =

let deleteDirectory output =
if Directory.Exists output then
Directory.Delete(output, true)

let log format = printfn format
Directory.Delete(output, true)

// Capture the original stdout for logging.
let private originalOut = stdout
// When used during test run, log will always output to the original stdout of the testhost, instead of the test output.
let log format = fprintfn originalOut format

type FilePath = string

Expand Down
Loading