From 1c05bbd8bbf9bddc159e19ee70a8f94f12f0f6cf Mon Sep 17 00:00:00 2001 From: Pantazis Deligiannis Date: Mon, 17 Oct 2022 10:42:14 -0700 Subject: [PATCH] streamline logging API (#377) --- .github/workflows/test-coyote.yml | 2 +- Samples/BoundedBuffer/BoundedBuffer.sln | 25 - Samples/BoundedBuffer/Program.cs | 2 +- .../CoffeeMachineActors.sln | 25 - .../CoffeeMachineTasks/CoffeeMachineTasks.sln | 25 - Samples/Common/LogWriter.cs | 2 +- .../DrinksServingRobotActors.sln | 25 - Samples/HelloWorldActors/HelloWorldActors.sln | 25 - Samples/Monitors/Monitors.sln | 25 - Samples/Scripts/build.ps1 | 14 +- Samples/Scripts/run-tests.ps1 | 20 + Samples/Timers/Timers.sln | 25 - .../Store/Cosmos/Providers/ClientProvider.cs | 6 +- Scripts/common.psm1 | 7 +- Source/Core/Actors/Actor.cs | 57 +- Source/Core/Actors/ActorExecutionContext.cs | 320 ++++-------- .../Coverage/ActorRuntimeLogGraphBuilder.cs | 2 +- Source/Core/Actors/EventQueues/EventQueue.cs | 8 +- .../Core/Actors/EventQueues/MockEventQueue.cs | 8 +- Source/Core/Actors/IActorRuntime.cs | 15 - .../{ActorLogWriter.cs => ActorLogManager.cs} | 22 +- .../Logging/ActorRuntimeLogTextFormatter.cs | 216 ++++---- Source/Core/Actors/RuntimeFactory.cs | 22 +- Source/Core/Actors/StateMachine.cs | 8 +- Source/Core/Configuration.cs | 102 ++-- Source/Core/IO/Debugging/Debug.cs | 187 ------- Source/Core/IO/Debugging/Error.cs | 59 --- Source/Core/IO/Logging/ConsoleLogger.cs | 153 ------ Source/Core/IO/Logging/ILogger.cs | 76 --- Source/Core/IO/Logging/InMemoryLogger.cs | 130 ----- Source/Core/IO/Logging/LogSeverity.cs | 31 -- Source/Core/IO/Logging/NullLogger.cs | 61 --- Source/Core/IO/Logging/TextWriterLogger.cs | 80 --- Source/Core/Logging/ConsoleLogger.cs | 312 +++++++++++ Source/Core/Logging/ILogger.cs | 173 +++++++ Source/Core/Logging/LogSeverity.cs | 36 ++ Source/Core/Logging/LogWriter.cs | 488 ++++++++++++++++++ Source/Core/Logging/MemoryLogWriter.cs | 244 +++++++++ Source/Core/Logging/MemoryLogger.cs | 221 ++++++++ Source/Core/Logging/NullLogger.cs | 118 +++++ Source/Core/Logging/TextWriterLogger.cs | 163 ++++++ Source/Core/Logging/VerbosityLevel.cs | 41 ++ Source/Core/Runtime/CoyoteRuntime.cs | 159 +++--- Source/Core/Runtime/ICoyoteRuntime.cs | 4 +- Source/Core/Runtime/Logging/IRuntimeLog.cs | 2 +- .../Logging/{LogWriter.cs => LogManager.cs} | 90 +--- .../Logging/RuntimeLogTextFormatter.cs | 62 +-- Source/Core/Runtime/RuntimeProvider.cs | 28 +- .../ControlledSynchronizationContext.cs | 2 +- .../Scheduling/ControlledTaskScheduler.cs | 2 +- .../Runtime/Scheduling/OperationScheduler.cs | 21 +- .../Core/Specifications/Monitors/Monitor.cs | 69 +-- Source/Core/Testing/ExplorationStrategy.cs | 20 +- .../Testing/Fuzzing/BoundedRandomStrategy.cs | 4 +- .../Core/Testing/Fuzzing/FuzzingStrategy.cs | 12 +- .../Testing/Fuzzing/PrioritizationStrategy.cs | 4 +- Source/Core/Testing/Fuzzing/RandomStrategy.cs | 4 +- .../Core/Testing/Interleaving/DFSStrategy.cs | 71 +-- .../Interleaving/InterleavingStrategy.cs | 25 +- .../Interleaving/PrioritizationStrategy.cs | 36 +- .../ProbabilisticRandomStrategy.cs | 4 +- .../Testing/Interleaving/QLearningStrategy.cs | 4 +- .../Testing/Interleaving/RandomStrategy.cs | 4 +- Source/Core/Utilities/Documentation.cs | 41 ++ Source/Test/Rewriting/AssemblyInfo.cs | 9 +- .../Rewriting/Passes/AssemblyDiffingPass.cs | 10 +- Source/Test/Rewriting/Passes/Pass.cs | 14 +- .../Rewriting/ExceptionFilterRewritingPass.cs | 9 +- .../InterAssemblyInvocationRewritingPass.cs | 16 +- .../Passes/Rewriting/MSTestRewritingPass.cs | 20 +- .../Passes/Rewriting/RewritingPass.cs | 6 +- .../Types/MemberTypeRewritingPass.cs | 18 +- .../Types/MethodBodyTypeRewritingPass.cs | 31 +- .../Rewriting/Types/TypeRewritingPass.cs | 6 +- .../UncontrolledInvocationRewritingPass.cs | 8 +- Source/Test/Rewriting/RewritingEngine.cs | 57 +- .../Types/Net/Http/HttpRequestMessage.cs | 4 +- .../AsyncTaskMethodBuilder.cs | 12 +- .../AsyncValueTaskMethodBuilder.cs | 12 +- .../Test/Rewriting/Types/Threading/Monitor.cs | 6 +- .../Types/Web/RequestControllerMiddleware.cs | 4 +- .../Reports/UncontrolledInvocationsReport.cs | 4 +- .../Test/SystematicTesting/TestMethodInfo.cs | 41 +- .../Test/SystematicTesting/TestingEngine.cs | 288 ++++------- Source/Test/Telemetry/TelemetryClient.cs | 23 +- ...untimeLogTests.cs => ActorLoggingTests.cs} | 6 +- .../Runtime/EntryPointTests.cs | 6 +- Tests/Tests.Actors/BaseActorTest.cs | 4 +- Tests/Tests.Actors/Common/CustomLogger.cs | 106 ---- .../EventQueues/EventQueueStressTests.cs | 6 +- .../EventQueues/EventQueueTests.cs | 14 +- .../EventQueues/TestEventQueue.cs | 2 +- .../Tests.Actors/Logging/ActorLoggingTests.cs | 143 +++++ .../Logging/BaseActorLoggingTests.cs | 116 +++++ .../Logging/CustomActorRuntimeLog.cs | 3 +- .../Logging/CustomActorRuntimeLogTests.cs | 445 ---------------- .../CustomActorRuntimeLogTextFormatter.cs | 64 +++ .../Tests.Actors/Logging/GraphLoggingTests.cs | 208 ++++++++ Tests/Tests.Actors/Logging/RuntimeLogTests.cs | 50 ++ .../Logging/RuntimeLogTextFormattingTests.cs | 148 ++++++ .../Operations/CreateActorIdFromNameTests.cs | 6 +- .../Operations/SendAndExecuteTests.cs | 1 - .../Runtime/OnEventDroppedTests.cs | 1 - Tests/Tests.Actors/Timers/TimerTests.cs | 2 +- .../Tasks/Join/TaskWhenAnyTests.cs | 8 +- .../Tasks/Logging/CustomTaskLogTests.cs | 126 ----- .../Tasks/TaskInterleavingsTests.cs | 18 +- Tests/Tests.Common/BaseTest.cs | 108 ++-- .../Logging/CustomActorRuntimeLogSubclass.cs | 34 -- Tests/Tests.Common/TestOutputLogger.cs | 83 ++- .../Text/ConsoleOutputInterceptor.cs | 46 ++ Tests/Tests.Common/Text/StringExtensions.cs | 8 +- .../Logging/ConsoleLoggerTests.cs | 107 ++++ Tests/Tests.Runtime/Logging/LogWriterTests.cs | 167 ++++++ .../Logging/MemoryLogWriterTests.cs | 195 +++++++ .../Logging/MemoryLoggerTests.cs | 101 ++++ .../Logging/TestingEngineLoggingTests.cs | 242 +++++++++ .../Logging/TextWriterLoggerTests.cs | 109 ++++ .../Logging/VerbosityMessages.cs | 14 + Tests/compare-rewriting-diff-logs.ps1 | 4 +- Tools/Coyote/Cli/CommandLineParser.cs | 138 +++-- Tools/Coyote/Program.cs | 102 ++-- docs/concepts/actors/logging.md | 289 +++++++---- docs/concepts/binary-rewriting.md | 32 +- .../Microsoft.Coyote.Actors/Actor/Logger.md | 2 +- .../ActorRuntimeLogTextFormatter.md | 6 +- .../ActorRuntimeLogTextFormatter.md | 2 +- .../OnCreateStateMachine.md | 2 +- .../OnEventHandlerTerminated.md | 3 +- docs/ref/Microsoft.Coyote.IO/ConsoleLogger.md | 32 -- .../ConsoleLogger/ConsoleLogger.md | 15 - .../ConsoleLogger/Encoding.md | 15 - .../ConsoleLogger/LogLevel.md | 16 - .../ConsoleLogger/TextWriter.md | 15 - .../ConsoleLogger/Write.md | 85 --- .../ConsoleLogger/WriteLine.md | 85 --- .../Microsoft.Coyote.IO/ILogger/TextWriter.md | 15 - docs/ref/Microsoft.Coyote.IO/ILogger/Write.md | 85 --- .../Microsoft.Coyote.IO/ILogger/WriteLine.md | 85 --- .../ref/Microsoft.Coyote.IO/InMemoryLogger.md | 38 -- .../InMemoryLogger/Dispose.md | 15 - .../InMemoryLogger/Encoding.md | 15 - .../InMemoryLogger/InMemoryLogger.md | 15 - .../InMemoryLogger/TextWriter.md | 15 - .../InMemoryLogger/Write.md | 64 --- .../InMemoryLogger/WriteLine.md | 64 --- docs/ref/Microsoft.Coyote.IO/LogSeverity.md | 23 - .../Microsoft.Coyote.IO/TextWriterLogger.md | 27 - .../TextWriterLogger/TextWriter.md | 15 - .../TextWriterLogger/Write.md | 85 --- .../TextWriterLogger/WriteLine.md | 85 --- docs/ref/Microsoft.Coyote.IONamespace.md | 11 - .../ILogger.md | 7 +- .../Microsoft.Coyote.Logging/ILogger/Write.md | 223 ++++++++ .../ILogger/WriteLine.md | 223 ++++++++ .../Microsoft.Coyote.Logging/LogSeverity.md | 24 + .../Microsoft.Coyote.Logging/MemoryLogger.md | 29 ++ .../MemoryLogger/Dispose.md | 15 + .../MemoryLogger/MemoryLogger.md | 16 + .../MemoryLogger}/ToString.md | 8 +- .../MemoryLogger/Write.md | 223 ++++++++ .../MemoryLogger/WriteLine.md | 223 ++++++++ .../TextWriterLogger.md | 24 + .../TextWriterLogger/Dispose.md} | 8 +- .../TextWriterLogger/TextWriterLogger.md | 9 +- .../TextWriterLogger/Write.md | 223 ++++++++ .../TextWriterLogger/WriteLine.md | 223 ++++++++ .../VerbosityLevel.md | 25 + docs/ref/Microsoft.Coyote.LoggingNamespace.md | 11 + .../ICoyoteRuntime.md | 2 +- .../ICoyoteRuntime/Logger.md | 4 +- .../RuntimeLogTextFormatter.md | 9 +- .../RuntimeLogTextFormatter/Logger.md | 10 +- .../RuntimeLogTextFormatter.md | 2 +- .../Monitor/Logger.md | 2 +- .../TestingEngine.md | 4 +- .../TestingEngine/Create.md | 28 +- .../TestingEngine/{Logger.md => SetLogger.md} | 10 +- docs/ref/Microsoft.Coyote.md | 12 +- docs/ref/Microsoft.Coyote/Configuration.md | 7 +- .../Configuration/LogLevel.md | 16 - .../{IsVerbose.md => VerbosityLevel.md} | 7 +- .../WithConsoleLoggingEnabled.md | 19 + .../Configuration/WithDebugLoggingEnabled.md | 19 - .../Configuration/WithTelemetryEnabled.md | 4 + .../Configuration/WithVerbosityEnabled.md | 10 +- docs/ref/toc.yml | 85 ++- mkdocs.yml | 58 +-- 188 files changed, 6495 insertions(+), 4371 deletions(-) delete mode 100644 Samples/BoundedBuffer/BoundedBuffer.sln delete mode 100644 Samples/CoffeeMachineActors/CoffeeMachineActors.sln delete mode 100644 Samples/CoffeeMachineTasks/CoffeeMachineTasks.sln delete mode 100644 Samples/DrinksServingRobotActors/DrinksServingRobotActors.sln delete mode 100644 Samples/HelloWorldActors/HelloWorldActors.sln delete mode 100644 Samples/Monitors/Monitors.sln create mode 100644 Samples/Scripts/run-tests.ps1 delete mode 100644 Samples/Timers/Timers.sln rename Source/Core/Actors/Logging/{ActorLogWriter.cs => ActorLogManager.cs} (96%) delete mode 100644 Source/Core/IO/Debugging/Debug.cs delete mode 100644 Source/Core/IO/Debugging/Error.cs delete mode 100644 Source/Core/IO/Logging/ConsoleLogger.cs delete mode 100644 Source/Core/IO/Logging/ILogger.cs delete mode 100644 Source/Core/IO/Logging/InMemoryLogger.cs delete mode 100644 Source/Core/IO/Logging/LogSeverity.cs delete mode 100644 Source/Core/IO/Logging/NullLogger.cs delete mode 100644 Source/Core/IO/Logging/TextWriterLogger.cs create mode 100644 Source/Core/Logging/ConsoleLogger.cs create mode 100644 Source/Core/Logging/ILogger.cs create mode 100644 Source/Core/Logging/LogSeverity.cs create mode 100644 Source/Core/Logging/LogWriter.cs create mode 100644 Source/Core/Logging/MemoryLogWriter.cs create mode 100644 Source/Core/Logging/MemoryLogger.cs create mode 100644 Source/Core/Logging/NullLogger.cs create mode 100644 Source/Core/Logging/TextWriterLogger.cs create mode 100644 Source/Core/Logging/VerbosityLevel.cs rename Source/Core/Runtime/Logging/{LogWriter.cs => LogManager.cs} (74%) create mode 100644 Source/Core/Utilities/Documentation.cs rename Tests/Tests.Actors.BugFinding/Logging/{CustomActorRuntimeLogTests.cs => ActorLoggingTests.cs} (59%) delete mode 100644 Tests/Tests.Actors/Common/CustomLogger.cs create mode 100644 Tests/Tests.Actors/Logging/ActorLoggingTests.cs create mode 100644 Tests/Tests.Actors/Logging/BaseActorLoggingTests.cs rename Tests/{Tests.Common/Runtime => Tests.Actors}/Logging/CustomActorRuntimeLog.cs (98%) delete mode 100644 Tests/Tests.Actors/Logging/CustomActorRuntimeLogTests.cs create mode 100644 Tests/Tests.Actors/Logging/CustomActorRuntimeLogTextFormatter.cs create mode 100644 Tests/Tests.Actors/Logging/GraphLoggingTests.cs create mode 100644 Tests/Tests.Actors/Logging/RuntimeLogTests.cs create mode 100644 Tests/Tests.Actors/Logging/RuntimeLogTextFormattingTests.cs delete mode 100644 Tests/Tests.BugFinding/Tasks/Logging/CustomTaskLogTests.cs delete mode 100644 Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLogSubclass.cs create mode 100644 Tests/Tests.Common/Text/ConsoleOutputInterceptor.cs create mode 100644 Tests/Tests.Runtime/Logging/ConsoleLoggerTests.cs create mode 100644 Tests/Tests.Runtime/Logging/LogWriterTests.cs create mode 100644 Tests/Tests.Runtime/Logging/MemoryLogWriterTests.cs create mode 100644 Tests/Tests.Runtime/Logging/MemoryLoggerTests.cs create mode 100644 Tests/Tests.Runtime/Logging/TestingEngineLoggingTests.cs create mode 100644 Tests/Tests.Runtime/Logging/TextWriterLoggerTests.cs create mode 100644 Tests/Tests.Runtime/Logging/VerbosityMessages.cs delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Write.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ILogger/TextWriter.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ILogger/Write.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/ILogger/WriteLine.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Write.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/LogSeverity.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/TextWriterLogger.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Write.md delete mode 100644 docs/ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine.md delete mode 100644 docs/ref/Microsoft.Coyote.IONamespace.md rename docs/ref/{Microsoft.Coyote.IO => Microsoft.Coyote.Logging}/ILogger.md (58%) create mode 100644 docs/ref/Microsoft.Coyote.Logging/ILogger/Write.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/ILogger/WriteLine.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/LogSeverity.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/MemoryLogger.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger.md rename docs/ref/{Microsoft.Coyote.IO/InMemoryLogger => Microsoft.Coyote.Logging/MemoryLogger}/ToString.md (52%) create mode 100644 docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Write.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/TextWriterLogger.md rename docs/ref/{Microsoft.Coyote.IO/TextWriterLogger/Encoding.md => Microsoft.Coyote.Logging/TextWriterLogger/Dispose.md} (56%) rename docs/ref/{Microsoft.Coyote.IO => Microsoft.Coyote.Logging}/TextWriterLogger/TextWriterLogger.md (62%) create mode 100644 docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Write.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine.md create mode 100644 docs/ref/Microsoft.Coyote.Logging/VerbosityLevel.md create mode 100644 docs/ref/Microsoft.Coyote.LoggingNamespace.md rename docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine/{Logger.md => SetLogger.md} (58%) delete mode 100644 docs/ref/Microsoft.Coyote/Configuration/LogLevel.md rename docs/ref/Microsoft.Coyote/Configuration/{IsVerbose.md => VerbosityLevel.md} (55%) create mode 100644 docs/ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled.md delete mode 100644 docs/ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled.md diff --git a/.github/workflows/test-coyote.yml b/.github/workflows/test-coyote.yml index 482a19c66..a678b0b59 100644 --- a/.github/workflows/test-coyote.yml +++ b/.github/workflows/test-coyote.yml @@ -96,5 +96,5 @@ jobs: ./Samples/Scripts/build-tests.ps1 -local -nuget shell: pwsh - name: Test Coyote samples - run: dotnet ./Samples/Common/bin/net6.0/TestDriver.dll + run: ./Samples/Scripts/run-tests.ps1 shell: pwsh diff --git a/Samples/BoundedBuffer/BoundedBuffer.sln b/Samples/BoundedBuffer/BoundedBuffer.sln deleted file mode 100644 index 5ba49d26f..000000000 --- a/Samples/BoundedBuffer/BoundedBuffer.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BoundedBuffer", "BoundedBuffer.csproj", "{3DC1B03D-64FA-4C68-A6ED-C97B052F42D2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3DC1B03D-64FA-4C68-A6ED-C97B052F42D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3DC1B03D-64FA-4C68-A6ED-C97B052F42D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3DC1B03D-64FA-4C68-A6ED-C97B052F42D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3DC1B03D-64FA-4C68-A6ED-C97B052F42D2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1A689663-5B26-4A43-A345-87184B3FD04A} - EndGlobalSection -EndGlobal diff --git a/Samples/BoundedBuffer/Program.cs b/Samples/BoundedBuffer/Program.cs index b32b5bb45..b662f62a2 100644 --- a/Samples/BoundedBuffer/Program.cs +++ b/Samples/BoundedBuffer/Program.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Samples.BoundedBuffer diff --git a/Samples/CoffeeMachineActors/CoffeeMachineActors.sln b/Samples/CoffeeMachineActors/CoffeeMachineActors.sln deleted file mode 100644 index 500dd9a4c..000000000 --- a/Samples/CoffeeMachineActors/CoffeeMachineActors.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoffeeMachineActors", "CoffeeMachineActors.csproj", "{553AF466-0FE6-41DE-9EC4-7312877AC238}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Debug|Any CPU.Build.0 = Debug|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Release|Any CPU.ActiveCfg = Release|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {48a2a0b4-4dc1-4256-8e7e-fc0bfe45625e} - EndGlobalSection -EndGlobal diff --git a/Samples/CoffeeMachineTasks/CoffeeMachineTasks.sln b/Samples/CoffeeMachineTasks/CoffeeMachineTasks.sln deleted file mode 100644 index fe0b0c504..000000000 --- a/Samples/CoffeeMachineTasks/CoffeeMachineTasks.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoffeeMachineTasks", "CoffeeMachineTasks.csproj", "{553AF466-0FE6-41DE-9EC4-7312877AC238}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Debug|Any CPU.Build.0 = Debug|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Release|Any CPU.ActiveCfg = Release|Any CPU - {553AF466-0FE6-41DE-9EC4-7312877AC238}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {48a2a0b4-4dc1-4256-8e7e-fc0bfe45625e} - EndGlobalSection -EndGlobal diff --git a/Samples/Common/LogWriter.cs b/Samples/Common/LogWriter.cs index 413509e91..520827454 100644 --- a/Samples/Common/LogWriter.cs +++ b/Samples/Common/LogWriter.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. using System; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; namespace Microsoft.Coyote.Samples.Common { diff --git a/Samples/DrinksServingRobotActors/DrinksServingRobotActors.sln b/Samples/DrinksServingRobotActors/DrinksServingRobotActors.sln deleted file mode 100644 index c8f5a5a9f..000000000 --- a/Samples/DrinksServingRobotActors/DrinksServingRobotActors.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DrinksServingRobotActors", "DrinksServingRobotActors.csproj", "{69E48BFB-0B60-4918-867E-4E49C66FA7C1}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {69E48BFB-0B60-4918-867E-4E49C66FA7C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69E48BFB-0B60-4918-867E-4E49C66FA7C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69E48BFB-0B60-4918-867E-4E49C66FA7C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69E48BFB-0B60-4918-867E-4E49C66FA7C1}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B98B56F1-30C8-4729-80C2-1E51E2539960} - EndGlobalSection -EndGlobal diff --git a/Samples/HelloWorldActors/HelloWorldActors.sln b/Samples/HelloWorldActors/HelloWorldActors.sln deleted file mode 100644 index f48de681d..000000000 --- a/Samples/HelloWorldActors/HelloWorldActors.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldActors", "HelloWorldActors.csproj", "{DB7D74D7-FDF8-4892-B160-0B78B97BCCD0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DB7D74D7-FDF8-4892-B160-0B78B97BCCD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB7D74D7-FDF8-4892-B160-0B78B97BCCD0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB7D74D7-FDF8-4892-B160-0B78B97BCCD0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB7D74D7-FDF8-4892-B160-0B78B97BCCD0}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2E83D53D-7F8C-4408-99F8-A92BFFAA289E} - EndGlobalSection -EndGlobal diff --git a/Samples/Monitors/Monitors.sln b/Samples/Monitors/Monitors.sln deleted file mode 100644 index 66d3bc7b9..000000000 --- a/Samples/Monitors/Monitors.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monitors", "Monitors.csproj", "{6A36321C-BBBA-4860-B954-811BD3EC9A15}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6A36321C-BBBA-4860-B954-811BD3EC9A15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A36321C-BBBA-4860-B954-811BD3EC9A15}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A36321C-BBBA-4860-B954-811BD3EC9A15}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A36321C-BBBA-4860-B954-811BD3EC9A15}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5F920A00-097B-429A-95F8-CF06F695CD1A} - EndGlobalSection -EndGlobal diff --git a/Samples/Scripts/build.ps1 b/Samples/Scripts/build.ps1 index 89dc7a241..4f7f15b8a 100644 --- a/Samples/Scripts/build.ps1 +++ b/Samples/Scripts/build.ps1 @@ -30,23 +30,23 @@ if ($null -eq $sdk_version) { # Build the task-based samples. Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../AccountManager/AccountManager.sln" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../BoundedBuffer/BoundedBuffer.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../BoundedBuffer/BoundedBuffer.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../CoffeeMachineTasks/CoffeeMachineTasks.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../CoffeeMachineTasks/CoffeeMachineTasks.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent # Build the actor samples. -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../HelloWorldActors/HelloWorldActors.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../HelloWorldActors/HelloWorldActors.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../CoffeeMachineActors/CoffeeMachineActors.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../CoffeeMachineActors/CoffeeMachineActors.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../DrinksServingRobotActors/DrinksServingRobotActors.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../DrinksServingRobotActors/DrinksServingRobotActors.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../CloudMessaging/CloudMessaging.sln" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../Timers/Timers.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../Timers/Timers.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent -Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../Monitors/Monitors.sln" ` +Invoke-DotnetBuild -dotnet $dotnet -solution "$PSScriptRoot/../Monitors/Monitors.csproj" ` -config $configuration -local $local.IsPresent -nuget $nuget.IsPresent # Build the web app samples. diff --git a/Samples/Scripts/run-tests.ps1 b/Samples/Scripts/run-tests.ps1 new file mode 100644 index 000000000..5353779f0 --- /dev/null +++ b/Samples/Scripts/run-tests.ps1 @@ -0,0 +1,20 @@ +param( + [string]$dotnet="dotnet" +) + +Import-Module $PSScriptRoot/../../Scripts/common.psm1 -Force +CheckPSVersion + +Write-Comment -prefix "." -text "Testing the Coyote samples" -color "yellow" + +$framework = "net6.0" +$tests = "$PSScriptRoot/../Common/bin/$framework/TestDriver.dll" +if (-not (Test-Path $tests)) { + Write-Error "tests for the Coyote samples not found." + exit +} + +$error_msg = "Failed to test the Coyote samples" +Invoke-ToolCommand -tool $dotnet -cmd $tests -error_msg $error_msg + +Write-Comment -prefix "." -text "Successfully tested the Coyote samples" -color "green" diff --git a/Samples/Timers/Timers.sln b/Samples/Timers/Timers.sln deleted file mode 100644 index 9af68e44b..000000000 --- a/Samples/Timers/Timers.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32505.173 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Timers", "Timers.csproj", "{370CBD49-8CBB-4A6E-A371-3755E09EBF16}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {370CBD49-8CBB-4A6E-A371-3755E09EBF16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {370CBD49-8CBB-4A6E-A371-3755E09EBF16}.Debug|Any CPU.Build.0 = Debug|Any CPU - {370CBD49-8CBB-4A6E-A371-3755E09EBF16}.Release|Any CPU.ActiveCfg = Release|Any CPU - {370CBD49-8CBB-4A6E-A371-3755E09EBF16}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {11FB49D3-BB9D-4591-99D1-89E9BC9C8BCB} - EndGlobalSection -EndGlobal diff --git a/Samples/WebApps/ImageGalleryAspNet/ImageGalleryService/Store/Cosmos/Providers/ClientProvider.cs b/Samples/WebApps/ImageGalleryAspNet/ImageGalleryService/Store/Cosmos/Providers/ClientProvider.cs index a9c884ea6..6f190d80c 100644 --- a/Samples/WebApps/ImageGalleryAspNet/ImageGalleryService/Store/Cosmos/Providers/ClientProvider.cs +++ b/Samples/WebApps/ImageGalleryAspNet/ImageGalleryService/Store/Cosmos/Providers/ClientProvider.cs @@ -12,7 +12,7 @@ namespace ImageGallery.Store.Cosmos /// public class ClientProvider : IClientProvider, IDisposable { - private bool IsDsposed = false; + private bool IsDisposed = false; protected CosmosClient CosmosClient { get; } @@ -47,7 +47,7 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (this.IsDsposed) + if (this.IsDisposed) { return; } @@ -57,7 +57,7 @@ protected virtual void Dispose(bool disposing) this.CosmosClient.Dispose(); } - this.IsDsposed = true; + this.IsDisposed = true; } } } diff --git a/Scripts/common.psm1 b/Scripts/common.psm1 index 0c96bd2d1..395c17f37 100644 --- a/Scripts/common.psm1 +++ b/Scripts/common.psm1 @@ -37,15 +37,16 @@ function Invoke-CoyoteTool([String]$cmd, [String]$dotnet, [String]$framework, [S function Invoke-DotnetBuild([String]$dotnet, [String]$solution, [String]$config, [bool]$local, [bool]$nuget) { Write-Comment -prefix "..." -text "Building $solution" - $nuget_config_file = "$PSScriptRoot/../NuGet.config" $platform = "/p:Platform=`"Any CPU`"" $restore_command = "restore $solution" $build_command = "build -c $config $solution --no-restore" if ($local -and $nuget) { - $restore_command = "$restore_command --configfile $nuget_config_file $platform" + $nuget_config_file = "$PSScriptRoot/../NuGet.config" + $restore_command = "$restore_command --configfile $nuget_config_file /p:UseLocalNugetPackages=true $platform" $build_command = "$build_command /p:UseLocalNugetPackages=true $platform" } elseif ($local) { - $restore_command = "$restore_command --configfile $nuget_config_file $platform" + $nuget_config_file = "$PSScriptRoot/../Samples/NuGet.config" + $restore_command = "$restore_command --configfile $nuget_config_file /p:UseLocalCoyote=true $platform" $build_command = "$build_command /p:UseLocalCoyote=true $platform" } diff --git a/Source/Core/Actors/Actor.cs b/Source/Core/Actors/Actor.cs index f82e9acbb..365bf35d4 100644 --- a/Source/Core/Actors/Actor.cs +++ b/Source/Core/Actors/Actor.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using Microsoft.Coyote.Actors.Coverage; using Microsoft.Coyote.Actors.Timers; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Actors @@ -132,9 +132,9 @@ public EventGroup CurrentEventGroup /// The logger installed to the runtime. /// /// - /// See Logging for more information. + /// See Logging for more information. /// - protected ILogger Logger => this.Context.Logger; + protected ILogger Logger => this.Context.LogWriter; /// /// User-defined hashed state of the actor. Override to improve the @@ -251,6 +251,16 @@ protected internal Task ReceiveEventAsync(params Type[] eventTypes) { this.Assert(this.CurrentStatus is ActorExecutionStatus.Active, "{0} invoked ReceiveEventAsync while halting.", this.Id); this.OnReceiveInvoked(); + + if (eventTypes is null) + { + throw new ArgumentNullException(nameof(eventTypes)); + } + else if (eventTypes.Length is 0) + { + throw new ArgumentException("The set of event types to receive cannot be empty.", nameof(eventTypes)); + } + return this.Inbox.ReceiveEventAsync(eventTypes); } @@ -264,6 +274,16 @@ protected internal Task ReceiveEventAsync(params Tuple [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void OnEnqueueEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Context.LogEnqueuedEvent(this, e, eventGroup, eventInfo); + internal void OnEnqueueEvent(Event e) => this.Context.LogEnqueuedEvent(this, e); /// /// Invoked when an event has been raised. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void OnRaiseEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Context.LogRaisedEvent(this, e, eventGroup, eventInfo); + internal void OnRaiseEvent(Event e) => this.Context.LogRaisedEvent(this, e); /// /// Invoked when the actor is waiting for the specified task to complete. @@ -1010,7 +1029,7 @@ internal void OnWaitEvent(IEnumerable eventTypes) /// /// Invoked when an event that the actor is waiting to receive has been enqueued. /// - internal void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) + internal void OnReceiveEvent(Event e, EventGroup eventGroup) { if (eventGroup != null) { @@ -1018,7 +1037,7 @@ internal void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo eventInfo this.CurrentEventGroup = eventGroup; } - this.Context.LogReceivedEvent(this, e, eventInfo); + this.Context.LogReceivedEvent(this, e); if (this.Context.IsExecutionControlled) { @@ -1030,7 +1049,7 @@ internal void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo eventInfo /// Invoked when an event that the actor is waiting to receive has already been in the /// event queue when the actor invoked the receive statement. /// - internal void OnReceiveEventWithoutWaiting(Event e, EventGroup eventGroup, EventInfo eventInfo) + internal void OnReceiveEventWithoutWaiting(Event e, EventGroup eventGroup) { if (eventGroup != null) { @@ -1038,7 +1057,7 @@ internal void OnReceiveEventWithoutWaiting(Event e, EventGroup eventGroup, Event this.CurrentEventGroup = eventGroup; } - this.Context.LogReceivedEventWithoutWaiting(this, e, eventInfo); + this.Context.LogReceivedEventWithoutWaiting(this, e); } /// @@ -1090,7 +1109,7 @@ private protected bool OnExceptionHandler(Exception ex, string methodName, Event OnExceptionOutcome outcome = this.OnException(ex, methodName, e); if (outcome is OnExceptionOutcome.ThrowException) { - this.Context.LogWriter.LogExceptionThrown(this.Id, this.CurrentStateName, methodName, ex); + this.Context.LogManager.LogExceptionThrown(this.Id, this.CurrentStateName, methodName, ex); return false; } else if (outcome is OnExceptionOutcome.Halt) @@ -1098,7 +1117,7 @@ private protected bool OnExceptionHandler(Exception ex, string methodName, Event this.CurrentStatus = ActorExecutionStatus.Halting; } - this.Context.LogWriter.LogExceptionHandled(this.Id, this.CurrentStateName, methodName, ex); + this.Context.LogManager.LogExceptionHandled(this.Id, this.CurrentStateName, methodName, ex); return true; } @@ -1114,12 +1133,12 @@ private protected bool OnUnhandledEventExceptionHandler(UnhandledEventException OnExceptionOutcome outcome = this.OnException(ex, string.Empty, e); if (outcome is OnExceptionOutcome.ThrowException) { - this.Context.LogWriter.LogExceptionThrown(this.Id, ex.CurrentStateName, string.Empty, ex); + this.Context.LogManager.LogExceptionThrown(this.Id, ex.CurrentStateName, string.Empty, ex); return false; } this.CurrentStatus = ActorExecutionStatus.Halting; - this.Context.LogWriter.LogExceptionHandled(this.Id, ex.CurrentStateName, string.Empty, ex); + this.Context.LogManager.LogExceptionHandled(this.Id, ex.CurrentStateName, string.Empty, ex); return true; } diff --git a/Source/Core/Actors/ActorExecutionContext.cs b/Source/Core/Actors/ActorExecutionContext.cs index ae2e125b4..6a05dc224 100644 --- a/Source/Core/Actors/ActorExecutionContext.cs +++ b/Source/Core/Actors/ActorExecutionContext.cs @@ -7,7 +7,6 @@ #if !DEBUG using System.Diagnostics; #endif -using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -16,7 +15,7 @@ using Microsoft.Coyote.Actors.Mocks; using Microsoft.Coyote.Actors.Timers; using Microsoft.Coyote.Actors.Timers.Mocks; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Microsoft.Coyote.Specifications; @@ -53,40 +52,41 @@ internal class ActorExecutionContext : IActorRuntime internal HashSet EnabledActors; /// - /// Completes when actor quiescence is reached. + /// Data structure containing information regarding testing coverage. /// - internal TaskCompletionSource QuiescenceCompletionSource; + internal readonly CoverageInfo CoverageInfo; /// - /// True if the runtime is waiting for actor quiescence. + /// Responsible for writing to the installed . /// - private bool IsActorQuiescenceAwaited; + internal LogWriter LogWriter => this.Runtime.LogWriter; /// - /// Synchronizes access to the logic checking for actor quiescence. + /// Manages all registered objects. /// - private readonly object QuiescenceSyncObject; + internal readonly ActorLogManager LogManager; + + /// + public ILogger Logger + { + get => this.LogWriter; + set => this.LogWriter.SetLogger(value); + } /// - /// Data structure containing information regarding testing coverage. + /// Completes when actor quiescence is reached. /// - internal readonly CoverageInfo CoverageInfo; + internal TaskCompletionSource QuiescenceCompletionSource; /// - /// Responsible for writing to all registered objects. + /// True if the runtime is waiting for actor quiescence. /// - internal ActorLogWriter LogWriter => this.Runtime.LogWriter as ActorLogWriter; - - /// - public ILogger Logger - { - get => this.Runtime.LogWriter.Logger; + private bool IsActorQuiescenceAwaited; - set - { - using var v = this.Runtime.LogWriter.SetLogger(value); - } - } + /// + /// Synchronizes access to the logic checking for actor quiescence. + /// + private readonly object QuiescenceSyncObject; /// /// True if the actor program is running, else false. @@ -127,15 +127,16 @@ public event OnFailureHandler OnFailure /// /// Initializes a new instance of the class. /// - internal ActorExecutionContext(Configuration configuration, CoyoteRuntime runtime) + internal ActorExecutionContext(Configuration configuration, CoyoteRuntime runtime, ActorLogManager logManager) { this.Configuration = configuration; this.Runtime = runtime; this.ActorMap = new ConcurrentDictionary(); this.EnabledActors = new HashSet(); + this.CoverageInfo = new CoverageInfo(); + this.LogManager = logManager; this.QuiescenceCompletionSource = new TaskCompletionSource(); this.IsActorQuiescenceAwaited = false; - this.CoverageInfo = new CoverageInfo(); this.QuiescenceSyncObject = new object(); } @@ -181,11 +182,11 @@ internal virtual ActorId CreateActor(ActorId id, Type type, string name, Event i Actor actor = this.CreateActor(id, type, name, creator, eventGroup); if (actor is StateMachine) { - this.LogWriter.LogCreateStateMachine(actor.Id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateStateMachine(actor.Id, creator?.Id.Name, creator?.Id.Type); } else { - this.LogWriter.LogCreateActor(actor.Id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateActor(actor.Id, creator?.Id.Name, creator?.Id.Type); } this.OnActorEventHandlerStarted(actor.Id); @@ -204,11 +205,11 @@ internal virtual async Task CreateActorAndExecuteAsync(ActorId id, Type Actor actor = this.CreateActor(id, type, name, creator, eventGroup); if (actor is StateMachine) { - this.LogWriter.LogCreateStateMachine(actor.Id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateStateMachine(actor.Id, creator?.Id.Name, creator?.Id.Type); } else { - this.LogWriter.LogCreateActor(actor.Id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateActor(actor.Id, creator?.Id.Name, creator?.Id.Type); } this.OnActorEventHandlerStarted(actor.Id); @@ -353,13 +354,13 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Even Guid opId = eventGroup is null ? Guid.Empty : eventGroup.Id; if (target is null || target.IsHalted) { - this.LogWriter.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, + this.LogManager.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, (sender as StateMachine)?.CurrentStateName ?? default, e, opId, isTargetHalted: true); this.HandleDroppedEvent(e, targetId); return EnqueueStatus.Dropped; } - this.LogWriter.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, + this.LogManager.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, (sender as StateMachine)?.CurrentStateName ?? default, e, opId, isTargetHalted: false); EnqueueStatus enqueueStatus = target.Enqueue(e, eventGroup, null); @@ -430,7 +431,8 @@ private async Task RunActorEventHandlerAsync(Actor actor, Event initialEvent, bo /// protected void OnActorEventHandlerStarted(ActorId actorId) { - if (this.Runtime.SchedulingPolicy != SchedulingPolicy.None) + if (this.Runtime.SchedulingPolicy != SchedulingPolicy.None || + this.Configuration.IsActorQuiescenceCheckingEnabledOutsideTesting) { lock (this.QuiescenceSyncObject) { @@ -444,7 +446,8 @@ protected void OnActorEventHandlerStarted(ActorId actorId) /// protected void OnActorEventHandlerCompleted(ActorId actorId) { - if (this.Runtime.SchedulingPolicy != SchedulingPolicy.None) + if (this.Runtime.SchedulingPolicy != SchedulingPolicy.None || + this.Configuration.IsActorQuiescenceCheckingEnabledOutsideTesting) { lock (this.QuiescenceSyncObject) { @@ -518,35 +521,21 @@ internal virtual int GetNondeterministicIntegerChoice(int maxValue, string calle /// /// Logs that the specified actor invoked an action. /// - internal virtual void LogInvokedAction(Actor actor, MethodInfo action, string handlingStateName, string currentStateName) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogExecuteAction(actor.Id, handlingStateName, currentStateName, action.Name); - } - } + internal void LogInvokedAction(Actor actor, MethodInfo action, string handlingStateName, string currentStateName) => + this.LogManager.LogExecuteAction(actor.Id, handlingStateName, currentStateName, action.Name); /// /// Logs that the specified actor enqueued an . /// - internal virtual void LogEnqueuedEvent(Actor actor, Event e, EventGroup eventGroup, EventInfo eventInfo) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogEnqueueEvent(actor.Id, e); - } - } + internal void LogEnqueuedEvent(Actor actor, Event e) => this.LogManager.LogEnqueueEvent(actor.Id, e); /// /// Logs that the specified actor dequeued an . /// internal virtual void LogDequeuedEvent(Actor actor, Event e, EventInfo eventInfo, bool isFreshDequeue) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogDequeueEvent(actor.Id, stateName, e); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogDequeueEvent(actor.Id, stateName, e); } /// @@ -560,37 +549,25 @@ internal virtual void LogDefaultEventDequeued(Actor actor) /// /// Logs that the specified actor raised an . /// - internal virtual void LogRaisedEvent(Actor actor, Event e, EventGroup eventGroup, EventInfo eventInfo) + internal void LogRaisedEvent(Actor actor, Event e) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogRaiseEvent(actor.Id, stateName, e); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogRaiseEvent(actor.Id, stateName, e); } /// /// Logs that the specified actor is handling a raised . /// - internal virtual void LogHandleRaisedEvent(Actor actor, Event e) + internal void LogHandleRaisedEvent(Actor actor, Event e) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogHandleRaisedEvent(actor.Id, stateName, e); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogHandleRaisedEvent(actor.Id, stateName, e); } /// /// Logs that the specified actor is handling a raised . /// - internal virtual void LogHandleHaltEvent(Actor actor, int inboxSize) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogHalt(actor.Id, inboxSize); - } - } + internal virtual void LogHandleHaltEvent(Actor actor, int inboxSize) => this.LogManager.LogHalt(actor.Id, inboxSize); /// /// Logs that the specified actor called @@ -604,26 +581,20 @@ internal virtual void LogReceiveCalled(Actor actor) /// /// Logs that the specified actor enqueued an event that it was waiting to receive. /// - internal virtual void LogReceivedEvent(Actor actor, Event e, EventInfo eventInfo) + internal void LogReceivedEvent(Actor actor, Event e) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: true); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: true); } /// /// Logs that the specified actor received an event without waiting because the event /// was already in the inbox when the actor invoked the receive statement. /// - internal virtual void LogReceivedEventWithoutWaiting(Actor actor, Event e, EventInfo eventInfo) + internal virtual void LogReceivedEventWithoutWaiting(Actor actor, Event e) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: false); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: false); } /// @@ -631,54 +602,37 @@ internal virtual void LogReceivedEventWithoutWaiting(Actor actor, Event e, Event /// internal virtual void LogWaitEvent(Actor actor, IEnumerable eventTypes) { - if (this.Configuration.IsVerbose) + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + if (eventTypes.Skip(1).Any()) { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - var eventWaitTypesArray = eventTypes.ToArray(); - if (eventWaitTypesArray.Length is 1) - { - this.LogWriter.LogWaitEvent(actor.Id, stateName, eventWaitTypesArray[0]); - } - else - { - this.LogWriter.LogWaitEvent(actor.Id, stateName, eventWaitTypesArray); - } + this.LogManager.LogWaitEvent(actor.Id, stateName, eventTypes.ToArray()); + } + else + { + this.LogManager.LogWaitEvent(actor.Id, stateName, eventTypes.First()); } } /// /// Logs that the event handler of the specified actor terminated. /// - internal virtual void LogEventHandlerTerminated(Actor actor, DequeueStatus dequeueStatus) + internal void LogEventHandlerTerminated(Actor actor, DequeueStatus dequeueStatus) { - if (this.Configuration.IsVerbose) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : default; - this.LogWriter.LogEventHandlerTerminated(actor.Id, stateName, dequeueStatus); - } + string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; + this.LogManager.LogEventHandlerTerminated(actor.Id, stateName, dequeueStatus); } /// /// Logs that the specified state machine entered a state. /// - internal virtual void LogEnteredState(StateMachine stateMachine) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: true); - } - } + internal void LogEnteredState(StateMachine stateMachine) => + this.LogManager.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: true); /// /// Logs that the specified state machine exited a state. /// - internal virtual void LogExitedState(StateMachine stateMachine) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: false); - } - } + internal void LogExitedState(StateMachine stateMachine) => + this.LogManager.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: false); /// /// Logs that the specified state machine invoked pop. @@ -691,26 +645,16 @@ internal virtual void LogPopState(StateMachine stateMachine) /// /// Logs that the specified state machine invoked an action. /// - internal virtual void LogInvokedOnEntryAction(StateMachine stateMachine, MethodInfo action) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogExecuteAction(stateMachine.Id, stateMachine.CurrentStateName, - stateMachine.CurrentStateName, action.Name); - } - } + internal void LogInvokedOnEntryAction(StateMachine stateMachine, MethodInfo action) => + this.LogManager.LogExecuteAction(stateMachine.Id, stateMachine.CurrentStateName, + stateMachine.CurrentStateName, action.Name); /// /// Logs that the specified state machine invoked an action. /// - internal virtual void LogInvokedOnExitAction(StateMachine stateMachine, MethodInfo action) - { - if (this.Configuration.IsVerbose) - { - this.LogWriter.LogExecuteAction(stateMachine.Id, stateMachine.CurrentStateName, - stateMachine.CurrentStateName, action.Name); - } - } + internal void LogInvokedOnExitAction(StateMachine stateMachine, MethodInfo action) => + this.LogManager.LogExecuteAction(stateMachine.Id, stateMachine.CurrentStateName, + stateMachine.CurrentStateName, action.Name); /// /// Builds the coverage graph information, if any. This information is only available @@ -721,14 +665,14 @@ internal CoverageInfo BuildCoverageInfo() var result = this.CoverageInfo; if (result != null) { - var builder = this.LogWriter.GetLogsOfType() + var builder = this.LogManager.GetLogsOfType() .FirstOrDefault(builder => builder.CollapseInstances); if (builder != null) { result.CoverageGraph = builder.SnapshotGraph(false); } - var eventCoverage = this.LogWriter.GetLogsOfType().FirstOrDefault(); + var eventCoverage = this.LogManager.GetLogsOfType().FirstOrDefault(); if (eventCoverage != null) { result.EventInfo = eventCoverage.EventCoverage; @@ -744,7 +688,7 @@ internal CoverageInfo BuildCoverageInfo() internal Graph GetExecutionGraph() { Graph result = null; - var builder = this.LogWriter.GetLogsOfType() + var builder = this.LogManager.GetLogsOfType() .FirstOrDefault(builder => !builder.CollapseInstances); if (builder != null) { @@ -830,23 +774,10 @@ internal void WrapAndThrowException(Exception exception, string s, params object this.Runtime.WrapAndThrowException(exception, s, args); /// - [Obsolete("Please set the Logger property directory instead of calling this method.")] - public TextWriter SetLogger(TextWriter logger) - { - var result = this.LogWriter.SetLogger(new TextWriterLogger(logger)); - if (result != null) - { - return result.TextWriter; - } - - return null; - } - - /// - public void RegisterLog(IRuntimeLog log) => this.Runtime.RegisterLog(log); + public void RegisterLog(IRuntimeLog log) => this.LogManager.RegisterLog(log, this.LogWriter); /// - public void RemoveLog(IRuntimeLog log) => this.Runtime.RemoveLog(log); + public void RemoveLog(IRuntimeLog log) => this.LogManager.RemoveLog(log); /// /// Returns a task that completes once all actors reach quiescence. @@ -918,8 +849,8 @@ internal sealed class Mock : ActorExecutionContext /// /// Initializes a new instance of the class. /// - internal Mock(Configuration configuration, CoyoteRuntime runtime) - : base(configuration, runtime) + internal Mock(Configuration configuration, CoyoteRuntime runtime, ActorLogManager logManager) + : base(configuration, runtime, logManager) { this.ActorIds = new ConcurrentDictionary(); this.NameValueToActorId = new ConcurrentDictionary(); @@ -1080,11 +1011,11 @@ internal override Actor CreateActor(ActorId id, Type type, string name, Actor cr if (actor is StateMachine) { - this.LogWriter.LogCreateStateMachine(id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateStateMachine(id, creator?.Id.Name, creator?.Id.Type); } else { - this.LogWriter.LogCreateActor(id, creator?.Id.Name, creator?.Id.Type); + this.LogManager.LogCreateActor(id, creator?.Id.Name, creator?.Id.Type); } return actor; @@ -1188,7 +1119,7 @@ private EnqueueStatus EnqueueEvent(ActorId targetId, Event e, Actor sender, Even if (target.IsHalted) { Guid groupId = eventGroup is null ? Guid.Empty : eventGroup.Id; - this.LogWriter.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, + this.LogManager.LogSendEvent(targetId, sender?.Id.Name, sender?.Id.Type, (sender as StateMachine)?.CurrentStateName ?? default, e, groupId, isTargetHalted: true); this.Assert(options is null || !options.MustHandle, "A must-handle event '{0}' was sent to {1} which has halted.", e.GetType().FullName, targetId); @@ -1236,7 +1167,7 @@ private EnqueueStatus EnqueueEvent(Actor actor, Event e, Actor sender, EventGrou }; Guid opId = eventGroup is null ? Guid.Empty : eventGroup.Id; - this.LogWriter.LogSendEvent(actor.Id, sender?.Id.Name, sender?.Id.Type, stateName, + this.LogManager.LogSendEvent(actor.Id, sender?.Id.Name, sender?.Id.Type, stateName, e, opId, isTargetHalted: false); return actor.Enqueue(e, eventGroup, eventInfo); } @@ -1340,15 +1271,6 @@ internal override int GetNondeterministicIntegerChoice(int maxValue, string call return this.Runtime.GetNextNondeterministicIntegerChoice(maxValue, callerName ?? caller?.Id.Name, callerType ?? caller?.Id.Type); } - /// - internal override void LogInvokedAction(Actor actor, MethodInfo action, string handlingStateName, string currentStateName) => - this.LogWriter.LogExecuteAction(actor.Id, handlingStateName, currentStateName, action.Name); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal override void LogEnqueuedEvent(Actor actor, Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.LogWriter.LogEnqueueEvent(actor.Id, e); - /// internal override void LogDequeuedEvent(Actor actor, Event e, EventInfo eventInfo, bool isFreshDequeue) { @@ -1360,8 +1282,7 @@ internal override void LogDequeuedEvent(Actor actor, Event e, EventInfo eventInf this.ResetProgramCounter(actor); } - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogDequeueEvent(actor.Id, stateName, e); + base.LogDequeuedEvent(actor, e, eventInfo, isFreshDequeue); } /// @@ -1371,25 +1292,11 @@ internal override void LogDefaultEventDequeued(Actor actor) this.ResetProgramCounter(actor); } - /// - internal override void LogRaisedEvent(Actor actor, Event e, EventGroup eventGroup, EventInfo eventInfo) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogRaiseEvent(actor.Id, stateName, e); - } - - /// - internal override void LogHandleRaisedEvent(Actor actor, Event e) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogHandleRaisedEvent(actor.Id, stateName, e); - } - /// internal override void LogHandleHaltEvent(Actor actor, int inboxSize) { this.Runtime.ScheduleNextOperation(actor.Operation, SchedulingPointType.Halt); - this.LogWriter.LogHalt(actor.Id, inboxSize); + base.LogHandleHaltEvent(actor, inboxSize); } /// @@ -1397,17 +1304,9 @@ internal override void LogHandleHaltEvent(Actor actor, int inboxSize) internal override void LogReceiveCalled(Actor actor) => this.AssertExpectedCallerActor(actor, "ReceiveEventAsync"); /// - internal override void LogReceivedEvent(Actor actor, Event e, EventInfo eventInfo) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: true); - } - - /// - internal override void LogReceivedEventWithoutWaiting(Actor actor, Event e, EventInfo eventInfo) + internal override void LogReceivedEventWithoutWaiting(Actor actor, Event e) { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogReceiveEvent(actor.Id, stateName, e, wasBlocked: false); + base.LogReceivedEventWithoutWaiting(actor, e); this.Runtime.ScheduleNextOperation(actor.Operation, SchedulingPointType.Receive); this.ResetProgramCounter(actor); } @@ -1415,55 +1314,16 @@ internal override void LogReceivedEventWithoutWaiting(Actor actor, Event e, Even /// internal override void LogWaitEvent(Actor actor, IEnumerable eventTypes) { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - var eventWaitTypesArray = eventTypes.ToArray(); - if (eventWaitTypesArray.Length is 1) - { - this.LogWriter.LogWaitEvent(actor.Id, stateName, eventWaitTypesArray[0]); - } - else - { - this.LogWriter.LogWaitEvent(actor.Id, stateName, eventWaitTypesArray); - } - + base.LogWaitEvent(actor, eventTypes); this.Runtime.ScheduleNextOperation(actor.Operation, SchedulingPointType.Pause); this.ResetProgramCounter(actor); } - /// - internal override void LogEventHandlerTerminated(Actor actor, DequeueStatus dequeueStatus) - { - string stateName = actor is StateMachine stateMachine ? stateMachine.CurrentStateName : null; - this.LogWriter.LogEventHandlerTerminated(actor.Id, stateName, dequeueStatus); - } - - /// - internal override void LogEnteredState(StateMachine stateMachine) => - this.LogWriter.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: true); - - /// - internal override void LogExitedState(StateMachine stateMachine) => - this.LogWriter.LogStateTransition(stateMachine.Id, stateMachine.CurrentStateName, isEntry: false); - /// internal override void LogPopState(StateMachine stateMachine) { this.AssertExpectedCallerActor(stateMachine, "Pop"); - this.LogWriter.LogPopState(stateMachine.Id, default, stateMachine.CurrentStateName); - } - - /// - internal override void LogInvokedOnEntryAction(StateMachine stateMachine, MethodInfo action) - { - string stateName = stateMachine.CurrentStateName; - this.LogWriter.LogExecuteAction(stateMachine.Id, stateName, stateName, action.Name); - } - - /// - internal override void LogInvokedOnExitAction(StateMachine stateMachine, MethodInfo action) - { - string stateName = stateMachine.CurrentStateName; - this.LogWriter.LogExecuteAction(stateMachine.Id, stateName, stateName, action.Name); + this.LogManager.LogPopState(stateMachine.Id, default, stateMachine.CurrentStateName); } /// diff --git a/Source/Core/Actors/Coverage/ActorRuntimeLogGraphBuilder.cs b/Source/Core/Actors/Coverage/ActorRuntimeLogGraphBuilder.cs index 9f1ab5f91..924be7740 100644 --- a/Source/Core/Actors/Coverage/ActorRuntimeLogGraphBuilder.cs +++ b/Source/Core/Actors/Coverage/ActorRuntimeLogGraphBuilder.cs @@ -67,7 +67,7 @@ private class EventInfo /// Get or set the underlying logging object. /// /// - /// See Logging for more information. + /// See Logging for more information. /// internal TextWriter Logger { get; set; } diff --git a/Source/Core/Actors/EventQueues/EventQueue.cs b/Source/Core/Actors/EventQueues/EventQueue.cs index 9cb6008ee..8eb1358f1 100644 --- a/Source/Core/Actors/EventQueues/EventQueue.cs +++ b/Source/Core/Actors/EventQueues/EventQueue.cs @@ -292,14 +292,14 @@ private Task ReceiveEventAsync(Dictionary> eventW /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnEnqueueEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnEnqueueEvent(e, eventGroup, eventInfo); + this.Owner.OnEnqueueEvent(e); /// /// Notifies the actor that an event has been raised. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnRaiseEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnRaiseEvent(e, eventGroup, eventInfo); + this.Owner.OnRaiseEvent(e); /// /// Notifies the actor that it is waiting to receive an event of one of the specified types. @@ -312,7 +312,7 @@ protected virtual void OnRaiseEvent(Event e, EventGroup eventGroup, EventInfo ev /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnReceiveEvent(e, eventGroup, eventInfo); + this.Owner.OnReceiveEvent(e, eventGroup); /// /// Notifies the actor that an event it was waiting to receive was already in the @@ -320,7 +320,7 @@ protected virtual void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnReceiveEventWithoutWaiting(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnReceiveEventWithoutWaiting(e, eventGroup, eventInfo); + this.Owner.OnReceiveEventWithoutWaiting(e, eventGroup); /// /// Notifies the actor that an event has been ignored. diff --git a/Source/Core/Actors/EventQueues/MockEventQueue.cs b/Source/Core/Actors/EventQueues/MockEventQueue.cs index ddf71ebb2..9202d0ae2 100644 --- a/Source/Core/Actors/EventQueues/MockEventQueue.cs +++ b/Source/Core/Actors/EventQueues/MockEventQueue.cs @@ -329,14 +329,14 @@ protected virtual bool IsDefaultHandlerAvailable() /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnEnqueueEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnEnqueueEvent(e, eventGroup, eventInfo); + this.Owner.OnEnqueueEvent(e); /// /// Notifies the actor that an event has been raised. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnRaiseEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnRaiseEvent(e, eventGroup, eventInfo); + this.Owner.OnRaiseEvent(e); /// /// Notifies the actor that it is waiting to receive an event of one of the specified types. @@ -349,7 +349,7 @@ protected virtual void OnRaiseEvent(Event e, EventGroup eventGroup, EventInfo ev /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnReceiveEvent(e, eventGroup, eventInfo); + this.Owner.OnReceiveEvent(e, eventGroup); /// /// Notifies the actor that an event it was waiting to receive was already in the @@ -357,7 +357,7 @@ protected virtual void OnReceiveEvent(Event e, EventGroup eventGroup, EventInfo /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void OnReceiveEventWithoutWaiting(Event e, EventGroup eventGroup, EventInfo eventInfo) => - this.Owner.OnReceiveEventWithoutWaiting(e, eventGroup, eventInfo); + this.Owner.OnReceiveEventWithoutWaiting(e, eventGroup); /// /// Notifies the actor that or one of its overloaded methods was invoked. diff --git a/Source/Core/Actors/IActorRuntime.cs b/Source/Core/Actors/IActorRuntime.cs index c2cf4f535..4b8832d8a 100644 --- a/Source/Core/Actors/IActorRuntime.cs +++ b/Source/Core/Actors/IActorRuntime.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; -using Microsoft.Coyote.IO; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Actors @@ -194,18 +192,5 @@ Task CreateActorAndExecuteAsync(ActorId id, Type type, Event initialEve /// This method is not thread-safe. /// int GetCurrentActorCount(); - - /// - /// The old way of setting the property. - /// - /// - /// The new way is to just set the Logger property to an object. - /// This method is only here for compatibility and has a minor perf impact as it has to - /// wrap the writer in an object that implements the interface. - /// - /// The writer to use for logging. - /// The previously installed logger. - [Obsolete("Please set the Logger property directly instead of calling this method.")] - TextWriter SetLogger(TextWriter writer); } } diff --git a/Source/Core/Actors/Logging/ActorLogWriter.cs b/Source/Core/Actors/Logging/ActorLogManager.cs similarity index 96% rename from Source/Core/Actors/Logging/ActorLogWriter.cs rename to Source/Core/Actors/Logging/ActorLogManager.cs index f993f4e43..fe72f0cb9 100644 --- a/Source/Core/Actors/Logging/ActorLogWriter.cs +++ b/Source/Core/Actors/Logging/ActorLogManager.cs @@ -2,27 +2,16 @@ // Licensed under the MIT License. using System; -using System.IO; -using System.Linq; using Microsoft.Coyote.Actors.Timers; -using Microsoft.Coyote.IO; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Actors { /// - /// Manages the installed and all registered objects. + /// Manages all registered objects. /// - internal sealed class ActorLogWriter : LogWriter + internal sealed class ActorLogManager : LogManager { - /// - /// Initializes a new instance of the class. - /// - internal ActorLogWriter(Configuration configuration, ActorRuntimeLogTextFormatter textFormatter) - : base(configuration, textFormatter) - { - } - /// /// Logs that the specified actor has been created. /// @@ -488,12 +477,5 @@ internal void LogStopTimer(TimerInfo info) } } } - - /// - protected override RuntimeLogTextFormatter CreateLogTextFormatter(ILogger logger) => - new ActorRuntimeLogTextFormatter() - { - Logger = logger - }; } } diff --git a/Source/Core/Actors/Logging/ActorRuntimeLogTextFormatter.cs b/Source/Core/Actors/Logging/ActorRuntimeLogTextFormatter.cs index 8762a2e45..81a5e14be 100644 --- a/Source/Core/Actors/Logging/ActorRuntimeLogTextFormatter.cs +++ b/Source/Core/Actors/Logging/ActorRuntimeLogTextFormatter.cs @@ -4,7 +4,7 @@ using System; using System.Threading; using Microsoft.Coyote.Actors.Timers; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Actors @@ -17,80 +17,76 @@ namespace Microsoft.Coyote.Actors /// public class ActorRuntimeLogTextFormatter : RuntimeLogTextFormatter, IActorRuntimeLog { - /// - /// Initializes a new instance of the class. - /// - public ActorRuntimeLogTextFormatter() - : base() - { - } - /// public virtual void OnCreateActor(ActorId id, string creatorName, string creatorType) { - var source = creatorName ?? $"thread '{Thread.CurrentThread.ManagedThreadId}'"; - var text = $" {id} was created by {source}."; - this.Logger.WriteLine(text); + if (creatorName is null) + { + this.Logger.WriteLine(" {0} was created by thread '{1}'.", id, Thread.CurrentThread.ManagedThreadId); + } + else + { + this.Logger.WriteLine(" {0} was created by {1}.", id, creatorName); + } } /// - public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) + public virtual void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) { - var source = creatorName ?? $"thread '{Thread.CurrentThread.ManagedThreadId}'"; - var text = $" {id} was created by {source}."; - this.Logger.WriteLine(text); + if (creatorName is null) + { + this.Logger.WriteLine(" {0} was created by thread '{1}'.", id, Thread.CurrentThread.ManagedThreadId); + } + else + { + this.Logger.WriteLine(" {0} was created by {1}.", id, creatorName); + } } /// public virtual void OnCreateTimer(TimerInfo info) { - string text; var source = info.OwnerId?.Name ?? $"thread '{Thread.CurrentThread.ManagedThreadId}'"; if (info.Period.TotalMilliseconds >= 0) { - text = $" Timer '{info}' (due-time:{info.DueTime.TotalMilliseconds}ms; " + - $"period :{info.Period.TotalMilliseconds}ms) was created by {source}."; + this.Logger.WriteLine(" Timer '{0}' (due-time:{1}ms; period:{2}ms) was created by {3}.", + info, info.DueTime.TotalMilliseconds, info.Period.TotalMilliseconds, source); } else { - text = $" Timer '{info}' (due-time:{info.DueTime.TotalMilliseconds}ms) was created by {source}."; + this.Logger.WriteLine(" Timer '{0}' (due-time:{1}ms) was created by {2}.", + info, info.DueTime.TotalMilliseconds, source); } - - this.Logger.WriteLine(text); } /// public virtual void OnDefaultEventHandler(ActorId id, string stateName) { - string text; if (stateName is null) { - text = $" {id} is executing the default handler."; + this.Logger.WriteLine(" {0} is executing the default handler.", id); } else { - text = $" {id} is executing the default handler in state '{stateName}'."; + this.Logger.WriteLine(" {0} is executing the default handler in state '{1}'.", id, stateName); } - - this.Logger.WriteLine(text); } /// - public void OnEventHandlerTerminated(ActorId id, string stateName, DequeueStatus dequeueStatus) + public virtual void OnEventHandlerTerminated(ActorId id, string stateName, DequeueStatus dequeueStatus) { if (dequeueStatus != DequeueStatus.Unavailable) { - string text; if (stateName is null) { - text = $" The event handler of {id} terminated with '{dequeueStatus}' dequeue status."; + this.Logger.WriteLine(" The event handler of {0} terminated with '{1}' dequeue status.", + id, dequeueStatus); } else { - text = $" The event handler of {id} terminated in state '{stateName}' with '{dequeueStatus}' dequeue status."; + this.Logger.WriteLine(" The event handler of {0} terminated in state '{1}' with '{2}' dequeue status.", + id, stateName, dequeueStatus); } - - this.Logger.WriteLine(text); } } @@ -98,167 +94,147 @@ public void OnEventHandlerTerminated(ActorId id, string stateName, DequeueStatus public virtual void OnDequeueEvent(ActorId id, string stateName, Event e) { string eventName = e.GetType().FullName; - string text; if (string.IsNullOrEmpty(stateName)) { - text = $" {id} dequeued event '{eventName}'."; + this.Logger.WriteLine(" {0} dequeued event '{1}'.", id, eventName); } else { - text = $" {id} dequeued event '{eventName}' in state '{stateName}'."; + this.Logger.WriteLine(" {0} dequeued event '{1}' in state '{2}'.", id, eventName, stateName); } - - this.Logger.WriteLine(text); } /// - public virtual void OnEnqueueEvent(ActorId id, Event e) - { - string eventName = e.GetType().FullName; - string text = $" {id} enqueued event '{eventName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnEnqueueEvent(ActorId id, Event e) => + this.Logger.WriteLine(" {0} enqueued event '{1}'.", id, e.GetType().FullName); /// public virtual void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex) { - string text; if (stateName is null) { - text = $" {id} running action '{actionName}' chose to handle exception '{ex.GetType().Name}'."; + this.Logger.WriteLine(LogSeverity.Warning, " {0} running action '{1}' chose to handle exception '{2}'.", + id, actionName, ex.GetType().Name); } else { - text = $" {id} running action '{actionName}' in state '{stateName}' chose to handle exception '{ex.GetType().Name}'."; + this.Logger.WriteLine(LogSeverity.Warning, " {0} running action '{1}' in state '{2}' chose to handle exception '{3}'.", + id, actionName, stateName, ex.GetType().Name); } - - this.Logger.WriteLine(LogSeverity.Warning, text); } /// public virtual void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex) { - string text; if (stateName is null) { - text = $" {id} running action '{actionName}' threw exception '{ex.GetType().Name}'."; + this.Logger.WriteLine(LogSeverity.Warning, " {0} running action '{1}' threw exception '{2}'.", + id, actionName, ex.GetType().Name); } else { - text = $" {id} running action '{actionName}' in state '{stateName}' threw exception '{ex.GetType().Name}'."; + this.Logger.WriteLine(LogSeverity.Warning, " {0} running action '{1}' in state '{2}' threw exception '{3}'.", + id, actionName, stateName, ex.GetType().Name); } - - this.Logger.WriteLine(LogSeverity.Warning, text); } /// public virtual void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) { - string text; if (currentStateName is null) { - text = $" {id} invoked action '{actionName}'."; + this.Logger.WriteLine(" {0} invoked action '{1}'.", id, actionName); } else if (handlingStateName != currentStateName) { - text = $" {id} invoked action '{actionName}' in state '{currentStateName}' where action was declared by state '{handlingStateName}'."; + this.Logger.WriteLine(" {0} invoked action '{1}' in state '{2}' where action was declared by state '{3}'.", + id, actionName, currentStateName, handlingStateName); } else { - text = $" {id} invoked action '{actionName}' in state '{currentStateName}'."; + this.Logger.WriteLine(" {0} invoked action '{1}' in state '{2}'.", + id, actionName, currentStateName); } - - this.Logger.WriteLine(text); } /// - public virtual void OnGotoState(ActorId id, string currentStateName, string newStateName) - { - string text = $" {id} is transitioning from state '{currentStateName}' to state '{newStateName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnGotoState(ActorId id, string currentStateName, string newStateName) => + this.Logger.WriteLine(" {0} is transitioning from state '{1}' to state '{2}'.", + id, currentStateName, newStateName); /// public virtual void OnHalt(ActorId id, int inboxSize) { - string text = $" {id} halted with {inboxSize} events in its inbox."; - this.Logger.WriteLine(text); + if (inboxSize is 1) + { + this.Logger.WriteLine(" {0} halted with '{1}' event in its inbox.", id, inboxSize); + } + else + { + this.Logger.WriteLine(" {0} halted with '{1}' events in its inbox.", id, inboxSize); + } } /// public virtual void OnPopState(ActorId id, string currentStateName, string restoredStateName) { currentStateName = string.IsNullOrEmpty(currentStateName) ? "[not recorded]" : currentStateName; - var reenteredStateName = restoredStateName ?? string.Empty; - var text = $" {id} popped state '{currentStateName}' and reentered state '{reenteredStateName}'."; - this.Logger.WriteLine(text); + this.Logger.WriteLine(" {0} popped state '{1}' and reentered state '{2}'.", + id, currentStateName, restoredStateName ?? string.Empty); } /// - public virtual void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) - { - string eventName = e.GetType().FullName; - var text = $" {id} popped state {stateName} due to unhandled event '{eventName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnPopStateUnhandledEvent(ActorId id, string stateName, Event e) => + this.Logger.WriteLine(" {0} popped state '{1}' due to unhandled event '{2}'.", + id, stateName, e.GetType().FullName); /// - public virtual void OnPushState(ActorId id, string currentStateName, string newStateName) - { - string text = $" {id} pushed from state '{currentStateName}' to state '{newStateName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnPushState(ActorId id, string currentStateName, string newStateName) => + this.Logger.WriteLine(" {0} pushed from state '{1}' to state '{2}'.", + id, currentStateName, newStateName); /// public virtual void OnRaiseEvent(ActorId id, string stateName, Event e) { - string eventName = e.GetType().FullName; - string text; if (stateName is null) { - text = $" {id} raised event '{eventName}'."; + this.Logger.WriteLine(" {0} raised event '{1}'.", id, e.GetType().FullName); } else { - text = $" {id} raised event '{eventName}' in state '{stateName}'."; + this.Logger.WriteLine(" {0} raised event '{1}' in state '{2}'.", + id, e.GetType().FullName, stateName); } - - this.Logger.WriteLine(text); } /// public virtual void OnHandleRaisedEvent(ActorId id, string stateName, Event e) { - string eventName = e.GetType().FullName; - string text; if (stateName is null) { - text = $" {id} is handling the raised event '{eventName}'."; + this.Logger.WriteLine(" {0} is handling the raised event '{1}'.", id, e.GetType().FullName); } else { - text = $" {id} is handling the raised event '{eventName}' in state '{stateName}'."; + this.Logger.WriteLine(" {0} is handling the raised event '{1}' in state '{2}'.", + id, e.GetType().FullName, stateName); } - - this.Logger.WriteLine(text); } /// public virtual void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked) { - string eventName = e.GetType().FullName; - string text; var unblocked = wasBlocked ? " and unblocked" : string.Empty; if (stateName is null) { - text = $" {id} dequeued event '{eventName}'{unblocked}."; + this.Logger.WriteLine(" {0} dequeued event '{1}'{2}.", id, + e.GetType().FullName, unblocked); } else { - text = $" {id} dequeued event '{eventName}'{unblocked} in state '{stateName}'."; + this.Logger.WriteLine(" {0} dequeued event '{1}'{2} in state '{3}'.", + id, e.GetType().FullName, unblocked, stateName); } - - this.Logger.WriteLine(text); } /// @@ -271,46 +247,46 @@ public virtual void OnSendEvent(ActorId targetActorId, string senderName, string senderStateName != null ? $"{senderName} in state '{senderStateName}'" : $"{senderName}" : $"Thread '{Thread.CurrentThread.ManagedThreadId}'"; var eventName = e.GetType().FullName; - var text = $" {sender} sent event '{eventName}' to {targetActorId}{isHalted}{eventGroupIdMsg}."; - this.Logger.WriteLine(text); + this.Logger.WriteLine(" {0} sent event '{1}' to {2}{3}{4}.", + sender, eventName, targetActorId, isHalted, eventGroupIdMsg); } /// - public virtual void OnStateTransition(ActorId id, string stateName, bool isEntry) - { - var direction = isEntry ? "enters" : "exits"; - var text = $" {id} {direction} state '{stateName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnStateTransition(ActorId id, string stateName, bool isEntry) => + this.Logger.WriteLine(" {0} {1} state '{2}'.", id, isEntry ? "enters" : "exits", stateName); /// public virtual void OnStopTimer(TimerInfo info) { - var source = info.OwnerId?.Name ?? $"thread '{Thread.CurrentThread.ManagedThreadId}'"; - var text = $" Timer '{info}' was stopped and disposed by {source}."; - this.Logger.WriteLine(text); + if (info.OwnerId is null) + { + this.Logger.WriteLine(" Timer '{0}' was stopped and disposed by thread '{1}'.", + info, Thread.CurrentThread.ManagedThreadId); + } + else + { + this.Logger.WriteLine(" Timer '{0}' was stopped and disposed by {1}.", info, info.OwnerId.Name); + } } /// public virtual void OnWaitEvent(ActorId id, string stateName, Type eventType) { - string text; if (stateName is null) { - text = $" {id} is waiting to dequeue an event of type '{eventType.FullName}'."; + this.Logger.WriteLine(" {0} is waiting to dequeue an event of type '{1}'.", + id, eventType.FullName); } else { - text = $" {id} is waiting to dequeue an event of type '{eventType.FullName}' in state '{stateName}'."; + this.Logger.WriteLine(" {0} is waiting to dequeue an event of type '{1}' in state '{2}'.", + id, eventType.FullName, stateName); } - - this.Logger.WriteLine(text); } /// public virtual void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes) { - string text; string eventNames; if (eventTypes.Length is 0) { @@ -341,14 +317,14 @@ public virtual void OnWaitEvent(ActorId id, string stateName, params Type[] even if (stateName is null) { - text = $" {id} is waiting to dequeue an event of type {eventNames}."; + this.Logger.WriteLine(" {0} is waiting to dequeue an event of type {1}.", + id, eventNames); } else { - text = $" {id} is waiting to dequeue an event of type {eventNames} in state '{stateName}'."; + this.Logger.WriteLine(" {0} is waiting to dequeue an event of type {1} in state '{2}'.", + id, eventNames, stateName); } - - this.Logger.WriteLine(text); } } } diff --git a/Source/Core/Actors/RuntimeFactory.cs b/Source/Core/Actors/RuntimeFactory.cs index 7a74b9225..0960ce027 100644 --- a/Source/Core/Actors/RuntimeFactory.cs +++ b/Source/Core/Actors/RuntimeFactory.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Runtime; + namespace Microsoft.Coyote.Actors { /// @@ -27,7 +30,22 @@ public static class RuntimeFactory /// Only one actor runtime can be used per process. If you create a new actor runtime /// it replaces the previously installed one. This is a thread-safe operation. /// - public static IActorRuntime Create(Configuration configuration) => - Runtime.RuntimeProvider.CreateAndInstall(configuration).DefaultActorExecutionContext; + public static IActorRuntime Create(Configuration configuration) + { + configuration ??= Configuration.Create(); + var logWriter = new LogWriter(configuration); + var logManager = CreateLogManager(logWriter); + return Runtime.RuntimeProvider.CreateAndInstall(configuration, logWriter, logManager).DefaultActorExecutionContext; + } + + /// + /// Creates a new runtime log manager that writes to the specified log writer. + /// + internal static LogManager CreateLogManager(LogWriter logWriter) + { + var logManager = new ActorLogManager(); + logManager.RegisterLog(new ActorRuntimeLogTextFormatter(), logWriter); + return logManager; + } } } diff --git a/Source/Core/Actors/StateMachine.cs b/Source/Core/Actors/StateMachine.cs index 2066a2f3f..61b126f18 100644 --- a/Source/Core/Actors/StateMachine.cs +++ b/Source/Core/Actors/StateMachine.cs @@ -381,7 +381,7 @@ private protected override async Task HandleEventAsync(Event e) await this.ExecuteCurrentStateOnExitAsync(null, e); if (this.CurrentStatus is ActorExecutionStatus.Active) { - this.Context.LogWriter.LogPopStateUnhandledEvent(this.Id, this.CurrentStateName, e); + this.Context.LogManager.LogPopStateUnhandledEvent(this.Id, this.CurrentStateName, e); this.DoStatePop(); continue; } @@ -513,7 +513,7 @@ private async Task ApplyEventHandlerTransitionAsync(Transition transition, Event if (this.CurrentStatus is ActorExecutionStatus.Active) { this.DoStatePop(); - this.Context.LogWriter.LogPopState(this.Id, prevStateName, this.CurrentStateName); + this.Context.LogManager.LogPopState(this.Id, prevStateName, this.CurrentStateName); this.Assert(this.CurrentState != null, "{0} popped its state with no matching push state.", this.Id); } } @@ -565,7 +565,7 @@ private void CheckDanglingTransition() /// private async Task GotoStateAsync(Type s, string onExitActionName, Event e) { - this.Context.LogWriter.LogGotoState(this.Id, this.CurrentStateName, + this.Context.LogManager.LogGotoState(this.Id, this.CurrentStateName, $"{s.DeclaringType}.{NameResolver.GetStateNameForLogging(s)}"); // The state machine performs the on exit action of the current state. @@ -588,7 +588,7 @@ private async Task GotoStateAsync(Type s, string onExitActionName, Event e) /// private async Task PushStateAsync(Type s, Event e) { - this.Context.LogWriter.LogPushState(this.Id, this.CurrentStateName, s.FullName); + this.Context.LogManager.LogPushState(this.Id, this.CurrentStateName, s.FullName); var nextState = StateInstanceCache[this.GetType()].First(val => val.GetType().Equals(s)); this.DoStatePush(nextState); diff --git a/Source/Core/Configuration.cs b/Source/Core/Configuration.cs index 8470d14c9..e16ef5de8 100644 --- a/Source/Core/Configuration.cs +++ b/Source/Core/Configuration.cs @@ -3,7 +3,7 @@ using System; using System.Runtime.Serialization; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote @@ -203,10 +203,17 @@ public class Configuration internal bool IsProgramStateHashingEnabled; /// - /// If this option is enabled, (safety) monitors are used in the production runtime. + /// If this option is enabled, safety monitors can run outside the scope of the testing engine. /// [DataMember] - internal bool IsMonitoringEnabledInInProduction; + internal bool IsMonitoringEnabledOutsideTesting { get; private set; } + + /// + /// If this option is enabled, the runtime can check for actor quiescence outside + /// the scope of the testing engine. + /// + [DataMember] + internal bool IsActorQuiescenceCheckingEnabledOutsideTesting; /// /// Attaches the debugger during trace replay. @@ -220,22 +227,14 @@ public class Configuration internal string ReproducibleTrace { get; private set; } /// - /// If true, then messages are logged. + /// The level of verbosity to use during logging. /// - [DataMember] - public bool IsVerbose { get; internal set; } + public VerbosityLevel VerbosityLevel { get; private set; } /// - /// If true, then debug verbosity is enabled. + /// If true, then console logging is enabled, else false. /// - [DataMember] - internal bool IsDebugVerbosityEnabled; - - /// - /// The level of detail to provide in verbose logging. - /// - [DataMember] - public LogSeverity LogLevel { get; internal set; } + internal bool IsConsoleLoggingEnabled { get; private set; } /// /// Enables activity coverage reporting of a Coyote program. @@ -244,7 +243,7 @@ public class Configuration internal bool IsActivityCoverageReported; /// - /// If specified, requests a DGML graph of the iteration that contains a bug, if a bug is found. + /// If true, requests a DGML graph of the iteration that contains a bug, if a bug is found. /// This is different from a coverage activity graph, as it will also show actor instances. /// [DataMember] @@ -271,6 +270,9 @@ protected Configuration() this.TestMethodName = string.Empty; this.ReproducibleTrace = string.Empty; + this.VerbosityLevel = VerbosityLevel.None; + this.IsConsoleLoggingEnabled = false; + this.SchedulingStrategy = "random"; this.TestingIterations = 1; this.TestingTimeout = 0; @@ -296,17 +298,14 @@ protected Configuration() this.LivenessTemperatureThreshold = 50000; this.UserExplicitlySetLivenessTemperatureThreshold = false; this.IsProgramStateHashingEnabled = false; - this.IsMonitoringEnabledInInProduction = false; + this.IsMonitoringEnabledOutsideTesting = false; + this.IsActorQuiescenceCheckingEnabledOutsideTesting = false; this.AttachDebugger = false; this.IsActivityCoverageReported = false; this.IsTraceVisualizationEnabled = false; this.IsXmlLogEnabled = false; - this.IsVerbose = false; - this.IsDebugVerbosityEnabled = false; - this.LogLevel = LogSeverity.Informational; - string optout = Environment.GetEnvironmentVariable("COYOTE_CLI_TELEMETRY_OPTOUT"); this.IsTelemetryEnabled = optout != "1" && optout != "true"; } @@ -319,6 +318,27 @@ public static Configuration Create() return new Configuration(); } + /// + /// Updates the configuration to use the specified verbosity level, + /// which by default is . + /// + /// The level of verbosity used during logging. + public Configuration WithVerbosityEnabled(VerbosityLevel level = VerbosityLevel.Info) + { + this.VerbosityLevel = level; + return this; + } + + /// + /// Updates the configuration to log all runtime messages to the console, unless overridden by a custom . + /// + /// If true, then logs all runtime messages to the console. + public Configuration WithConsoleLoggingEnabled(bool isEnabled = true) + { + this.IsConsoleLoggingEnabled = isEnabled; + return this; + } + /// /// Updates the configuration with the specified number of iterations to run during systematic testing. /// @@ -621,28 +641,6 @@ public Configuration WithTestIterationsRunToCompletion(bool runToCompletion = tr return this; } - /// - /// Updates the configuration with verbose output enabled or disabled. - /// - /// If true, then messages are logged. - /// The level of detail to provide in verbose logging. - public Configuration WithVerbosityEnabled(bool isVerbose = true, LogSeverity logLevel = LogSeverity.Informational) - { - this.IsVerbose = isVerbose; - this.LogLevel = logLevel; - return this; - } - - /// - /// Updates the configuration with debug logging enabled or disabled. - /// - /// If true, then debug messages are logged. - public Configuration WithDebugLoggingEnabled(bool isDebugLoggingEnabled = true) - { - this.IsDebugVerbosityEnabled = isDebugLoggingEnabled; - return this; - } - /// /// Updates the configuration to enable or disable reporting activity coverage. /// @@ -678,6 +676,7 @@ public Configuration WithXmlLogEnabled(bool isEnabled = true) /// /// Updates the configuration with telemetry enabled or disabled. /// + /// If true, then enables telemetry. public Configuration WithTelemetryEnabled(bool isEnabled = true) { this.IsTelemetryEnabled = isEnabled; @@ -685,11 +684,22 @@ public Configuration WithTelemetryEnabled(bool isEnabled = true) } /// - /// Enable running of Monitor objects in production. + /// Updates the configuration to allow safety monitors to run outside + /// the scope of the testing engine. + /// + internal Configuration WithMonitoringEnabledOutsideTesting(bool isEnabled = true) + { + this.IsMonitoringEnabledOutsideTesting = isEnabled; + return this; + } + + /// + /// Updates the configuration to allow the runtime to check for actor quiescence + /// outside the scope of the testing engine. /// - internal Configuration WithProductionMonitorEnabled(bool isEnabled = true) + internal Configuration WithActorQuiescenceCheckingEnabledOutsideTesting(bool isEnabled = true) { - this.IsMonitoringEnabledInInProduction = isEnabled; + this.IsActorQuiescenceCheckingEnabledOutsideTesting = isEnabled; return this; } } diff --git a/Source/Core/IO/Debugging/Debug.cs b/Source/Core/IO/Debugging/Debug.cs deleted file mode 100644 index b077e0b9b..000000000 --- a/Source/Core/IO/Debugging/Debug.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Globalization; - -namespace Microsoft.Coyote.IO -{ - /// - /// Static class implementing debug reporting methods. - /// - internal static class Debug - { - /// - /// Checks if debugging is enabled. - /// - internal static bool IsEnabled = false; - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string value) - { - if (IsEnabled) - { - Console.Write(value); - } - } - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string format, object arg0) - { - if (IsEnabled) - { - Console.Write(string.Format(CultureInfo.InvariantCulture, format, arg0)); - } - } - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string format, object arg0, object arg1) - { - if (IsEnabled) - { - Console.Write(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1)); - } - } - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string format, object arg0, object arg1, object arg2) - { - if (IsEnabled) - { - Console.Write(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1, arg2)); - } - } - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string format, object arg0, object arg1, object arg2, object arg3) - { - if (IsEnabled) - { - Console.Write(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1, arg2, arg3)); - } - } - - /// - /// Writes the debugging information to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void Write(string format, params object[] args) - { - if (IsEnabled) - { - Console.Write(string.Format(CultureInfo.InvariantCulture, format, args)); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string value) - { - if (IsEnabled) - { - Console.WriteLine(value); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string format, object arg0) - { - if (IsEnabled) - { - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, format, arg0)); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string format, object arg0, object arg1) - { - if (IsEnabled) - { - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1)); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string format, object arg0, object arg1, object arg2) - { - if (IsEnabled) - { - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1, arg2)); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string format, object arg0, object arg1, object arg2, object arg3) - { - if (IsEnabled) - { - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, format, arg0, arg1, arg2, arg3)); - } - } - - /// - /// Writes the debugging information, followed by the current line terminator, to the output stream. - /// - /// - /// The print occurs only if debugging is enabled. - /// - public static void WriteLine(string format, params object[] args) - { - if (IsEnabled) - { - Console.WriteLine(string.Format(CultureInfo.InvariantCulture, format, args)); - } - } - } -} diff --git a/Source/Core/IO/Debugging/Error.cs b/Source/Core/IO/Debugging/Error.cs deleted file mode 100644 index 826489cad..000000000 --- a/Source/Core/IO/Debugging/Error.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Globalization; -using System.IO; - -namespace Microsoft.Coyote.IO -{ - /// - /// Static class implementing error reporting methods. - /// - internal static class Error - { - /// - /// If you play with Console.ForegroundColor then you should grab this lock in order - /// to avoid color leakage (wrong color becomes set permanently). - /// - public static readonly object ColorLock = new object(); - - /// - /// Reports a generic error to the user. - /// - public static void Report(string format, params object[] args) - { - string message = string.Format(CultureInfo.InvariantCulture, format, args); - Write(ConsoleColor.Red, "Error: "); - Write(ConsoleColor.Yellow, message); - Console.Error.WriteLine(string.Empty); - } - - /// - /// Writes the specified string value to the output stream. - /// - private static void Write(ConsoleColor color, string value) - { - Write(Console.Error, color, value); - } - - /// - /// Writes with console color to the specified TextWriter. - /// - internal static void Write(TextWriter logger, ConsoleColor color, string value) - { - lock (ColorLock) - { - try - { - Console.ForegroundColor = color; - logger.Write(value); - } - finally - { - Console.ResetColor(); - } - } - } - } -} diff --git a/Source/Core/IO/Logging/ConsoleLogger.cs b/Source/Core/IO/Logging/ConsoleLogger.cs deleted file mode 100644 index b14f83343..000000000 --- a/Source/Core/IO/Logging/ConsoleLogger.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Text; - -namespace Microsoft.Coyote.IO -{ - /// - /// Logger that writes text to the console. - /// - /// - /// See Logging for more information. - /// - public sealed class ConsoleLogger : TextWriter, ILogger - { - /// - /// Initializes a new instance of the class. - /// - public ConsoleLogger() - { - } - - /// - public TextWriter TextWriter => this; - - /// - /// When overridden in a derived class, returns the character encoding in which the - /// output is written. - /// - public override Encoding Encoding => Console.OutputEncoding; - - /// - /// The level of detail to report. - /// - public LogSeverity LogLevel { get; set; } - - /// - public override void Write(string value) - { - this.Write(LogSeverity.Informational, value); - } - - /// - public void Write(LogSeverity severity, string value) - { - switch (severity) - { - case LogSeverity.Informational: - if (this.LogLevel <= LogSeverity.Informational) - { - Console.Write(value); - } - - break; - case LogSeverity.Warning: - if (this.LogLevel <= LogSeverity.Warning) - { - Error.Write(Console.Out, ConsoleColor.Yellow, value); - } - - break; - case LogSeverity.Error: - if (this.LogLevel <= LogSeverity.Error) - { - Error.Write(Console.Out, ConsoleColor.Red, value); - } - - break; - - case LogSeverity.Important: - Console.Write(value); - break; - - default: - break; - } - } - - /// - public void Write(LogSeverity severity, string format, params object[] args) - { - string value = string.Format(format, args); - this.Write(severity, value); - } - - /// - public override void WriteLine(string value) - { - this.WriteLine(LogSeverity.Informational, value); - } - - /// - public override void Write(string format, params object[] args) - { - string value = string.Format(format, args); - this.Write(LogSeverity.Informational, value); - } - - /// - public override void WriteLine(string format, params object[] args) - { - string value = string.Format(format, args); - this.WriteLine(LogSeverity.Informational, value); - } - - /// - public void WriteLine(LogSeverity severity, string value) - { - switch (severity) - { - case LogSeverity.Informational: - if (this.LogLevel <= LogSeverity.Informational) - { - Console.WriteLine(value); - } - - break; - case LogSeverity.Warning: - if (this.LogLevel <= LogSeverity.Warning) - { - Error.Write(Console.Out, ConsoleColor.Yellow, value); - Console.WriteLine(); - } - - break; - case LogSeverity.Error: - if (this.LogLevel <= LogSeverity.Error) - { - Error.Write(Console.Out, ConsoleColor.Red, value); - Console.WriteLine(); - } - - break; - - case LogSeverity.Important: - Console.WriteLine(value); - break; - - default: - break; - } - } - - /// - public void WriteLine(LogSeverity severity, string format, params object[] args) - { - string value = string.Format(format, args); - this.WriteLine(severity, value); - } - } -} diff --git a/Source/Core/IO/Logging/ILogger.cs b/Source/Core/IO/Logging/ILogger.cs deleted file mode 100644 index 9dc493613..000000000 --- a/Source/Core/IO/Logging/ILogger.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; - -namespace Microsoft.Coyote.IO -{ - /// - /// A logger is used to capture messages, warnings and errors. - /// - public interface ILogger : IDisposable - { - /// - /// This property provides a TextWriter that implements ILogger which is handy if you - /// have existing code that requires a TextWriter. - /// - public TextWriter TextWriter { get; } - - /// - /// Writes an informational string to the log. - /// - /// The string to write. - public void Write(string value); - - /// - /// Writes an informational string to the log. - /// - /// The string format to write. - /// The arguments needed to format the string. - public void Write(string format, params object[] args); - - /// - /// Writes a string to the log. - /// - /// The severity of the issue being logged. - /// The string to write. - public void Write(LogSeverity severity, string value); - - /// - /// Writes a string to the log. - /// - /// The severity of the issue being logged. - /// The string format to write. - /// The arguments needed to format the string. - public void Write(LogSeverity severity, string format, params object[] args); - - /// - /// Writes an informational string to the log. - /// - /// The string to write. - public void WriteLine(string value); - - /// - /// Writes an informational string to the log. - /// - /// The string format to write. - /// The arguments needed to format the string. - public void WriteLine(string format, params object[] args); - - /// - /// Writes a string followed by a line terminator to the text string or stream. - /// - /// The severity of the issue being logged. - /// The string to write. - public void WriteLine(LogSeverity severity, string value); - - /// - /// Writes a string followed by a line terminator to the text string or stream. - /// - /// The severity of the issue being logged. - /// The string format to write. - /// The arguments needed to format the string. - public void WriteLine(LogSeverity severity, string format, params object[] args); - } -} diff --git a/Source/Core/IO/Logging/InMemoryLogger.cs b/Source/Core/IO/Logging/InMemoryLogger.cs deleted file mode 100644 index def9596cd..000000000 --- a/Source/Core/IO/Logging/InMemoryLogger.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.IO; -using System.Text; - -namespace Microsoft.Coyote.IO -{ - /// - /// Thread safe logger that writes text to an in-memory buffer. - /// The buffered text can be extracted using the ToString() method. - /// - /// - /// See Logging for more information. - /// - public sealed class InMemoryLogger : TextWriter, ILogger - { - /// - /// Underlying string builder. - /// - private readonly StringBuilder Builder; - - /// - /// Serializes access to the string writer. - /// - private readonly object Lock; - - /// - /// Optional logger provided by the user to delegate logging to. - /// - internal ILogger UserLogger { get; set; } - - /// - public TextWriter TextWriter => this; - - /// - /// When overridden in a derived class, returns the character encoding in which the - /// output is written. - /// - public override Encoding Encoding => Encoding.Unicode; - - /// - /// Initializes a new instance of the class. - /// - public InMemoryLogger() - { - this.Builder = new StringBuilder(); - this.Lock = new object(); - } - - /// - public override void Write(string value) => this.Write(LogSeverity.Informational, value); - - /// - public void Write(LogSeverity severity, string value) - { - try - { - lock (this.Lock) - { - this.Builder.Append(value); - if (this.UserLogger != null) - { - this.UserLogger.Write(severity, value); - } - } - } - catch (ObjectDisposedException) - { - // The writer was disposed. - } - } - - /// - public void Write(LogSeverity severity, string format, params object[] args) => - this.Write(severity, string.Format(format, args)); - - /// - public override void WriteLine(string value) => this.WriteLine(LogSeverity.Informational, value); - - /// - public void WriteLine(LogSeverity severity, string value) - { - try - { - lock (this.Lock) - { - this.Builder.AppendLine(value); - if (this.UserLogger != null) - { - this.UserLogger.WriteLine(severity, value); - } - } - } - catch (ObjectDisposedException) - { - // The writer was disposed. - } - } - - /// - public void WriteLine(LogSeverity severity, string format, params object[] args) => - this.WriteLine(severity, string.Format(format, args)); - - /// - /// Returns the logged text as a string. - /// - public override string ToString() - { - lock (this.Lock) - { - return this.Builder.ToString(); - } - } - - /// - /// Releases the resources used by the logger. - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - this.Builder.Clear(); - } - - base.Dispose(disposing); - } - } -} diff --git a/Source/Core/IO/Logging/LogSeverity.cs b/Source/Core/IO/Logging/LogSeverity.cs deleted file mode 100644 index a15d07488..000000000 --- a/Source/Core/IO/Logging/LogSeverity.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Coyote.IO -{ - /// - /// Flag indicating the type of logging information being provided to the . - /// - public enum LogSeverity - { - /// - /// General information about what is happening in the program. - /// - Informational, - - /// - /// Warnings that something unusual is found and is being handled. - /// - Warning, - - /// - /// Error is something unexpected that usually means program cannot proceed normally. - /// - Error, - - /// - /// Output that is not an error or warning, but is important. - /// - Important - } -} diff --git a/Source/Core/IO/Logging/NullLogger.cs b/Source/Core/IO/Logging/NullLogger.cs deleted file mode 100644 index ab969b3aa..000000000 --- a/Source/Core/IO/Logging/NullLogger.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.IO; -using System.Text; - -namespace Microsoft.Coyote.IO -{ - /// - internal class NullLogger : TextWriter, ILogger - { - /// - public TextWriter TextWriter => this; - - /// - public override void Write(string value) - { - } - - /// - /// When overridden in a derived class, returns the character encoding in which the - /// output is written. - /// - public override Encoding Encoding => Encoding.Unicode; - - /// - public void Write(LogSeverity severity, string value) - { - } - - /// - public override void Write(string format, params object[] args) - { - } - - /// - public void Write(LogSeverity severity, string format, params object[] args) - { - } - - /// - public override void WriteLine(string value) - { - } - - /// - public override void WriteLine(string format, params object[] args) - { - } - - /// - public void WriteLine(LogSeverity severity, string value) - { - } - - /// - public void WriteLine(LogSeverity severity, string format, params object[] args) - { - } - } -} diff --git a/Source/Core/IO/Logging/TextWriterLogger.cs b/Source/Core/IO/Logging/TextWriterLogger.cs deleted file mode 100644 index cc5455194..000000000 --- a/Source/Core/IO/Logging/TextWriterLogger.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.IO; -using System.Text; - -namespace Microsoft.Coyote.IO -{ - /// - /// Bridges custom user provided TextWriter logger so it can be passed into - /// Coyote via the interface. - /// - public class TextWriterLogger : TextWriter, ILogger - { - private readonly TextWriter UserLogger; - - /// - public TextWriter TextWriter => this.UserLogger; - - /// - /// Initializes a new instance of the class. - /// - /// The TextWriter to delegate to. - public TextWriterLogger(TextWriter userLogger) - { - this.UserLogger = userLogger; - } - - /// - public override Encoding Encoding => this.UserLogger.Encoding; - - /// - public override void Write(string message) - { - this.UserLogger.Write(message); - } - - /// - public override void Write(string format, object[] args) - { - this.UserLogger.Write(format, args); - } - - /// - public void Write(LogSeverity severity, string value) - { - this.Write(value); - } - - /// - public void Write(LogSeverity severity, string format, params object[] args) - { - this.Write(format, args); - } - - /// - public override void WriteLine(string message) - { - this.UserLogger.WriteLine(message); - } - - /// - public override void WriteLine(string format, object[] args) - { - this.UserLogger.WriteLine(format, args); - } - - /// - public void WriteLine(LogSeverity severity, string value) - { - this.WriteLine(value); - } - - /// - public void WriteLine(LogSeverity severity, string format, params object[] args) - { - this.WriteLine(format, args); - } - } -} diff --git a/Source/Core/Logging/ConsoleLogger.cs b/Source/Core/Logging/ConsoleLogger.cs new file mode 100644 index 000000000..59c30fc1d --- /dev/null +++ b/Source/Core/Logging/ConsoleLogger.cs @@ -0,0 +1,312 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Threading; + +namespace Microsoft.Coyote.Logging +{ + /// + /// Logger that writes text to the console. + /// + internal sealed class ConsoleLogger : ILogger + { + /// + /// The level of verbosity used during logging. + /// + private readonly VerbosityLevel VerbosityLevel; + + /// + /// Initializes a new instance of the class. + /// + public ConsoleLogger(VerbosityLevel level) + { + this.VerbosityLevel = level; + } + + /// + public void Write(string value) => this.Write(LogSeverity.Info, value); + + /// + public void Write(string format, object arg0) => this.Write(LogSeverity.Info, format, arg0); + + /// + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); + + /// + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, string.Format(format, args)); + + /// + public void Write(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.Write(value); + } + } + else + { + Console.Write(value); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.Write(format, arg0); + } + } + else + { + Console.Write(format, arg0); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.Write(format, arg0, arg1); + } + } + else + { + Console.Write(format, arg0, arg1); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.Write(format, arg0, arg1, arg2); + } + } + else + { + Console.Write(format, arg0, arg1, arg2); + } + } + } + + /// + public void Write(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.Write(format, args); + } + } + else + { + Console.Write(format, args); + } + } + } + + /// + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); + + /// + public void WriteLine(string format, object arg0) => this.WriteLine(LogSeverity.Info, format, arg0); + + /// + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, string.Format(format, args)); + + /// + public void WriteLine(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.WriteLine(value); + } + } + else + { + Console.WriteLine(value); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.WriteLine(format, arg0); + } + } + else + { + Console.WriteLine(format, arg0); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.WriteLine(format, arg0, arg1); + } + } + else + { + Console.WriteLine(format, arg0, arg1); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.WriteLine(format, arg0, arg1, arg2); + } + } + else + { + Console.WriteLine(format, arg0, arg1, arg2); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + if (severity == LogSeverity.Warning || severity == LogSeverity.Error) + { + using (new ConsoleColorManager(severity)) + { + Console.WriteLine(format, args); + } + } + else + { + Console.WriteLine(format, args); + } + } + } + + /// + /// Releases any resources held by the logger. + /// + public void Dispose() + { + } + + /// + /// Sets and restores colors when logging messages to the console. + /// + private struct ConsoleColorManager : IDisposable + { + /// + /// The original foreground color. + /// + private readonly ConsoleColor? OriginalForegroundColor; + + /// + /// Serializes changes to the console color. + /// + private static readonly SemaphoreSlim Lock = new SemaphoreSlim(1, 1); + + /// + /// Initializes a new instance of the struct. + /// + internal ConsoleColorManager(LogSeverity severity) + { + var originalForegroundColor = Console.ForegroundColor; + switch (severity) + { + case LogSeverity.Warning: + Lock.Wait(); + this.OriginalForegroundColor = originalForegroundColor; + Console.ForegroundColor = ConsoleColor.Yellow; + break; + case LogSeverity.Error: + Lock.Wait(); + this.OriginalForegroundColor = originalForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + break; + default: + this.OriginalForegroundColor = null; + break; + } + } + + /// + /// Restores the original console color. + /// + public void Dispose() + { + if (this.OriginalForegroundColor.HasValue) + { + Console.ForegroundColor = this.OriginalForegroundColor.Value; + Lock.Release(); + } + } + } + } +} diff --git a/Source/Core/Logging/ILogger.cs b/Source/Core/Logging/ILogger.cs new file mode 100644 index 000000000..37c0e7314 --- /dev/null +++ b/Source/Core/Logging/ILogger.cs @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Coyote.Logging +{ + /// + /// A logger is used to capture messages, warnings and errors. + /// + public interface ILogger : IDisposable + { + /// + /// Writes an informational string to the log. + /// + /// The string to write. + public void Write(string value); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + public void Write(string format, object arg0); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + public void Write(string format, object arg0, object arg1); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + /// The third object to format and write. + public void Write(string format, object arg0, object arg1, object arg2); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The arguments needed to format the string. + public void Write(string format, params object[] args); + + /// + /// Writes a string to the log with the specified verbosity level. + /// + /// The severity of the message being logged. + /// The string to write. + public void Write(LogSeverity severity, string value); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + public void Write(LogSeverity severity, string format, object arg0); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + public void Write(LogSeverity severity, string format, object arg0, object arg1); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + /// The third object to format and write. + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2); + + /// + /// Writes a string to the log with the specified verbosity level. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The arguments needed to format the string. + public void Write(LogSeverity severity, string format, params object[] args); + + /// + /// Writes an informational string to the log. + /// + /// The string to write. + public void WriteLine(string value); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + public void WriteLine(string format, object arg0); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + public void WriteLine(string format, object arg0, object arg1); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + /// The third object to format and write. + public void WriteLine(string format, object arg0, object arg1, object arg2); + + /// + /// Writes an informational string to the log. + /// + /// The string format to write. + /// The arguments needed to format the string. + public void WriteLine(string format, params object[] args); + + /// + /// Writes a string followed by a line terminator to the text string or stream + /// with the specified verbosity level. + /// + /// The severity of the message being logged. + /// The string to write. + public void WriteLine(LogSeverity severity, string value); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + public void WriteLine(LogSeverity severity, string format, object arg0); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1); + + /// + /// Writes an informational string to the log. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The first object to format and write. + /// The second object to format and write. + /// The third object to format and write. + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2); + + /// + /// Writes a string followed by a line terminator to the text string or stream + /// with the specified verbosity level. + /// + /// The severity of the message being logged. + /// The string format to write. + /// The arguments needed to format the string. + public void WriteLine(LogSeverity severity, string format, params object[] args); + } +} diff --git a/Source/Core/Logging/LogSeverity.cs b/Source/Core/Logging/LogSeverity.cs new file mode 100644 index 000000000..cd5a284a2 --- /dev/null +++ b/Source/Core/Logging/LogSeverity.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Coyote.Logging +{ + /// + /// The severity of the log message being provided to the . + /// + public enum LogSeverity + { + /// + /// Log that contains information useful for debugging purposes. + /// + Debug = 0, + + /// + /// Log that contains general information. + /// + Info, + + /// + /// Log that contains information about a warning. + /// + Warning, + + /// + /// Log that contains information about an error. + /// + Error, + + /// + /// Log that contains important information. + /// + Important + } +} diff --git a/Source/Core/Logging/LogWriter.cs b/Source/Core/Logging/LogWriter.cs new file mode 100644 index 000000000..61b6d957b --- /dev/null +++ b/Source/Core/Logging/LogWriter.cs @@ -0,0 +1,488 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Coyote.Logging +{ + /// + /// Responsible for logging runtime messages using the installed , + /// as well as optionally writing all observed messages to memory. + /// + internal class LogWriter : ILogger + { + /// + /// The configuration used by the runtime. + /// + protected readonly Configuration Configuration; + + /// + /// Used to log messages. + /// + internal ILogger Logger { get; private set; } + + /// + /// Synchronizes access to the log writer. + /// + protected readonly object Lock; + + /// + /// Initializes a new instance of the class. + /// + internal LogWriter(Configuration configuration, bool isConsoleLoggingEnabled = false) + { + this.Configuration = configuration; + this.Lock = new object(); + if (this.Configuration.IsConsoleLoggingEnabled || isConsoleLoggingEnabled) + { + this.Logger = new ConsoleLogger(this.Configuration.VerbosityLevel); + } + else + { + this.Logger = new NullLogger(); + } + } + + /// + public void Write(string value) => this.Write(LogSeverity.Info, value); + + /// + public void Write(string format, object arg0) => this.Write(LogSeverity.Info, format, arg0); + + /// + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); + + /// + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, string.Format(format, args)); + + /// + public virtual void Write(LogSeverity severity, string value) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.Write(severity, value); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void Write(LogSeverity severity, string format, object arg0) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.Write(severity, format, arg0); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void Write(LogSeverity severity, string format, object arg0, object arg1) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.Write(severity, format, arg0, arg1); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.Write(severity, format, arg0, arg1, arg2); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void Write(LogSeverity severity, string format, params object[] args) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.Write(severity, format, args); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); + + /// + public void WriteLine(string format, object arg0) => this.WriteLine(LogSeverity.Info, format, arg0); + + /// + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, string.Format(format, args)); + + /// + public virtual void WriteLine(LogSeverity severity, string value) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.WriteLine(severity, value); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void WriteLine(LogSeverity severity, string format, object arg0) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.WriteLine(severity, format, arg0); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.WriteLine(severity, format, arg0, arg1); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.WriteLine(severity, format, arg0, arg1, arg2); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + public virtual void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (this.IsVerbose(severity)) + { + try + { + this.Logger.WriteLine(severity, format, args); + } + catch (ObjectDisposedException) + { + // The writer was disposed. + } + } + } + + /// + /// Logs the specified debug message. + /// + internal void LogDebug(string message) => this.WriteLine(LogSeverity.Debug, message); + + /// + /// Logs the specified debug message. + /// + internal void LogDebug(string format, object arg0) => + this.WriteLine(LogSeverity.Debug, format, arg0); + + /// + /// Logs the specified debug message. + /// + internal void LogDebug(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Debug, format, arg0, arg1); + + /// + /// Logs the specified debug message. + /// + internal void LogDebug(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Debug, format, arg0, arg1, arg2); + + /// + /// Logs the specified debug message. + /// + internal void LogDebug(string format, params object[] args) => + this.WriteLine(LogSeverity.Debug, format, args); + + /// + /// Logs the debug message produced by the specified function. + /// + internal void LogDebug(Func messageProducer) + { + if (IsDebugVerbosityEnabled(this.Configuration.VerbosityLevel)) + { + this.LogDebug(messageProducer()); + } + } + + /// + /// Logs the specified informational message. + /// + internal void LogInfo(string message) => this.WriteLine(LogSeverity.Info, message); + + /// + /// Logs the specified informational message. + /// + internal void LogInfo(string format, object arg0) => + this.WriteLine(LogSeverity.Info, format, arg0); + + /// + /// Logs the specified informational message. + /// + internal void LogInfo(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + /// Logs the specified informational message. + /// + internal void LogInfo(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + /// Logs the specified informational message. + /// + internal void LogInfo(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, format, args); + + /// + /// Logs the specified warning message. + /// + internal void LogWarning(string message) => this.WriteLine(LogSeverity.Warning, message); + + /// + /// Logs the specified warning message. + /// + internal void LogWarning(string format, object arg0) => + this.WriteLine(LogSeverity.Warning, format, arg0); + + /// + /// Logs the specified warning message. + /// + internal void LogWarning(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Warning, format, arg0, arg1); + + /// + /// Logs the specified warning message. + /// + internal void LogWarning(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Warning, format, arg0, arg1, arg2); + + /// + /// Logs the specified warning message. + /// + internal void LogWarning(string format, params object[] args) => + this.WriteLine(LogSeverity.Warning, format, args); + + /// + /// Logs the specified error message. + /// + internal void LogError(string message) => this.WriteLine(LogSeverity.Error, message); + + /// + /// Logs the specified error message. + /// + internal void LogError(string format, object arg0) => + this.WriteLine(LogSeverity.Error, format, arg0); + + /// + /// Logs the specified error message. + /// + internal void LogError(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Error, format, arg0, arg1); + + /// + /// Logs the specified error message. + /// + internal void LogError(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Error, format, arg0, arg1, arg2); + + /// + /// Logs the specified error message. + /// + internal void LogError(string format, params object[] args) => + this.WriteLine(LogSeverity.Error, format, args); + + /// + /// Logs the specified important message. + /// + internal void LogImportant(string message) => this.WriteLine(LogSeverity.Important, message); + + /// + /// Logs the specified important message. + /// + internal void LogImportant(string format, object arg0) => + this.WriteLine(LogSeverity.Important, format, arg0); + + /// + /// Logs the specified important message. + /// + internal void LogImportant(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Important, format, arg0, arg1); + + /// + /// Logs the specified important message. + /// + internal void LogImportant(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Important, format, arg0, arg1, arg2); + + /// + /// Logs the specified important message. + /// + internal void LogImportant(string format, params object[] args) => + this.WriteLine(LogSeverity.Important, format, args); + + /// + /// Use this method to override the default . + /// + internal void SetLogger(ILogger logger) + { + lock (this.Lock) + { + if (logger is null) + { + throw new InvalidOperationException("Cannot set a null logger."); + } + else if (this.Configuration.IsConsoleLoggingEnabled) + { + throw new InvalidOperationException($"Cannot set custom logger '{logger.GetType().FullName}' when console logging is enabled."); + } + + if (this.IsRuntimeLogger()) + { + // Only dispose a logger created by the runtime. + this.Logger.Dispose(); + } + + this.Logger = logger; + } + } + + /// + /// Checks if the specified log severity can be logged. + /// + internal bool IsVerbose(LogSeverity severity) => IsVerbose(severity, this.Configuration.VerbosityLevel); + + /// + /// Checks if the specified log severity can be logged. + /// + internal static bool IsVerbose(LogSeverity severity, VerbosityLevel level) => + level switch + { + VerbosityLevel.None => severity >= LogSeverity.Important, + VerbosityLevel.Error => severity >= LogSeverity.Error, + VerbosityLevel.Warning => severity >= LogSeverity.Warning, + VerbosityLevel.Info => severity >= LogSeverity.Info, + VerbosityLevel.Debug => severity >= LogSeverity.Debug, + VerbosityLevel.Exhaustive => true, + _ => false + }; + + /// + /// Checks if debug log severity is enabled. + /// + private static bool IsDebugVerbosityEnabled(VerbosityLevel level) => + level is VerbosityLevel.Debug || level is VerbosityLevel.Exhaustive; + + /// + /// Checks if the installed is a runtime logger. + /// + internal bool IsRuntimeLogger() + { + lock (this.Lock) + { + return this.Logger is ConsoleLogger || this.Logger is NullLogger; + } + } + + /// + /// Releases any resources held by the log writer. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (this.Lock) + { + if (this.IsRuntimeLogger()) + { + // Only dispose a logger created by the runtime. + this.Logger.Dispose(); + } + } + } + } + + /// + /// Releases any resources held by the log writer. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/Source/Core/Logging/MemoryLogWriter.cs b/Source/Core/Logging/MemoryLogWriter.cs new file mode 100644 index 000000000..3768ea8c7 --- /dev/null +++ b/Source/Core/Logging/MemoryLogWriter.cs @@ -0,0 +1,244 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text; + +namespace Microsoft.Coyote.Logging +{ + /// + /// Responsible for logging runtime messages using the installed , + /// as well as writing all observed messages to memory. + /// + internal sealed class MemoryLogWriter : LogWriter + { + /// + /// Buffer where all observed messages are written. + /// + private readonly StringBuilder Builder; + + /// + /// True if the log writer is able to write logs, else false. + /// + private volatile bool IsWritable; + + /// + /// Initializes a new instance of the class. + /// + internal MemoryLogWriter(Configuration configuration, bool isConsoleLoggingEnabled = false) + : base(configuration, isConsoleLoggingEnabled) + { + this.Builder = new StringBuilder(); + this.IsWritable = true; + } + + /// + public override void Write(LogSeverity severity, string value) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.Append(value); + } + } + + base.Write(severity, value); + } + } + + /// + public override void Write(LogSeverity severity, string format, object arg0) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + } + } + + base.Write(severity, format, arg0); + } + } + + /// + public override void Write(LogSeverity severity, string format, object arg0, object arg1) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + } + } + + base.Write(severity, format, arg0, arg1); + } + } + + /// + public override void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + } + } + + base.Write(severity, format, arg0, arg1, arg2); + } + } + + /// + public override void Write(LogSeverity severity, string format, params object[] args) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + } + } + + base.Write(severity, format, args); + } + } + + /// + public override void WriteLine(LogSeverity severity, string value) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendLine(value); + } + } + + base.WriteLine(severity, value); + } + } + + /// + public override void WriteLine(LogSeverity severity, string format, object arg0) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + this.Builder.AppendLine(); + } + } + + base.WriteLine(severity, format, arg0); + } + } + + /// + public override void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + this.Builder.AppendLine(); + } + } + + base.WriteLine(severity, format, arg0, arg1); + } + } + + /// + public override void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + this.Builder.AppendLine(); + } + } + + base.WriteLine(severity, format, arg0, arg1, arg2); + } + } + + /// + public override void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (this.IsWritable) + { + if (this.IsObservable(severity)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + this.Builder.AppendLine(); + } + } + + base.WriteLine(severity, format, args); + } + } + + /// + /// Returns any observed messages that have been written in memory. + /// + internal string GetObservedMessages() + { + lock (this.Lock) + { + return this.Builder.ToString(); + } + } + + /// + /// Checks if the specified log severity can be observed. + /// + private bool IsObservable(LogSeverity severity) => severity >= LogSeverity.Info || this.IsVerbose(severity); + + /// + /// Closes the log writer. + /// + internal void Close() + { + this.IsWritable = false; + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + lock (this.Lock) + { + this.Builder.Clear(); + } + } + } + } +} diff --git a/Source/Core/Logging/MemoryLogger.cs b/Source/Core/Logging/MemoryLogger.cs new file mode 100644 index 000000000..7dd15b1b2 --- /dev/null +++ b/Source/Core/Logging/MemoryLogger.cs @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Text; + +namespace Microsoft.Coyote.Logging +{ + /// + /// Logger that writes all messages to memory. + /// + /// + /// This class is thread-safe. + /// + public sealed class MemoryLogger : ILogger + { + /// + /// The underlying string builder. + /// + private readonly StringBuilder Builder; + + /// + /// The level of verbosity used during logging. + /// + private readonly VerbosityLevel VerbosityLevel; + + /// + /// Synchronizes access to the logger. + /// + private readonly object Lock; + + /// + /// Initializes a new instance of the class. + /// + public MemoryLogger(VerbosityLevel level) + { + this.Builder = new StringBuilder(); + this.VerbosityLevel = level; + this.Lock = new object(); + } + + /// + public void Write(string value) => this.Write(LogSeverity.Info, value); + + /// + public void Write(string format, object arg0) => this.Write(LogSeverity.Info, format, arg0); + + /// + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); + + /// + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, string.Format(format, args)); + + /// + public void Write(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.Append(value); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + } + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + } + } + } + + /// + public void Write(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + } + } + } + + /// + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); + + /// + public void WriteLine(string format, object arg0) => this.WriteLine(LogSeverity.Info, format, arg0); + + /// + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, string.Format(format, args)); + + /// + public void WriteLine(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendLine(value); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + this.Builder.AppendLine(); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + this.Builder.AppendLine(); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + this.Builder.AppendLine(); + } + } + } + + /// + public void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + this.Builder.AppendLine(); + } + } + } + + /// + public override string ToString() + { + lock (this.Lock) + { + return this.Builder.ToString(); + } + } + + /// + /// Releases any resources held by the logger. + /// + public void Dispose() + { + lock (this.Lock) + { + this.Builder.Clear(); + } + } + } +} diff --git a/Source/Core/Logging/NullLogger.cs b/Source/Core/Logging/NullLogger.cs new file mode 100644 index 000000000..456e13dec --- /dev/null +++ b/Source/Core/Logging/NullLogger.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Coyote.Logging +{ + /// + /// Logger that discards all log messages. + /// + internal class NullLogger : ILogger + { + /// + public void Write(string value) + { + } + + /// + public void Write(string format, object arg0) + { + } + + /// + public void Write(string format, object arg0, object arg1) + { + } + + /// + public void Write(string format, object arg0, object arg1, object arg2) + { + } + + /// + public void Write(string format, params object[] args) + { + } + + /// + public void Write(LogSeverity severity, string value) + { + } + + /// + public void Write(LogSeverity severity, string format, object arg0) + { + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1) + { + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + } + + /// + public void Write(LogSeverity severity, string format, params object[] args) + { + } + + /// + public void WriteLine(string value) + { + } + + /// + public void WriteLine(string format, object arg0) + { + } + + /// + public void WriteLine(string format, object arg0, object arg1) + { + } + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) + { + } + + /// + public void WriteLine(string format, params object[] args) + { + } + + /// + public void WriteLine(LogSeverity severity, string value) + { + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0) + { + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + } + + /// + public void WriteLine(LogSeverity severity, string format, params object[] args) + { + } + + /// + /// Releases any resources held by the logger. + /// + public void Dispose() + { + } + } +} diff --git a/Source/Core/Logging/TextWriterLogger.cs b/Source/Core/Logging/TextWriterLogger.cs new file mode 100644 index 000000000..6cf18adea --- /dev/null +++ b/Source/Core/Logging/TextWriterLogger.cs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; + +namespace Microsoft.Coyote.Logging +{ + /// + /// Logger that writes to the specified . + /// + public sealed class TextWriterLogger : ILogger + { + /// + /// The user-provided . + /// + private readonly TextWriter Logger; + + /// + /// The level of verbosity used during logging. + /// + private readonly VerbosityLevel VerbosityLevel; + + /// + /// Initializes a new instance of the class. + /// + public TextWriterLogger(TextWriter logger, VerbosityLevel level) + { + this.Logger = logger; + this.VerbosityLevel = level; + } + + /// + public void Write(string value) => this.Write(LogSeverity.Info, value); + + /// + public void Write(string format, object arg0) => this.Write(LogSeverity.Info, format, arg0); + + /// + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); + + /// + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, string.Format(format, args)); + + /// + public void Write(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.Write(value); + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.Write(format, arg0); + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.Write(format, arg0, arg1); + } + } + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.Write(format, arg0, arg1, arg2); + } + } + + /// + public void Write(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.Write(format, args); + } + } + + /// + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); + + /// + public void WriteLine(string format, object arg0) => this.WriteLine(LogSeverity.Info, format, arg0); + + /// + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, string.Format(format, args)); + + /// + public void WriteLine(LogSeverity severity, string value) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.WriteLine(value); + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.WriteLine(format, arg0); + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.WriteLine(format, arg0, arg1); + } + } + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.WriteLine(format, arg0, arg1, arg2); + } + } + + /// + public void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + this.Logger.WriteLine(format, args); + } + } + + /// + /// Releases any resources held by the logger. + /// + public void Dispose() => this.Logger.Dispose(); + } +} diff --git a/Source/Core/Logging/VerbosityLevel.cs b/Source/Core/Logging/VerbosityLevel.cs new file mode 100644 index 000000000..7ed78edda --- /dev/null +++ b/Source/Core/Logging/VerbosityLevel.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Coyote.Logging +{ + /// + /// The level of verbosity used during logging. + /// + public enum VerbosityLevel + { + /// + /// Discards any log messages. + /// + None = 0, + + /// + /// Logs error messages that are not recoverable. + /// + Error, + + /// + /// Logs warnings highlighting an abnormal or unexpected event. + /// + Warning, + + /// + /// Logs informational messages. + /// + Info, + + /// + /// Logs messages that are useful for debugging. + /// + Debug, + + /// + /// Logs the most detailed messages. + /// + Exhaustive + } +} diff --git a/Source/Core/Runtime/CoyoteRuntime.cs b/Source/Core/Runtime/CoyoteRuntime.cs index 4818bd6ae..85f5a621c 100644 --- a/Source/Core/Runtime/CoyoteRuntime.cs +++ b/Source/Core/Runtime/CoyoteRuntime.cs @@ -11,7 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Coyote.Actors; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime.CompilerServices; using Microsoft.Coyote.Specifications; using Microsoft.Coyote.Testing; @@ -159,6 +159,23 @@ internal sealed class CoyoteRuntime : ICoyoteRuntime, IDisposable /// internal readonly IRandomValueGenerator ValueGenerator; + /// + /// Responsible for writing to the installed . + /// + internal readonly LogWriter LogWriter; + + /// + public ILogger Logger + { + get => this.LogWriter; + set => this.LogWriter.SetLogger(value); + } + + /// + /// Manages all registered objects. + /// + internal readonly LogManager LogManager; + /// /// List of all registered safety and liveness specification monitors. /// @@ -246,45 +263,28 @@ internal sealed class CoyoteRuntime : ICoyoteRuntime, IDisposable /// internal string BugReport { get; private set; } - /// - /// Responsible for writing to all registered objects. - /// - internal LogWriter LogWriter { get; private set; } - - /// - public ILogger Logger - { - get => this.LogWriter.Logger; - - set - { - using var v = this.LogWriter.SetLogger(value); - } - } - /// public event OnFailureHandler OnFailure; /// /// Initializes a new instance of the class. /// - internal CoyoteRuntime(Configuration configuration, IRandomValueGenerator valueGenerator) - : this(configuration, null, valueGenerator) - { - } + internal static CoyoteRuntime Create(Configuration configuration, IRandomValueGenerator valueGenerator, + LogWriter logWriter, LogManager logManager) => + new CoyoteRuntime(configuration, null, valueGenerator, logWriter, logManager); /// /// Initializes a new instance of the class. /// - internal CoyoteRuntime(Configuration configuration, OperationScheduler scheduler) - : this(configuration, scheduler, scheduler.ValueGenerator) - { - } + internal static CoyoteRuntime Create(Configuration configuration, OperationScheduler scheduler, + LogWriter logWriter, LogManager logManager) => + new CoyoteRuntime(configuration, scheduler, scheduler.ValueGenerator, logWriter, logManager); /// /// Initializes a new instance of the class. /// - private CoyoteRuntime(Configuration configuration, OperationScheduler scheduler, IRandomValueGenerator valueGenerator) + private CoyoteRuntime(Configuration configuration, OperationScheduler scheduler, IRandomValueGenerator valueGenerator, + LogWriter logWriter, LogManager logManager) { // Registers the runtime with the provider which in return assigns a unique identifier. this.Id = RuntimeProvider.Register(this); @@ -316,9 +316,9 @@ private CoyoteRuntime(Configuration configuration, OperationScheduler scheduler, Interlocked.Increment(ref ExecutionControlledUseCount); } - this.LogWriter = new ActorLogWriter(configuration, new ActorRuntimeLogTextFormatter()); - this.ValueGenerator = valueGenerator; + this.LogWriter = logWriter; + this.LogManager = logManager; this.SpecificationMonitors = new List(); this.TaskLivenessMonitors = new List(); @@ -328,8 +328,8 @@ private CoyoteRuntime(Configuration configuration, OperationScheduler scheduler, TaskContinuationOptions.HideScheduler, this.ControlledTaskScheduler); this.DefaultActorExecutionContext = this.SchedulingPolicy is SchedulingPolicy.Interleaving ? - new ActorExecutionContext.Mock(configuration, this) : - new ActorExecutionContext(configuration, this); + new ActorExecutionContext.Mock(configuration, this, this.LogManager as ActorLogManager) : + new ActorExecutionContext(configuration, this, this.LogManager as ActorLogManager); } /// @@ -340,7 +340,7 @@ private CoyoteRuntime(Configuration configuration, OperationScheduler scheduler, #endif internal Task RunTestAsync(Delegate testMethod, string testName) { - this.Logger.WriteLine("[coyote::test] Runtime '{0}' started test{1} on thread '{2}'.", + this.LogWriter.LogInfo("[coyote::test] Runtime '{0}' started test{1} on thread '{2}'.", this.Id, string.IsNullOrEmpty(testName) ? string.Empty : $" '{testName}'", Thread.CurrentThread.ManagedThreadId); this.Assert(testMethod != null, "Unable to execute a null test method."); @@ -672,7 +672,7 @@ internal void RegisterNewOperation(ControlledOperation op) this.PendingStartOperationMap.Add(op, new ManualResetEventSlim(false)); } - IO.Debug.WriteLine("[coyote::debug] Created operation '{0}' of group '{1}' on thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Created operation '{0}' of group '{1}' on thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); } } @@ -691,7 +691,7 @@ internal void StartOperation(ControlledOperation op) this.SetCurrentExecutionContext(op); using (SynchronizedSection.Enter(this.RuntimeLock)) { - IO.Debug.WriteLine("[coyote::debug] Started operation '{0}' of group '{1}' on thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Started operation '{0}' of group '{1}' on thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); op.Status = OperationStatus.Enabled; if (this.SchedulingPolicy is SchedulingPolicy.Interleaving) @@ -727,7 +727,7 @@ private void WaitOperationsStart() var pendingOp = this.PendingStartOperationMap.First(); while (pendingOp.Key.Status is OperationStatus.None && this.ExecutionStatus is ExecutionStatus.Running) { - IO.Debug.WriteLine("[coyote::debug] Sleeping thread '{0}' until operation '{1}' of group '{2}' starts.", + this.LogWriter.LogDebug("[coyote::debug] Sleeping thread '{0}' until operation '{1}' of group '{2}' starts.", Thread.CurrentThread.ManagedThreadId, pendingOp.Key.Name, pendingOp.Key.Group); using (SynchronizedSection.Exit(this.RuntimeLock)) { @@ -741,7 +741,7 @@ private void WaitOperationsStart() } } - IO.Debug.WriteLine("[coyote::debug] Waking up thread '{0}'.", Thread.CurrentThread.ManagedThreadId); + this.LogWriter.LogDebug("[coyote::debug] Waking up thread '{0}'.", Thread.CurrentThread.ManagedThreadId); } pendingOp.Value.Dispose(); @@ -765,14 +765,14 @@ private void PauseOperation(ControlledOperation op) // Do not allow the operation to wake up, unless its currently scheduled and enabled or the runtime stopped running. while (!(op == this.ScheduledOperation && op.Status is OperationStatus.Enabled) && this.ExecutionStatus is ExecutionStatus.Running) { - IO.Debug.WriteLine("[coyote::debug] Sleeping operation '{0}' of group '{1}' on thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Sleeping operation '{0}' of group '{1}' on thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); using (SynchronizedSection.Exit(this.RuntimeLock)) { op.WaitSignal(); } - IO.Debug.WriteLine("[coyote::debug] Waking up operation '{0}' of group '{1}' on thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Waking up operation '{0}' of group '{1}' on thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); } } @@ -792,7 +792,7 @@ internal void PauseOperationUntil(ControlledOperation current, Func condit current ??= this.GetExecutingOperation(); while (current != null && !condition() && this.ExecutionStatus is ExecutionStatus.Running) { - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' of group '{1}' is waiting for {2} on thread '{3}'.", + this.LogWriter.LogDebug("[coyote::debug] Operation '{0}' of group '{1}' is waiting for {2} on thread '{3}'.", current.Name, current.Group, debugMsg ?? "condition to get resolved", Thread.CurrentThread.ManagedThreadId); // TODO: can we identify when the dependency is uncontrolled? current.PauseWithDependency(condition, isConditionControlled); @@ -854,7 +854,7 @@ internal void ScheduleNextOperation(ControlledOperation current, SchedulingPoint // now been resolved, so resume it on this uncontrolled thread. current = this.ScheduledOperation; type = this.LastPostponedSchedulingPoint.Value; - IO.Debug.WriteLine("[coyote::debug] Resuming scheduling point '{0}' of operation '{1}' in uncontrolled thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Resuming scheduling point '{0}' of operation '{1}' in uncontrolled thread '{2}'.", type, current, Thread.CurrentThread.ManagedThreadId); } else if (type is SchedulingPointType.Create || type is SchedulingPointType.ContinueWith) @@ -862,7 +862,7 @@ internal void ScheduleNextOperation(ControlledOperation current, SchedulingPoint // This is a scheduling point that was invoked because a new operation was // created by an uncontrolled thread, so postpone the scheduling point and // resume it on the next available controlled thread. - IO.Debug.WriteLine("[coyote::debug] Postponing scheduling point '{0}' in uncontrolled thread '{1}'.", + this.LogWriter.LogDebug("[coyote::debug] Postponing scheduling point '{0}' in uncontrolled thread '{1}'.", type, Thread.CurrentThread.ManagedThreadId); this.LastPostponedSchedulingPoint = type; return; @@ -891,7 +891,7 @@ internal void ScheduleNextOperation(ControlledOperation current, SchedulingPoint isSuppressible && current.Status is OperationStatus.Enabled) { // Suppress the scheduling point. - IO.Debug.WriteLine("[coyote::debug] Suppressing scheduling point in operation '{0}'.", current.Name); + this.LogWriter.LogDebug("[coyote::debug] Suppressing scheduling point in operation '{0}'.", current.Name); return; } @@ -922,7 +922,7 @@ internal void ScheduleNextOperation(ControlledOperation current, SchedulingPoint // If uncontrolled concurrency is detected, then do not check for deadlocks directly, // but instead leave it to the background deadlock detection timer and postpone the // scheduling point, which might get resolved from an uncontrolled thread. - IO.Debug.WriteLine("[coyote::debug] Postponing scheduling point '{0}' of operation '{1}' due to potential deadlock.", + this.LogWriter.LogDebug("[coyote::debug] Postponing scheduling point '{0}' of operation '{1}' due to potential deadlock.", type, current); this.LastPostponedSchedulingPoint = type; this.PauseOperation(current); @@ -945,7 +945,8 @@ internal void ScheduleNextOperation(ControlledOperation current, SchedulingPoint this.Detach(ExecutionStatus.BoundReached); } - IO.Debug.WriteLine("[coyote::debug] Scheduling operation '{0}' of group '{1}'.", next.Name, next.Group); + this.LogWriter.LogDebug("[coyote::debug] Scheduling operation '{0}' of group '{1}'.", + next.Name, next.Group); if (current != next) { // Pause the currently scheduled operation, and enable the next one. @@ -982,7 +983,7 @@ internal void DelayOperation(ControlledOperation current) { // Choose the next delay to inject. The value is in milliseconds. int delay = this.GetNondeterministicDelay(current, (int)this.Configuration.MaxFuzzingDelay); - IO.Debug.WriteLine("[coyote::debug] Delaying operation '{0}' on thread '{1}' by {2}ms.", + this.LogWriter.LogDebug("[coyote::debug] Delaying operation '{0}' on thread '{1}' by {2}ms.", current.Name, Thread.CurrentThread.ManagedThreadId, delay); // Only sleep the executing operation if a non-zero delay was chosen. @@ -1009,7 +1010,7 @@ internal void CompleteOperation(ControlledOperation op) op.ExecuteContinuations(); using (SynchronizedSection.Enter(this.RuntimeLock)) { - IO.Debug.WriteLine("[coyote::debug] Completed operation '{0}' of group '{1}' on thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Completed operation '{0}' of group '{1}' on thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); op.Status = OperationStatus.Completed; } @@ -1026,7 +1027,7 @@ internal bool TryResetOperation(ControlledOperation op) { if (op.Status is OperationStatus.Completed) { - IO.Debug.WriteLine("[coyote::debug] Resetting operation '{0}' of group '{1}' from thread '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Resetting operation '{0}' of group '{1}' from thread '{2}'.", op.Name, op.Group, Thread.CurrentThread.ManagedThreadId); op.Status = OperationStatus.None; if (this.SchedulingPolicy is SchedulingPolicy.Interleaving) @@ -1051,7 +1052,7 @@ internal void SuppressScheduling() { using (SynchronizedSection.Enter(this.RuntimeLock)) { - IO.Debug.WriteLine("[coyote::debug] Suppressing scheduling of enabled operations in runtime '{0}'.", this.Id); + this.LogWriter.LogDebug("[coyote::debug] Suppressing scheduling of enabled operations in runtime '{0}'.", this.Id); this.IsSchedulerSuppressed = true; } } @@ -1063,7 +1064,7 @@ internal void ResumeScheduling() { using (SynchronizedSection.Enter(this.RuntimeLock)) { - IO.Debug.WriteLine("[coyote::debug] Resuming scheduling of enabled operations in runtime '{0}'.", this.Id); + this.LogWriter.LogDebug("[coyote::debug] Resuming scheduling of enabled operations in runtime '{0}'.", this.Id); this.IsSchedulerSuppressed = false; } } @@ -1077,7 +1078,7 @@ internal void CheckpointExecutionTrace() using (SynchronizedSection.Enter(this.RuntimeLock)) { ExecutionTrace trace = this.Scheduler.CheckpointExecutionTrace(); - IO.Debug.WriteLine("[coyote::debug] Set checkpoint in current execution path with length '{0}' in runtime '{1}'.", + this.LogWriter.LogDebug("[coyote::debug] Set checkpoint in current execution path with length '{0}' in runtime '{1}'.", trace.Length, this.Id); } } @@ -1124,7 +1125,7 @@ internal bool GetNextNondeterministicBooleanChoice(string callerName, string cal result = this.ValueGenerator.Next(2) is 0 ? true : false; } - this.LogWriter.LogRandom(result, callerName, callerType); + this.LogManager.LogRandom(result, callerName, callerType); return result; } } @@ -1171,7 +1172,7 @@ internal int GetNextNondeterministicIntegerChoice(int maxValue, string callerNam result = this.ValueGenerator.Next(maxValue); } - this.LogWriter.LogRandom(result, callerName, callerType); + this.LogManager.LogRandom(result, callerName, callerType); return result; } } @@ -1205,7 +1206,7 @@ private int GetNondeterministicDelay(ControlledOperation op, int maxDelay) /// private bool TryEnableOperationsWithResolvedDependencies(ControlledOperation current) { - IO.Debug.WriteLine("[coyote::debug] Trying to enable any operation with resolved dependencies in runtime '{0}'.", this.Id); + this.LogWriter.LogDebug("[coyote::debug] Trying to enable any operation with resolved dependencies in runtime '{0}'.", this.Id); int attempt = 0; int delay = (int)this.Configuration.UncontrolledConcurrencyResolutionDelay; @@ -1228,7 +1229,7 @@ private bool TryEnableOperationsWithResolvedDependencies(ControlledOperation cur this.TryEnableOperation(op); if (previousStatus == op.Status) { - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' of group '{1}' has status '{2}'.", + this.LogWriter.LogDebug("[coyote::debug] Operation '{0}' of group '{1}' has status '{2}'.", op.Name, op.Group, op.Status); if (op.IsPaused && op.IsDependencyUncontrolled) { @@ -1244,7 +1245,7 @@ private bool TryEnableOperationsWithResolvedDependencies(ControlledOperation cur } else { - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' of group '{1}' changed status from '{2}' to '{3}'.", + this.LogWriter.LogDebug("[coyote::debug] Operation '{0}' of group '{1}' changed status from '{2}' to '{3}'.", op.Name, op.Group, previousStatus, op.Status); statusChanges++; } @@ -1283,7 +1284,7 @@ private bool TryEnableOperationsWithResolvedDependencies(ControlledOperation cur if (++attempt < maxAttempts && isConcurrencyUnresolved) { // Implement a simple retry logic to try resolve uncontrolled concurrency. - IO.Debug.WriteLine("[coyote::debug] Pausing controlled thread '{0}' to try resolve uncontrolled concurrency.", + this.LogWriter.LogDebug("[coyote::debug] Pausing controlled thread '{0}' to try resolve uncontrolled concurrency.", Thread.CurrentThread.ManagedThreadId); using (SynchronizedSection.Exit(this.RuntimeLock)) { @@ -1297,7 +1298,8 @@ private bool TryEnableOperationsWithResolvedDependencies(ControlledOperation cur break; } - IO.Debug.WriteLine("[coyote::debug] There are {0} enabled operations in runtime '{1}'.", enabledOpsCount, this.Id); + this.LogWriter.LogDebug("[coyote::debug] There are {0} enabled operations in runtime '{1}'.", + enabledOpsCount, this.Id); this.MaxConcurrencyDegree = Math.Max(this.MaxConcurrencyDegree, enabledOpsCount); return enabledOpsCount > 0; } @@ -1354,7 +1356,7 @@ private void TryPauseAndResolveUncontrolledTask(Task task) uint maxAttempts = this.Configuration.UncontrolledConcurrencyResolutionAttempts; while (attempt++ < maxAttempts && !task.IsCompleted) { - IO.Debug.WriteLine("[coyote::debug] Pausing controlled thread '{0}' to try resolve uncontrolled concurrency.", + this.LogWriter.LogDebug("[coyote::debug] Pausing controlled thread '{0}' to try resolve uncontrolled concurrency.", Thread.CurrentThread.ManagedThreadId); using (SynchronizedSection.Exit(this.RuntimeLock)) { @@ -1372,7 +1374,7 @@ private void TryPauseAndResolveUncontrolledTask(Task task) if (this.LastPostponedSchedulingPoint.HasValue) { - IO.Debug.WriteLine("[coyote::debug] Resuming controlled thread '{0}' with uncontrolled concurrency resolved.", + this.LogWriter.LogDebug("[coyote::debug] Resuming controlled thread '{0}' with uncontrolled concurrency resolved.", Thread.CurrentThread.ManagedThreadId); this.ScheduleNextOperation(default, this.LastPostponedSchedulingPoint.Value, isSuppressible: false); } @@ -1526,7 +1528,7 @@ public void RegisterMonitor() private bool TryCreateMonitor(Type type) { if (this.SchedulingPolicy != SchedulingPolicy.None || - this.Configuration.IsMonitoringEnabledInInProduction) + this.Configuration.IsMonitoringEnabledOutsideTesting) { using (SynchronizedSection.Enter(this.RuntimeLock)) { @@ -1534,7 +1536,7 @@ private bool TryCreateMonitor(Type type) if (!this.SpecificationMonitors.Any(m => m.GetType() == type)) { var monitor = (Specifications.Monitor)Activator.CreateInstance(type); - monitor.Initialize(this.Configuration, this, this.LogWriter); + monitor.Initialize(this.Configuration, this); monitor.InitializeStateInformation(); this.SpecificationMonitors.Add(monitor); if (this.SchedulingPolicy is SchedulingPolicy.Interleaving) @@ -1569,7 +1571,7 @@ public void Monitor(Event e) internal void InvokeMonitor(Type type, Event e, string senderName, string senderType, string senderStateName) { if (this.SchedulingPolicy != SchedulingPolicy.None || - this.Configuration.IsMonitoringEnabledInInProduction) + this.Configuration.IsMonitoringEnabledOutsideTesting) { using (SynchronizedSection.Enter(this.RuntimeLock)) { @@ -1845,7 +1847,7 @@ private void CheckIfExecutionHasDeadlocked(IEnumerable ops) msg.Append("Coyote cannot accurately determine if this is a real deadlock or not."); if (!this.Configuration.ReportPotentialDeadlocksAsBugs) { - this.Logger.WriteLine($"[coyote::test] {msg}"); + this.LogWriter.LogInfo("[coyote::test] {0}", msg); this.Detach(ExecutionStatus.Deadlocked); } @@ -1863,7 +1865,7 @@ private void CheckIfExecutionHasDeadlocked(IEnumerable ops) private async Task CheckIfExecutionHasDeadlockedAsync() { var info = new SchedulingActivityInfo(); - IO.Debug.WriteLine("[coyote::debug] Started periodic monitoring for potential deadlocks in runtime '{0}'.", this.Id); + this.LogWriter.LogDebug("[coyote::debug] Started periodic monitoring for potential deadlocks in runtime '{0}'.", this.Id); while (true) { try @@ -1891,14 +1893,14 @@ private async Task CheckIfExecutionHasDeadlockedAsync() } else { - this.Logger.WriteLine($"[coyote::test] {msg}"); + this.LogWriter.LogInfo("[coyote::test] {0}", msg); this.Detach(ExecutionStatus.Deadlocked); } } else { // Passed check, so continue with the next timeout period. - IO.Debug.WriteLine("[coyote::debug] Passed periodic check for potential deadlocks in runtime '{0}'.", this.Id); + this.LogWriter.LogDebug("[coyote::debug] Passed periodic check for potential deadlocks in runtime '{0}'.", this.Id); info.OperationCount = this.OperationMap.Count; info.StepCount = this.Scheduler.StepCount; if (this.LastPostponedSchedulingPoint is SchedulingPointType.Pause || @@ -1990,7 +1992,7 @@ private void CheckIfSchedulingStepsBoundIsReached() } else { - IO.Debug.WriteLine($"[coyote::debug] {message}"); + this.LogWriter.LogDebug("[coyote::debug] {0}", message); this.Detach(ExecutionStatus.BoundReached); } } @@ -2030,7 +2032,7 @@ internal void NotifyAssertionFailure(string text) if (this.ExecutionStatus is ExecutionStatus.Running) { this.BugReport = text; - this.LogWriter.LogAssertionFailure($"[coyote::error] {text}"); + this.LogManager.LogAssertionFailure($"[coyote::error] {text}"); this.RaiseOnFailureEvent(new AssertionFailureException(text)); if (this.Configuration.AttachDebugger) { @@ -2133,13 +2135,13 @@ private bool TryHandleUncontrolledConcurrency(string message, string methodName { if (this.Configuration.IsPartiallyControlledConcurrencyAllowed) { - IO.Debug.WriteLine($"[coyote::debug] {message}"); + this.LogWriter.LogDebug("[coyote::debug] {0}", message); this.IsUncontrolledConcurrencyDetected = true; return true; } else if (this.Configuration.IsSystematicFuzzingFallbackEnabled) { - IO.Debug.WriteLine($"[coyote::debug] {message}"); + this.LogWriter.LogDebug("[coyote::debug] {0}", message); this.IsUncontrolledConcurrencyDetected = true; this.Detach(ExecutionStatus.ConcurrencyUncontrolled); } @@ -2193,7 +2195,7 @@ internal void ProcessUnhandledExceptionInOperation(ControlledOperation op, Excep if (exception.GetBaseException() is ThreadInterruptedException) { // Ignore this exception, its thrown by the runtime. - IO.Debug.WriteLine("[coyote::debug] Controlled thread '{0}' executing operation '{1}' was interrupted.", + this.LogWriter.LogDebug("[coyote::debug] Controlled thread '{0}' executing operation '{1}' was interrupted.", Thread.CurrentThread.ManagedThreadId, op.Name); } else @@ -2323,10 +2325,10 @@ private void SetControlledSynchronizationContext() => SynchronizationContext.SetSynchronizationContext(this.SyncContext); /// - public void RegisterLog(IRuntimeLog log) => this.LogWriter.RegisterLog(log); + public void RegisterLog(IRuntimeLog log) => this.LogManager.RegisterLog(log, this.LogWriter); /// - public void RemoveLog(IRuntimeLog log) => this.LogWriter.RemoveLog(log); + public void RemoveLog(IRuntimeLog log) => this.LogManager.RemoveLog(log); /// public void Stop() => this.IsRunning = false; @@ -2348,24 +2350,24 @@ private void Detach(ExecutionStatus status) { if (status is ExecutionStatus.PathExplored) { - this.Logger.WriteLine($"[coyote::test] Exploration finished in runtime '{this.Id}' [reached the end of the test method]."); + this.LogWriter.LogInfo("[coyote::test] Exploration finished in runtime '{0}' [reached the end of the test method].", this.Id); } else if (status is ExecutionStatus.BoundReached) { - this.Logger.WriteLine($"[coyote::test] Exploration finished in runtime '{this.Id}' [reached the given bound]."); + this.LogWriter.LogInfo("[coyote::test] Exploration finished in runtime '{0}' [reached the given bound].", this.Id); } else if (status is ExecutionStatus.Deadlocked) { - this.Logger.WriteLine($"[coyote::test] Exploration finished in runtime '{this.Id}' [detected a potential deadlock]."); + this.LogWriter.LogInfo("[coyote::test] Exploration finished in runtime '{0}' [detected a potential deadlock].", this.Id); } else if (status is ExecutionStatus.ConcurrencyUncontrolled) { - this.Logger.WriteLine($"[coyote::test] Exploration finished in runtime '{this.Id}' [detected uncontrolled concurrency]."); + this.LogWriter.LogInfo("[coyote::test] Exploration finished in runtime '{0}' [detected uncontrolled concurrency].", this.Id); } else if (status is ExecutionStatus.BugFound) { - this.Logger.WriteLine($"[coyote::test] Exploration finished in runtime '{this.Id}' [found a bug using the '{0}' strategy].", - this.Configuration.SchedulingStrategy); + this.LogWriter.LogInfo("[coyote::test] Exploration finished in runtime '{0}' [found a bug using the '{1}' strategy].", + this.Id, this.Configuration.SchedulingStrategy); } this.ExecutionStatus = status; @@ -2435,6 +2437,7 @@ private void Dispose(bool disposing) this.ControlledTaskScheduler.Dispose(); this.SyncContext.Dispose(); this.CancellationSource.Dispose(); + this.LogWriter.Dispose(); } if (this.SchedulingPolicy is SchedulingPolicy.Interleaving) diff --git a/Source/Core/Runtime/ICoyoteRuntime.cs b/Source/Core/Runtime/ICoyoteRuntime.cs index 464d4fbf2..0a894fedd 100644 --- a/Source/Core/Runtime/ICoyoteRuntime.cs +++ b/Source/Core/Runtime/ICoyoteRuntime.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. using System; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Specifications; namespace Microsoft.Coyote.Runtime @@ -13,7 +13,7 @@ namespace Microsoft.Coyote.Runtime public interface ICoyoteRuntime : IDisposable { /// - /// Get or set the used to log messages. + /// Gets or sets an for logging runtime messages. /// /// /// See Logging for more information. diff --git a/Source/Core/Runtime/Logging/IRuntimeLog.cs b/Source/Core/Runtime/Logging/IRuntimeLog.cs index 7d63f5902..1980e757c 100644 --- a/Source/Core/Runtime/Logging/IRuntimeLog.cs +++ b/Source/Core/Runtime/Logging/IRuntimeLog.cs @@ -8,7 +8,7 @@ namespace Microsoft.Coyote.Runtime /// is happening in the . /// /// - /// See Logging for more information. + /// See Logging for more information. /// public interface IRuntimeLog { diff --git a/Source/Core/Runtime/Logging/LogWriter.cs b/Source/Core/Runtime/Logging/LogManager.cs similarity index 74% rename from Source/Core/Runtime/Logging/LogWriter.cs rename to Source/Core/Runtime/Logging/LogManager.cs index 42736e086..78ba15baf 100644 --- a/Source/Core/Runtime/Logging/LogWriter.cs +++ b/Source/Core/Runtime/Logging/LogManager.cs @@ -3,55 +3,20 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; namespace Microsoft.Coyote.Runtime { /// - /// Manages the installed and all registered objects. + /// Manages all registered objects. /// - internal class LogWriter + internal class LogManager { /// /// The set of registered log writers. /// - protected readonly HashSet Logs; - - /// - /// Used to log messages. - /// - protected internal ILogger Logger { get; protected set; } - - /// - /// The log level to report. - /// - protected readonly LogSeverity LogLevel; - - /// - /// Initializes a new instance of the class. - /// - internal LogWriter(Configuration configuration, RuntimeLogTextFormatter textFormatter) - { - this.Logs = new HashSet(); - this.LogLevel = configuration.LogLevel; - - if (configuration.IsVerbose) - { - this.Logger = new ConsoleLogger() - { - LogLevel = this.LogLevel - }; - - textFormatter.Logger = this.Logger; - this.Logs.Add(textFormatter); - } - else - { - this.Logger = new NullLogger(); - } - } + protected readonly HashSet Logs = new HashSet(); /// /// Logs that the specified monitor has been created. @@ -224,22 +189,19 @@ internal void LogCompletion() /// /// Use this method to register an . /// - internal void RegisterLog(IRuntimeLog log) + internal void RegisterLog(IRuntimeLog log, LogWriter logWriter) { if (log is null) { throw new InvalidOperationException("Cannot register a null log."); } - // Make sure we only have one text logger by replacing the previous one, if it exists. if (log is RuntimeLogTextFormatter textFormatter) { - var previousTextFormatter = this.GetLogsOfType().FirstOrDefault(); - this.RemoveLog(previousTextFormatter); - if (this.Logger != null) - { - textFormatter.Logger = this.Logger; - } + textFormatter.LogWriter = logWriter; + + // Ensure we only have one text formatter by replacing the previous one, if it exists. + this.RemoveLog(this.GetLogsOfType().FirstOrDefault()); } this.Logs.Add(log); @@ -262,39 +224,5 @@ internal void RemoveLog(IRuntimeLog log) internal IEnumerable GetLogsOfType() where TRuntimeLog : IRuntimeLog => this.Logs.OfType(); - - /// - /// Use this method to override the default for logging messages. - /// - internal ILogger SetLogger(ILogger logger) - { - var previousLogger = this.Logger; - this.Logger = logger ?? new NullLogger(); - - var textFormatter = this.GetLogsOfType().FirstOrDefault(); - if (this.Logger is NullLogger) - { - this.RemoveLog(textFormatter); - } - else if (textFormatter is null) - { - this.Logs.Add(this.CreateLogTextFormatter(this.Logger)); - } - else - { - textFormatter.Logger = this.Logger; - } - - return previousLogger; - } - - /// - /// Creates a new . - /// - protected virtual RuntimeLogTextFormatter CreateLogTextFormatter(ILogger logger) => - new RuntimeLogTextFormatter() - { - Logger = logger - }; } } diff --git a/Source/Core/Runtime/Logging/RuntimeLogTextFormatter.cs b/Source/Core/Runtime/Logging/RuntimeLogTextFormatter.cs index ed61bfbdc..4c01823d4 100644 --- a/Source/Core/Runtime/Logging/RuntimeLogTextFormatter.cs +++ b/Source/Core/Runtime/Logging/RuntimeLogTextFormatter.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. using System.Threading; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; namespace Microsoft.Coyote.Runtime { @@ -15,52 +15,35 @@ namespace Microsoft.Coyote.Runtime public class RuntimeLogTextFormatter : IRuntimeLog { /// - /// Get or set the interface to the logger. + /// Logs messages using the installed . /// - /// - /// If you want Coyote to log to an existing TextWriter, then use the object - /// but that will have a minor performance overhead, so it is better to use directly. - /// - public ILogger Logger { get; set; } + internal LogWriter LogWriter; /// - /// Initializes a new instance of the class. + /// Used for logging runtime messages. /// - public RuntimeLogTextFormatter() - { - this.Logger = new ConsoleLogger(); - } + protected ILogger Logger => this.LogWriter; /// public virtual void OnCreateMonitor(string monitorType) { - var text = $" {monitorType} was created."; - this.Logger.WriteLine(text); + this.Logger.WriteLine(" {0} was created.", monitorType); } /// - public virtual void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) - { - string text = $" {monitorType} executed action '{actionName}' in state '{stateName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) => + this.Logger.WriteLine(" {0} executed action '{1}' in state '{2}'.", monitorType, actionName, stateName); /// public virtual void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, - string senderType, string senderStateName, Event e) - { - string eventName = e.GetType().FullName; - string text = $" {monitorType} is processing event '{eventName}' in state '{stateName}'."; - this.Logger.WriteLine(text); - } + string senderType, string senderStateName, Event e) => + this.Logger.WriteLine(" {0} is processing event '{1}' in state '{2}'.", + monitorType, e.GetType().FullName, stateName); /// - public virtual void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) - { - string eventName = e.GetType().FullName; - string text = $" {monitorType} raised event '{eventName}' in state '{stateName}'."; - this.Logger.WriteLine(text); - } + public virtual void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) => + this.Logger.WriteLine(" {0} raised event '{1}' in state '{2}'.", + monitorType, e.GetType().FullName, stateName); /// public virtual void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) @@ -72,32 +55,25 @@ public virtual void OnMonitorStateTransition(string monitorType, string stateNam } /// - public virtual void OnMonitorError(string monitorType, string stateName, bool? isInHotState) - { - this.Logger.WriteLine(LogSeverity.Error, $" {monitorType} found an error in state {stateName}."); - } + public virtual void OnMonitorError(string monitorType, string stateName, bool? isInHotState) => + this.Logger.WriteLine(LogSeverity.Error, " {0} found an error in state {1}.", monitorType, stateName); /// public virtual void OnRandom(bool result, string callerName, string callerType) { var source = callerName ?? $"Thread '{Thread.CurrentThread.ManagedThreadId}'"; - var text = $" {source} nondeterministically chose '{result}'."; - this.Logger.WriteLine(text); + this.Logger.WriteLine(" {0} non-deterministically chose '{1}'.", source, result); } /// public virtual void OnRandom(int result, string callerName, string callerType) { var source = callerName ?? $"Thread '{Thread.CurrentThread.ManagedThreadId}'"; - var text = $" {source} nondeterministically chose '{result}'."; - this.Logger.WriteLine(text); + this.Logger.WriteLine(" {0} non-deterministically chose '{1}'.", source, result); } /// - public virtual void OnAssertionFailure(string error) - { - this.Logger.WriteLine(LogSeverity.Error, error); - } + public virtual void OnAssertionFailure(string error) => this.Logger.WriteLine(LogSeverity.Error, error); /// public virtual void OnCompleted() diff --git a/Source/Core/Runtime/RuntimeProvider.cs b/Source/Core/Runtime/RuntimeProvider.cs index 1d2be1dce..65260b412 100644 --- a/Source/Core/Runtime/RuntimeProvider.cs +++ b/Source/Core/Runtime/RuntimeProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Threading; +using Microsoft.Coyote.Logging; namespace Microsoft.Coyote.Runtime { @@ -21,7 +22,7 @@ public static class RuntimeProvider /// /// The default installed runtime instance. /// - internal static CoyoteRuntime Default { get; private set; } = CreateWithConfiguration(default); + internal static CoyoteRuntime Default { get; private set; } = CreateWithConfiguration(default, default, default); /// /// The runtime installed in the current execution context. @@ -41,7 +42,7 @@ public static class RuntimeProvider /// Only one Coyote runtime can be used per process. If you create a new Coyote runtime /// it replaces the previously installed one. This is a thread-safe operation. /// - public static ICoyoteRuntime Create() => CreateAndInstall(default).DefaultActorExecutionContext; + public static ICoyoteRuntime Create() => CreateAndInstall(default, default, default).DefaultActorExecutionContext; /// /// Creates a new Coyote runtime with the specified . @@ -53,36 +54,39 @@ public static class RuntimeProvider /// it replaces the previously installed one. This is a thread-safe operation. /// public static ICoyoteRuntime Create(Configuration configuration) => - CreateAndInstall(configuration).DefaultActorExecutionContext; + CreateAndInstall(configuration, default, default).DefaultActorExecutionContext; /// - /// Creates a new Coyote runtime with the specified and sets - /// it as the default installed runtime, or returns the runtime if it already exists. + /// Creates a new Coyote runtime with the specified and sets it + /// as the default installed runtime, or returns the runtime if it already exists. /// /// /// This is a thread-safe operation. /// - internal static CoyoteRuntime CreateAndInstall(Configuration configuration) + internal static CoyoteRuntime CreateAndInstall(Configuration configuration, LogWriter logWriter, LogManager logManager) { lock (SyncObject) { // Assign the newly created runtime as the default installed runtime. - return Default = CreateWithConfiguration(configuration); + return Default = CreateWithConfiguration(configuration, logWriter, logManager); } } /// /// Creates a new Coyote runtime with the specified . /// - private static CoyoteRuntime CreateWithConfiguration(Configuration configuration) + private static CoyoteRuntime CreateWithConfiguration(Configuration configuration, LogWriter logWriter, LogManager logManager) { - if (configuration is null) + configuration ??= Configuration.Create(); + var valueGenerator = new RandomValueGenerator(configuration); + logWriter ??= new LogWriter(configuration); + if (logManager is null) { - configuration = Configuration.Create(); + logManager = new LogManager(); + logManager.RegisterLog(new RuntimeLogTextFormatter(), logWriter); } - var valueGenerator = new RandomValueGenerator(configuration); - return new CoyoteRuntime(configuration, valueGenerator); + return CoyoteRuntime.Create(configuration, valueGenerator, logWriter, logManager); } /// diff --git a/Source/Core/Runtime/Scheduling/ControlledSynchronizationContext.cs b/Source/Core/Runtime/Scheduling/ControlledSynchronizationContext.cs index d1ca98a5f..c51933dd7 100644 --- a/Source/Core/Runtime/Scheduling/ControlledSynchronizationContext.cs +++ b/Source/Core/Runtime/Scheduling/ControlledSynchronizationContext.cs @@ -34,7 +34,7 @@ public override void Post(SendOrPostCallback d, object state) { try { - IO.Debug.WriteLine("[coyote::debug] Posting callback from thread '{0}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Posting callback from thread '{0}'.", Thread.CurrentThread.ManagedThreadId); this.Runtime?.Schedule(() => d(state)); } diff --git a/Source/Core/Runtime/Scheduling/ControlledTaskScheduler.cs b/Source/Core/Runtime/Scheduling/ControlledTaskScheduler.cs index e2764338e..3c34a2d63 100644 --- a/Source/Core/Runtime/Scheduling/ControlledTaskScheduler.cs +++ b/Source/Core/Runtime/Scheduling/ControlledTaskScheduler.cs @@ -39,7 +39,7 @@ protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQu /// protected override void QueueTask(Task task) { - IO.Debug.WriteLine("[coyote::debug] Enqueuing task '{0}' from thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Enqueuing task '{0}' from thread '{1}'.", task.Id, Thread.CurrentThread.ManagedThreadId); this.Runtime?.Schedule(task); } diff --git a/Source/Core/Runtime/Scheduling/OperationScheduler.cs b/Source/Core/Runtime/Scheduling/OperationScheduler.cs index 85db516a5..aed50d502 100644 --- a/Source/Core/Runtime/Scheduling/OperationScheduler.cs +++ b/Source/Core/Runtime/Scheduling/OperationScheduler.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.Specifications; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Testing; using Microsoft.Coyote.Testing.Fuzzing; using Microsoft.Coyote.Testing.Interleaving; @@ -75,14 +75,13 @@ internal sealed class OperationScheduler /// /// Initializes a new instance of the class. /// - private OperationScheduler(Configuration configuration, SchedulingPolicy policy, IRandomValueGenerator generator, - ExecutionTrace prefixTrace) + private OperationScheduler(Configuration configuration, SchedulingPolicy policy, IRandomValueGenerator generator, ExecutionTrace prefixTrace) { this.Configuration = configuration; this.SchedulingPolicy = policy; + this.PrefixTrace = prefixTrace; this.ValueGenerator = generator; this.Trace = ExecutionTrace.Create(); - this.PrefixTrace = prefixTrace; this.Reducers = new List(); if (configuration.IsSharedStateReductionEnabled) @@ -98,13 +97,15 @@ private OperationScheduler(Configuration configuration, SchedulingPolicy policy, if (this.SchedulingPolicy is SchedulingPolicy.Interleaving) { - this.Strategy = InterleavingStrategy.Create(configuration, generator, prefixTrace); + this.Strategy = InterleavingStrategy.Create(configuration, prefixTrace); this.IsReplaying = prefixTrace.Length > 0; } else if (this.SchedulingPolicy is SchedulingPolicy.Fuzzing) { - this.Strategy = FuzzingStrategy.Create(configuration, generator); + this.Strategy = FuzzingStrategy.Create(configuration); } + + this.Strategy.RandomValueGenerator = generator; } /// @@ -123,13 +124,15 @@ internal static OperationScheduler Setup(Configuration configuration, Scheduling new OperationScheduler(configuration, policy, valueGenerator, ExecutionTrace.Create()); /// - /// Initializes the next iteration. + /// Initializes the next test iteration. /// /// The id of the next iteration. - /// True to start the specified iteration, else false to stop exploring. - internal bool InitializeNextIteration(uint iteration) + /// The log writer associated with the current test iteration. + /// True to start the specified test iteration, else false to stop exploring. + internal bool InitializeNextIteration(uint iteration, LogWriter logWriter) { this.Trace.Clear(); + this.Strategy.LogWriter = logWriter; return this.Strategy.InitializeNextIteration(iteration); } diff --git a/Source/Core/Specifications/Monitors/Monitor.cs b/Source/Core/Specifications/Monitors/Monitor.cs index 5d1e09171..43c8c9791 100644 --- a/Source/Core/Specifications/Monitors/Monitor.cs +++ b/Source/Core/Specifications/Monitors/Monitor.cs @@ -10,7 +10,7 @@ using System.Runtime.ExceptionServices; using Microsoft.Coyote.Actors; using Microsoft.Coyote.Actors.Coverage; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Specifications @@ -96,18 +96,13 @@ public abstract class Monitor /// internal string Name => this.GetType().FullName; - /// - /// Responsible for logging. - /// - private LogWriter LogWriter; - /// /// The logger installed to the runtime. /// /// - /// See Logging for more information. + /// See Logging for more information. /// - protected ILogger Logger => this.LogWriter.Logger; + protected ILogger Logger => this.Runtime.LogWriter; /// /// Gets the current state. @@ -163,11 +158,10 @@ protected Monitor() /// /// Initializes this monitor. /// - internal void Initialize(Configuration configuration, CoyoteRuntime runtime, LogWriter logWriter) + internal void Initialize(Configuration configuration, CoyoteRuntime runtime) { this.Configuration = configuration; this.Runtime = runtime; - this.LogWriter = logWriter; } /// @@ -304,7 +298,7 @@ protected void Assert(bool predicate, string s, params object[] args) /// internal void MonitorEvent(Event e, string senderName, string senderType, string senderState) { - this.LogWriter.LogMonitorProcessEvent(this.Name, this.CurrentStateName, senderName, senderType, senderState, e); + this.Runtime.LogManager.LogMonitorProcessEvent(this.Name, this.CurrentStateName, senderName, senderType, senderState, e); this.HandleEvent(e); } @@ -692,7 +686,7 @@ internal int GetHashedState() /// internal void GotoStartState() { - this.LogWriter.LogCreateMonitor(this.Name); + this.Runtime.LogManager.LogCreateMonitor(this.Name); if (this.Runtime.SchedulingPolicy is SchedulingPolicy.Interleaving && this.Configuration.IsActivityCoverageReported) { @@ -935,61 +929,34 @@ private MethodInfo GetActionWithName(string actionName) /// /// Logs that the monitor entered a state. /// - private void LogEnteredState(Monitor monitor) - { - if (this.Configuration.IsVerbose || CoyoteRuntime.IsExecutionControlled) - { - string monitorState = monitor.CurrentStateName; - this.LogWriter.LogMonitorStateTransition(monitor.GetType().FullName, monitorState, true, monitor.GetHotState()); - } - } + private void LogEnteredState(Monitor monitor) => + this.Runtime.LogManager.LogMonitorStateTransition(monitor.GetType().FullName, + monitor.CurrentStateName, true, monitor.GetHotState()); /// /// Logs that the monitor exited a state. /// - private void LogExitedState(Monitor monitor) - { - if (this.Configuration.IsVerbose || CoyoteRuntime.IsExecutionControlled) - { - string monitorState = monitor.CurrentStateName; - this.LogWriter.LogMonitorStateTransition(monitor.GetType().FullName, monitorState, false, monitor.GetHotState()); - } - } + private void LogExitedState(Monitor monitor) => + this.Runtime.LogManager.LogMonitorStateTransition(monitor.GetType().FullName, + monitor.CurrentStateName, false, monitor.GetHotState()); /// /// Logs that the monitor invoked an action. /// - private void LogInvokedAction(Monitor monitor, MethodInfo action, string stateName) - { - if (this.Configuration.IsVerbose || CoyoteRuntime.IsExecutionControlled) - { - this.LogWriter.LogMonitorExecuteAction(monitor.GetType().FullName, stateName, action.Name); - } - } + private void LogInvokedAction(Monitor monitor, MethodInfo action, string stateName) => + this.Runtime.LogManager.LogMonitorExecuteAction(monitor.GetType().FullName, stateName, action.Name); /// /// Logs that the monitor raised an . /// - private void LogRaisedEvent(Monitor monitor, Event e) - { - if (this.Configuration.IsVerbose || CoyoteRuntime.IsExecutionControlled) - { - string monitorState = monitor.CurrentStateNameWithTemperature; - this.LogWriter.LogMonitorRaiseEvent(monitor.GetType().FullName, monitorState, e); - } - } + private void LogRaisedEvent(Monitor monitor, Event e) => + this.Runtime.LogManager.LogMonitorRaiseEvent(monitor.GetType().FullName, monitor.CurrentStateNameWithTemperature, e); /// /// Logs that the monitor found an error. /// - private void LogMonitorError(Monitor monitor) - { - if (this.Configuration.IsVerbose || CoyoteRuntime.IsExecutionControlled) - { - string monitorState = monitor.CurrentStateName; - this.LogWriter.LogMonitorError(monitor.GetType().FullName, monitorState, monitor.GetHotState()); - } - } + private void LogMonitorError(Monitor monitor) => + this.Runtime.LogManager.LogMonitorError(monitor.GetType().FullName, monitor.CurrentStateName, monitor.GetHotState()); /// /// Reports the activity coverage of this monitor. diff --git a/Source/Core/Testing/ExplorationStrategy.cs b/Source/Core/Testing/ExplorationStrategy.cs index 3f1fdcb66..be86402ed 100644 --- a/Source/Core/Testing/ExplorationStrategy.cs +++ b/Source/Core/Testing/ExplorationStrategy.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.Coyote.Logging; + namespace Microsoft.Coyote.Testing { /// @@ -13,11 +15,6 @@ internal abstract class ExplorationStrategy /// protected readonly Configuration Configuration; - /// - /// A random value generator that can be used by the strategy. - /// - protected readonly IRandomValueGenerator RandomValueGenerator; - /// /// The maximum number of steps to explore. /// @@ -33,6 +30,16 @@ internal abstract class ExplorationStrategy /// internal readonly bool IsFair; + /// + /// A random value generator that can be used by the strategy. + /// + protected internal IRandomValueGenerator RandomValueGenerator { get; internal set; } + + /// + /// Responsible for writing to the installed . + /// + protected internal LogWriter LogWriter { get; internal set; } + /// /// Text describing the last exploration error, if there was any. /// @@ -41,10 +48,9 @@ internal abstract class ExplorationStrategy /// /// Initializes a new instance of the class. /// - protected ExplorationStrategy(Configuration configuration, IRandomValueGenerator generator, bool isFair) + protected ExplorationStrategy(Configuration configuration, bool isFair) { this.Configuration = configuration; - this.RandomValueGenerator = generator; this.MaxSteps = isFair ? configuration.MaxFairSchedulingSteps : configuration.MaxUnfairSchedulingSteps; this.StepCount = 0; this.IsFair = isFair; diff --git a/Source/Core/Testing/Fuzzing/BoundedRandomStrategy.cs b/Source/Core/Testing/Fuzzing/BoundedRandomStrategy.cs index 19c31e80f..d671d3813 100644 --- a/Source/Core/Testing/Fuzzing/BoundedRandomStrategy.cs +++ b/Source/Core/Testing/Fuzzing/BoundedRandomStrategy.cs @@ -21,8 +21,8 @@ internal class BoundedRandomStrategy : FuzzingStrategy /// /// Initializes a new instance of the class. /// - internal BoundedRandomStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator, false) + internal BoundedRandomStrategy(Configuration configuration) + : base(configuration, false) { this.TotalTaskDelayMap = new ConcurrentDictionary(); } diff --git a/Source/Core/Testing/Fuzzing/FuzzingStrategy.cs b/Source/Core/Testing/Fuzzing/FuzzingStrategy.cs index c3b487b21..4458b3b54 100644 --- a/Source/Core/Testing/Fuzzing/FuzzingStrategy.cs +++ b/Source/Core/Testing/Fuzzing/FuzzingStrategy.cs @@ -28,8 +28,8 @@ internal abstract class FuzzingStrategy : ExplorationStrategy /// /// Initializes a new instance of the class. /// - internal FuzzingStrategy(Configuration configuration, IRandomValueGenerator generator, bool isFair) - : base(configuration, generator, isFair) + internal FuzzingStrategy(Configuration configuration, bool isFair) + : base(configuration, isFair) { this.OperationIdMap = new ConcurrentDictionary(); } @@ -37,15 +37,15 @@ internal FuzzingStrategy(Configuration configuration, IRandomValueGenerator gene /// /// Creates a from the specified configuration. /// - internal static FuzzingStrategy Create(Configuration configuration, IRandomValueGenerator generator) + internal static FuzzingStrategy Create(Configuration configuration) { switch (configuration.SchedulingStrategy) { case "prioritization": - return new PrioritizationStrategy(configuration, generator); + return new PrioritizationStrategy(configuration); default: - // return new RandomStrategy(configuration, generator); - return new BoundedRandomStrategy(configuration, generator); + // return new RandomStrategy(configuration); + return new BoundedRandomStrategy(configuration); } } diff --git a/Source/Core/Testing/Fuzzing/PrioritizationStrategy.cs b/Source/Core/Testing/Fuzzing/PrioritizationStrategy.cs index db322a034..320e8184b 100644 --- a/Source/Core/Testing/Fuzzing/PrioritizationStrategy.cs +++ b/Source/Core/Testing/Fuzzing/PrioritizationStrategy.cs @@ -38,8 +38,8 @@ internal class PrioritizationStrategy : FuzzingStrategy /// /// Initializes a new instance of the class. /// - internal PrioritizationStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator, false) + internal PrioritizationStrategy(Configuration configuration) + : base(configuration, false) { this.PriorityChangePoints = configuration.StrategyBound; this.HighPrioritySet = new List(); diff --git a/Source/Core/Testing/Fuzzing/RandomStrategy.cs b/Source/Core/Testing/Fuzzing/RandomStrategy.cs index 5ce5b0adf..b2580aead 100644 --- a/Source/Core/Testing/Fuzzing/RandomStrategy.cs +++ b/Source/Core/Testing/Fuzzing/RandomStrategy.cs @@ -14,8 +14,8 @@ internal class RandomStrategy : FuzzingStrategy /// /// Initializes a new instance of the class. /// - internal RandomStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator, true) + internal RandomStrategy(Configuration configuration) + : base(configuration, true) { } diff --git a/Source/Core/Testing/Interleaving/DFSStrategy.cs b/Source/Core/Testing/Interleaving/DFSStrategy.cs index 65689fa84..ebe8b70a1 100644 --- a/Source/Core/Testing/Interleaving/DFSStrategy.cs +++ b/Source/Core/Testing/Interleaving/DFSStrategy.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using System.Text; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Testing.Interleaving @@ -41,8 +41,8 @@ internal sealed class DFSStrategy : InterleavingStrategy /// /// Initializes a new instance of the class. /// - internal DFSStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator, false) + internal DFSStrategy(Configuration configuration) + : base(configuration, false) { this.SchIndex = 0; this.NondetIndex = 0; @@ -270,46 +270,51 @@ internal override bool NextInteger(ControlledOperation current, int maxValue, ou /// private void DebugPrintSchedule() { - Debug.WriteLine("*******************"); - Debug.WriteLine("Schedule stack size: " + this.ScheduleStack.Count); - for (int idx = 0; idx < this.ScheduleStack.Count; idx++) + this.LogWriter.LogDebug(() => { - Debug.WriteLine("Index: " + idx); - foreach (var sc in this.ScheduleStack[idx]) + StringBuilder sb = new StringBuilder(); + sb.AppendLine("*******************"); + sb.AppendLine($"Schedule stack size: {this.ScheduleStack.Count}"); + for (int idx = 0; idx < this.ScheduleStack.Count; idx++) { - Debug.Write(sc.Id + " [" + sc.IsDone + "], "); - } + sb.AppendLine($"Index: {idx}"); + foreach (var sc in this.ScheduleStack[idx]) + { + sb.Append($"{sc.Id} [{sc.IsDone}], "); + } - Debug.WriteLine(string.Empty); - } + sb.AppendLine(); + } - Debug.WriteLine("*******************"); - Debug.WriteLine("Random bool stack size: " + this.BoolNondetStack.Count); - for (int idx = 0; idx < this.BoolNondetStack.Count; idx++) - { - Debug.WriteLine("Index: " + idx); - foreach (var nc in this.BoolNondetStack[idx]) + sb.AppendLine("*******************"); + sb.AppendLine($"Random bool stack size: {this.BoolNondetStack.Count}"); + for (int idx = 0; idx < this.BoolNondetStack.Count; idx++) { - Debug.Write(nc.Value + " [" + nc.IsDone + "], "); - } + sb.AppendLine($"Index: {idx}"); + foreach (var nc in this.BoolNondetStack[idx]) + { + sb.Append($"{nc.Value} [{nc.IsDone}], "); + } - Debug.WriteLine(string.Empty); - } + sb.AppendLine(); + } - Debug.WriteLine("*******************"); - Debug.WriteLine("Random int stack size: " + this.IntNondetStack.Count); - for (int idx = 0; idx < this.IntNondetStack.Count; idx++) - { - Debug.WriteLine("Index: " + idx); - foreach (var nc in this.IntNondetStack[idx]) + sb.AppendLine("*******************"); + sb.AppendLine($"Random int stack size: {this.IntNondetStack.Count}"); + for (int idx = 0; idx < this.IntNondetStack.Count; idx++) { - Debug.Write(nc.Value + " [" + nc.IsDone + "], "); - } + sb.AppendLine($"Index: {idx}"); + foreach (var nc in this.IntNondetStack[idx]) + { + sb.Append($"{nc.Value} [{nc.IsDone}], "); + } - Debug.WriteLine(string.Empty); - } + sb.AppendLine(); + } - Debug.WriteLine("*******************"); + sb.AppendLine("*******************"); + return sb.ToString(); + }); } /// diff --git a/Source/Core/Testing/Interleaving/InterleavingStrategy.cs b/Source/Core/Testing/Interleaving/InterleavingStrategy.cs index 75e5243f0..56daeed7e 100644 --- a/Source/Core/Testing/Interleaving/InterleavingStrategy.cs +++ b/Source/Core/Testing/Interleaving/InterleavingStrategy.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Testing.Interleaving @@ -22,40 +21,40 @@ internal abstract class InterleavingStrategy : ExplorationStrategy /// /// Initializes a new instance of the class. /// - protected InterleavingStrategy(Configuration configuration, IRandomValueGenerator generator, bool isFair) - : base(configuration, generator, isFair) + protected InterleavingStrategy(Configuration configuration, bool isFair) + : base(configuration, isFair) { } /// /// Creates a from the specified configuration. /// - internal static InterleavingStrategy Create(Configuration configuration, IRandomValueGenerator generator, ExecutionTrace tracePrefix) + internal static InterleavingStrategy Create(Configuration configuration, ExecutionTrace tracePrefix) { InterleavingStrategy strategy = null; if (configuration.SchedulingStrategy is "random") { - strategy = new RandomStrategy(configuration, generator); + strategy = new RandomStrategy(configuration); } else if (configuration.SchedulingStrategy is "prioritization") { - strategy = new PrioritizationStrategy(configuration, generator, false); + strategy = new PrioritizationStrategy(configuration, false); } else if (configuration.SchedulingStrategy is "fair-prioritization") { - strategy = new PrioritizationStrategy(configuration, generator, true); + strategy = new PrioritizationStrategy(configuration, true); } else if (configuration.SchedulingStrategy is "probabilistic") { - strategy = new ProbabilisticRandomStrategy(configuration, generator); + strategy = new ProbabilisticRandomStrategy(configuration); } else if (configuration.SchedulingStrategy is "rl") { - strategy = new QLearningStrategy(configuration, generator); + strategy = new QLearningStrategy(configuration); } else if (configuration.SchedulingStrategy is "dfs") { - strategy = new DFSStrategy(configuration, generator); + strategy = new DFSStrategy(configuration); } strategy.TracePrefix = tracePrefix; @@ -109,7 +108,7 @@ internal bool GetNextOperation(IEnumerable ops, ControlledO } catch (InvalidOperationException ex) { - Error.Report(ex.Message); + this.LogWriter.LogError(ex.Message); next = null; return false; } @@ -164,7 +163,7 @@ internal bool GetNextBoolean(ControlledOperation current, out bool next) } catch (InvalidOperationException ex) { - Error.Report(ex.Message); + this.LogWriter.LogError(ex.Message); next = false; return false; } @@ -217,7 +216,7 @@ internal bool GetNextInteger(ControlledOperation current, int maxValue, out int } catch (InvalidOperationException ex) { - Error.Report(ex.Message); + this.LogWriter.LogError(ex.Message); next = 0; return false; } diff --git a/Source/Core/Testing/Interleaving/PrioritizationStrategy.cs b/Source/Core/Testing/Interleaving/PrioritizationStrategy.cs index f2464412e..dd08f6bc0 100644 --- a/Source/Core/Testing/Interleaving/PrioritizationStrategy.cs +++ b/Source/Core/Testing/Interleaving/PrioritizationStrategy.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using System.Text; using Microsoft.Coyote.Runtime; namespace Microsoft.Coyote.Testing.Interleaving @@ -46,8 +46,8 @@ internal sealed class PrioritizationStrategy : RandomStrategy /// /// Initializes a new instance of the class. /// - internal PrioritizationStrategy(Configuration configuration, IRandomValueGenerator generator, bool isFair) - : base(configuration, generator, isFair) + internal PrioritizationStrategy(Configuration configuration, bool isFair) + : base(configuration, isFair) { this.PrioritizedOperationGroups = new List(); this.PriorityChangePoints = new HashSet(); @@ -155,7 +155,7 @@ private void SetNewOperationGroupPriorities(IEnumerable ops // Randomly choose a priority for this group. int index = this.RandomValueGenerator.Next(this.PrioritizedOperationGroups.Count) + 1; this.PrioritizedOperationGroups.Insert(index, group); - Debug.WriteLine("[coyote::strategy] Assigned priority '{0}' for operation group '{1}'.", index, group); + this.LogWriter.LogDebug("[coyote::strategy] Assigned priority '{0}' for operation group '{1}'.", index, group); } if (this.PrioritizedOperationGroups.Count > count) @@ -175,7 +175,7 @@ private bool TryPrioritizeNextOperationGroup(IEnumerable op { // This scheduling step was chosen as a priority change point. group = this.GetOperationGroupWithHighestPriority(ops); - Debug.WriteLine("[coyote::strategy] Reduced the priority of operation group '{0}'.", group); + this.LogWriter.LogDebug("[coyote::strategy] Reduced the priority of operation group '{0}'.", group); } this.NumPriorityChangePoints++; @@ -228,22 +228,25 @@ internal override void Reset() /// private void DebugPrintOperationPriorityList() { - if (Debug.IsEnabled) + this.LogWriter.LogDebug(() => { - Debug.WriteLine("[coyote::strategy] Updated operation group priority list: "); + var sb = new StringBuilder(); + sb.AppendLine("[coyote::strategy] Updated operation group priority list: "); for (int idx = 0; idx < this.PrioritizedOperationGroups.Count; idx++) { var group = this.PrioritizedOperationGroups[idx]; if (group.Any(m => m.Status is OperationStatus.Enabled)) { - Debug.WriteLine(" |_ [{0}] operation group with id '{1}' [enabled]", idx, group); + sb.AppendLine($" |_ [{idx}] operation group with id '{group}' [enabled]"); } else if (group.Any(m => m.Status != OperationStatus.Completed)) { - Debug.WriteLine(" |_ [{0}] operation group with id '{1}'", idx, group); + sb.AppendLine($" |_ [{idx}] operation group with id '{group}'"); } } - } + + return sb.ToString(); + }); } /// @@ -251,21 +254,24 @@ private void DebugPrintOperationPriorityList() /// private void DebugPrintPriorityChangePoints() { - if (Debug.IsEnabled && this.PriorityChangePoints.Count > 0) + this.LogWriter.LogDebug(() => { + var sb = new StringBuilder(); if (this.PriorityChangePoints.Count > 0) { // Sort them before printing for readability. var sortedChangePoints = this.PriorityChangePoints.ToArray(); Array.Sort(sortedChangePoints); - Debug.WriteLine("[coyote::strategy] Assigned {0} priority change points: {1}.", - sortedChangePoints.Length, string.Join(", ", sortedChangePoints)); + var points = string.Join(", ", sortedChangePoints); + sb.AppendLine($"[coyote::strategy] Assigned '{sortedChangePoints.Length}' priority change points: {points}."); } else { - Debug.WriteLine("[coyote::strategy] Assigned 0 priority change points."); + sb.AppendLine("[coyote::strategy] Assigned '0' priority change points."); } - } + + return sb.ToString(); + }); } } } diff --git a/Source/Core/Testing/Interleaving/ProbabilisticRandomStrategy.cs b/Source/Core/Testing/Interleaving/ProbabilisticRandomStrategy.cs index 7aa827bce..07def9700 100644 --- a/Source/Core/Testing/Interleaving/ProbabilisticRandomStrategy.cs +++ b/Source/Core/Testing/Interleaving/ProbabilisticRandomStrategy.cs @@ -21,8 +21,8 @@ internal sealed class ProbabilisticRandomStrategy : RandomStrategy /// /// Initializes a new instance of the class. /// - internal ProbabilisticRandomStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator) + internal ProbabilisticRandomStrategy(Configuration configuration) + : base(configuration) { this.Bound = configuration.StrategyBound; } diff --git a/Source/Core/Testing/Interleaving/QLearningStrategy.cs b/Source/Core/Testing/Interleaving/QLearningStrategy.cs index d785bcc88..a3dc3699d 100644 --- a/Source/Core/Testing/Interleaving/QLearningStrategy.cs +++ b/Source/Core/Testing/Interleaving/QLearningStrategy.cs @@ -84,8 +84,8 @@ internal sealed class QLearningStrategy : RandomStrategy /// Initializes a new instance of the class. /// It uses the specified random number generator. /// - public QLearningStrategy(Configuration configuration, IRandomValueGenerator generator) - : base(configuration, generator, false) + public QLearningStrategy(Configuration configuration) + : base(configuration, false) { this.OperationQTable = new Dictionary>(); this.ExecutionPath = new LinkedList<(ulong, SchedulingPointType, int)>(); diff --git a/Source/Core/Testing/Interleaving/RandomStrategy.cs b/Source/Core/Testing/Interleaving/RandomStrategy.cs index 53fe081b5..4a9c18ee8 100644 --- a/Source/Core/Testing/Interleaving/RandomStrategy.cs +++ b/Source/Core/Testing/Interleaving/RandomStrategy.cs @@ -15,8 +15,8 @@ internal class RandomStrategy : InterleavingStrategy /// /// Initializes a new instance of the class. /// - internal RandomStrategy(Configuration configuration, IRandomValueGenerator generator, bool isFair = true) - : base(configuration, generator, isFair) + internal RandomStrategy(Configuration configuration, bool isFair = true) + : base(configuration, isFair) { } diff --git a/Source/Core/Utilities/Documentation.cs b/Source/Core/Utilities/Documentation.cs new file mode 100644 index 000000000..246f90aeb --- /dev/null +++ b/Source/Core/Utilities/Documentation.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Coyote +{ + /// + /// Provides access to the Coyote documentation. + /// + internal static class Documentation + { + /// + /// Url with information on learning about coyote. + /// + internal const string LearnAboutCoyoteUrl = "https://aka.ms/learn-coyote"; + + /// + /// Url with information about what is new with coyote. + /// + internal const string LearnWhatIsNewUrl = "https://aka.ms/coyote-what-is-new"; + + /// + /// Url with information about the testing process. + /// + internal const string LearnAboutTestUrl = "https://aka.ms/coyote-test"; + + /// + /// Url with information about the replaying process. + /// + internal const string LearnAboutReplayUrl = "https://aka.ms/coyote-replay"; + + /// + /// Url with information about the rewriting process. + /// + internal const string LearnAboutRewritingUrl = "https://aka.ms/coyote-rewrite"; + + /// + /// Url with information about the gathered telemetry. + /// + internal const string LearnAboutTelemetryUrl = "https://aka.ms/coyote-telemetry"; + } +} diff --git a/Source/Test/Rewriting/AssemblyInfo.cs b/Source/Test/Rewriting/AssemblyInfo.cs index 515f8d4e6..5ab4c4a85 100644 --- a/Source/Test/Rewriting/AssemblyInfo.cs +++ b/Source/Test/Rewriting/AssemblyInfo.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Reflection; -using Microsoft.Coyote.IO; using Mono.Cecil; using Mono.Cecil.Cil; @@ -148,15 +147,15 @@ internal void Invoke(Pass pass) pass.VisitAssembly(this); foreach (var module in this.Definition.Modules) { - Debug.WriteLine($"....... Module: {module.Name} ({module.FileName})"); + pass.LogWriter.LogDebug("....... Module: {0} ({1})", module.Name, module.FileName); pass.VisitModule(module); foreach (var type in module.GetTypes()) { - Debug.WriteLine($"......... Type: {type.FullName}"); + pass.LogWriter.LogDebug("......... Type: {0}", type.FullName); pass.VisitType(type); foreach (var field in type.Fields.ToArray()) { - Debug.WriteLine($"........... Field: {field.FullName}"); + pass.LogWriter.LogDebug("........... Field: {0}", field.FullName); pass.VisitField(field); } @@ -167,7 +166,7 @@ internal void Invoke(Pass pass) continue; } - Debug.WriteLine($"........... Method {method.FullName}"); + pass.LogWriter.LogDebug("........... Method {0}", method.FullName); pass.VisitMethod(method); if (pass is RewritingPass rewritingPass && rewritingPass.IsMethodBodyModified) { diff --git a/Source/Test/Rewriting/Passes/AssemblyDiffingPass.cs b/Source/Test/Rewriting/Passes/AssemblyDiffingPass.cs index 225a17e13..5459bcee8 100644 --- a/Source/Test/Rewriting/Passes/AssemblyDiffingPass.cs +++ b/Source/Test/Rewriting/Passes/AssemblyDiffingPass.cs @@ -7,7 +7,7 @@ using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Mono.Cecil; using Mono.Cecil.Cil; @@ -26,8 +26,8 @@ internal class AssemblyDiffingPass : Pass /// /// Initializes a new instance of the class. /// - internal AssemblyDiffingPass(IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal AssemblyDiffingPass(IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { this.ContentMap = new Dictionary(); } @@ -148,7 +148,7 @@ internal string GetDiffJson(AssemblyInfo assembly, AssemblyDiffingPass pass) if (!this.ContentMap.TryGetValue(assembly.FullName, out AssemblyContents thisContents) || !pass.ContentMap.TryGetValue(assembly.FullName, out AssemblyContents otherContents)) { - this.Logger.WriteLine(LogSeverity.Error, "Unable to diff IL code that belongs to different assemblies."); + this.LogWriter.LogError("Unable to diff IL code that belongs to different assemblies."); return string.Empty; } @@ -181,7 +181,7 @@ private string GetJson(AssemblyContents contents) } catch (Exception ex) { - this.Logger.WriteLine(LogSeverity.Error, $"Unable to serialize IL to JSON. {ex.Message}"); + this.LogWriter.LogError("Unable to serialize IL to JSON. {0}", ex.Message); } return string.Empty; diff --git a/Source/Test/Rewriting/Passes/Pass.cs b/Source/Test/Rewriting/Passes/Pass.cs index 79ad51ce5..4a3092bd3 100644 --- a/Source/Test/Rewriting/Passes/Pass.cs +++ b/Source/Test/Rewriting/Passes/Pass.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Mono.Cecil; using Mono.Cecil.Cil; @@ -50,17 +50,17 @@ internal abstract class Pass private static readonly Dictionary CachedQualifiedNames = new Dictionary(); /// - /// The installed logger. + /// Responsible for writing to the installed . /// - protected readonly ILogger Logger; + protected internal readonly LogWriter LogWriter; /// /// Initializes a new instance of the class. /// - protected Pass(IEnumerable visitedAssemblies, ILogger logger) + protected Pass(IEnumerable visitedAssemblies, LogWriter logWriter) { this.VisitedAssemblies = visitedAssemblies; - this.Logger = logger; + this.LogWriter = logWriter; } /// @@ -184,7 +184,7 @@ protected bool TryResolve(MethodReference method, out MethodDefinition resolved, if (logError && resolved is null && method != null) { - this.Logger.WriteLine(LogSeverity.Warning, $"Unable to resolve the '{method.FullName}' method. " + + this.LogWriter.LogWarning($"Unable to resolve the '{method.FullName}' method. " + "The method is either unsupported by Coyote, an external method not being rewritten, or the " + ".NET platform of Coyote and the target assembly do not match."); } @@ -209,7 +209,7 @@ protected bool TryResolve(TypeReference type, out TypeDefinition resolved, bool if (logError && resolved is null && type != null) { - this.Logger.WriteLine(LogSeverity.Warning, $"Unable to resolve the '{type.FullName}' type. " + + this.LogWriter.LogWarning($"Unable to resolve the '{type.FullName}' type. " + "The type is either unsupported by Coyote, an external type not being rewritten, or the " + ".NET platform of Coyote and the target assembly do not match."); } diff --git a/Source/Test/Rewriting/Passes/Rewriting/ExceptionFilterRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/ExceptionFilterRewritingPass.cs index 80476b71b..242bc9027 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/ExceptionFilterRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/ExceptionFilterRewritingPass.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting.Types; using Microsoft.Coyote.Runtime; using Mono.Cecil; @@ -26,8 +26,8 @@ internal class ExceptionFilterRewritingPass : RewritingPass /// /// Initializes a new instance of the class. /// - internal ExceptionFilterRewritingPass(IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal ExceptionFilterRewritingPass(IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { } @@ -103,7 +103,8 @@ private void AddThrowIfExecutionCanceledException(ExceptionHandler handler) this.IsMethodBodyModified = true; } - Debug.WriteLine($"............. [+] rewriting catch block to rethrow a {nameof(ThreadInterruptedException)}"); + this.LogWriter.LogDebug("............. [+] rewriting catch block to rethrow a {0}", + nameof(ThreadInterruptedException)); var providerType = this.Method.Module.ImportReference(typeof(ExceptionProvider)).Resolve(); MethodReference providerMethod = providerType.Methods.FirstOrDefault( diff --git a/Source/Test/Rewriting/Passes/Rewriting/InterAssemblyInvocationRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/InterAssemblyInvocationRewritingPass.cs index bfa3d5755..9732df148 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/InterAssemblyInvocationRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/InterAssemblyInvocationRewritingPass.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Mono.Cecil; using Mono.Cecil.Cil; @@ -25,8 +25,8 @@ internal class InterAssemblyInvocationRewritingPass : RewritingPass /// /// Initializes a new instance of the class. /// - internal InterAssemblyInvocationRewritingPass(IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal InterAssemblyInvocationRewritingPass(IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { } @@ -55,7 +55,7 @@ instruction.Operand is MethodReference methodReference && interceptionMethod, nextInstruction, interceptedReturnType, methodName); if (instructions.Count > 0) { - Debug.WriteLine($"............. [+] uncontrolled task assertion when invoking '{methodName}'"); + this.LogWriter.LogDebug("............. [+] uncontrolled task assertion when invoking '{0}'", methodName); instructions.ForEach(i => this.Processor.InsertBefore(nextInstruction, i)); this.IsMethodBodyModified = true; } @@ -72,7 +72,7 @@ instruction.Operand is MethodReference methodReference && interceptionMethod, nextInstruction, interceptedReturnType, methodName); if (instructions.Count > 0) { - Debug.WriteLine($"............. [+] uncontrolled value task assertion when invoking '{methodName}'"); + this.LogWriter.LogDebug("............. [+] uncontrolled value task assertion when invoking '{0}'", methodName); instructions.ForEach(i => this.Processor.InsertBefore(nextInstruction, i)); this.IsMethodBodyModified = true; } @@ -84,7 +84,7 @@ instruction.Operand is MethodReference methodReference && typeof(TaskAwaiter), methodReference, nameof(TaskAwaiter.Wrap)); Instruction newInstruction = Instruction.Create(OpCodes.Call, interceptionMethod); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); this.Processor.InsertAfter(instruction, newInstruction); this.IsMethodBodyModified = true; @@ -96,7 +96,7 @@ instruction.Operand is MethodReference methodReference && typeof(ValueTaskAwaiter), methodReference, nameof(ValueTaskAwaiter.Wrap)); Instruction newInstruction = Instruction.Create(OpCodes.Call, interceptionMethod); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); this.Processor.InsertAfter(instruction, newInstruction); this.IsMethodBodyModified = true; @@ -107,7 +107,7 @@ instruction.Operand is MethodReference methodReference && MethodReference interceptionMethod = this.CreateInterceptionMethod( typeof(HttpClient), methodReference, nameof(HttpClient.Control)); Instruction newInstruction = Instruction.Create(OpCodes.Call, interceptionMethod); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); this.Processor.InsertAfter(instruction, newInstruction); this.IsMethodBodyModified = true; diff --git a/Source/Test/Rewriting/Passes/Rewriting/MSTestRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/MSTestRewritingPass.cs index 20ef6b876..d2b86cdc8 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/MSTestRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/MSTestRewritingPass.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.SystematicTesting; using Mono.Cecil; using Mono.Cecil.Cil; @@ -22,8 +22,8 @@ internal class MSTestRewritingPass : RewritingPass /// /// Initializes a new instance of the class. /// - internal MSTestRewritingPass(Configuration configuration, IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal MSTestRewritingPass(Configuration configuration, IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { this.Configuration = configuration; } @@ -52,15 +52,15 @@ protected internal override void VisitMethod(MethodDefinition method) if (isTestMethod) { - Debug.WriteLine($"............. [-] test method '{method.Name}'"); + this.LogWriter.LogDebug("............. [-] test method '{0}'", method.Name); MethodDefinition newMethod = CloneMethod(method); this.RewriteTestMethod(method, newMethod); method.DeclaringType.Methods.Add(newMethod); - Debug.WriteLine($"............. [+] systematic test method '{method.Name}'"); - Debug.WriteLine($"............. [+] test method '{newMethod.Name}'"); + this.LogWriter.LogDebug("............. [+] systematic test method '{0}'", method.Name); + this.LogWriter.LogDebug("............. [+] test method '{0}'", newMethod.Name); } base.VisitMethod(method); @@ -242,12 +242,8 @@ internal void RewriteTestMethod(MethodDefinition method, MethodDefinition testMe this.Configuration.RandomGeneratorSeed.Value); } - if (this.Configuration.IsVerbose) - { - this.EmitMethodCall(processor, resolvedConfigurationType, "WithVerbosityEnabled", - this.Configuration.IsVerbose, this.Configuration.LogLevel); - } - + this.EmitMethodCall(processor, resolvedConfigurationType, "WithVerbosityEnabled", + this.Configuration.VerbosityLevel); if (!this.Configuration.IsTelemetryEnabled) { this.EmitMethodCall(processor, resolvedConfigurationType, "WithTelemetryEnabled", diff --git a/Source/Test/Rewriting/Passes/Rewriting/RewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/RewritingPass.cs index 5afe0500d..b5c78fad3 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/RewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/RewritingPass.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; @@ -23,8 +23,8 @@ internal abstract class RewritingPass : Pass /// /// Initializes a new instance of the class. /// - protected RewritingPass(IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + protected RewritingPass(IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { } diff --git a/Source/Test/Rewriting/Passes/Rewriting/Types/MemberTypeRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/Types/MemberTypeRewritingPass.cs index eff3f9471..b9c0e3781 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/Types/MemberTypeRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/Types/MemberTypeRewritingPass.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. using System.Collections.Generic; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Mono.Cecil; namespace Microsoft.Coyote.Rewriting @@ -15,8 +15,8 @@ internal sealed class MemberTypeRewritingPass : TypeRewritingPass /// /// Initializes a new instance of the class. /// - internal MemberTypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, ILogger logger) - : base(options, visitedAssemblies, logger) + internal MemberTypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, LogWriter logWriter) + : base(options, visitedAssemblies, logWriter) { } @@ -26,9 +26,9 @@ protected internal override void VisitField(FieldDefinition field) if (this.TryRewriteType(field.FieldType, out TypeReference newFieldType) && this.TryResolve(newFieldType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] field '{field}'"); + this.LogWriter.LogDebug("............. [-] field '{0}'", field); field.FieldType = newFieldType; - Debug.WriteLine($"............. [+] field '{field}'"); + this.LogWriter.LogDebug("............. [+] field '{0}'", field); } } @@ -40,9 +40,9 @@ protected internal override void VisitMethod(MethodDefinition method) if (this.TryRewriteType(method.ReturnType, out TypeReference newReturnType) && this.TryResolve(newReturnType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] return type '{method.ReturnType}'"); + this.LogWriter.LogDebug("............. [-] return type '{0}'", method.ReturnType); method.ReturnType = newReturnType; - Debug.WriteLine($"............. [+] return type '{method.ReturnType}'"); + this.LogWriter.LogDebug("............. [+] return type '{0}'", method.ReturnType); } if (method.HasParameters) @@ -53,9 +53,9 @@ protected internal override void VisitMethod(MethodDefinition method) if (this.TryRewriteType(parameter.ParameterType, out TypeReference newParameterType) && this.TryResolve(newParameterType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] parameter '{parameter.ParameterType} {parameter.Name}'"); + this.LogWriter.LogDebug("............. [-] parameter '{0} {1}'", parameter.ParameterType, parameter.Name); parameter.ParameterType = newParameterType; - Debug.WriteLine($"............. [+] parameter '{parameter.ParameterType} {parameter.Name}'"); + this.LogWriter.LogDebug("............. [+] parameter '{0} {1}'", parameter.ParameterType, parameter.Name); } } } diff --git a/Source/Test/Rewriting/Passes/Rewriting/Types/MethodBodyTypeRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/Types/MethodBodyTypeRewritingPass.cs index 40b60ae06..8b3f0beb2 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/Types/MethodBodyTypeRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/Types/MethodBodyTypeRewritingPass.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. using System.Collections.Generic; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; @@ -14,9 +14,8 @@ internal sealed class MethodBodyTypeRewritingPass : TypeRewritingPass /// /// Initializes a new instance of the class. /// - internal MethodBodyTypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, - ILogger logger) - : base(options, visitedAssemblies, logger) + internal MethodBodyTypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, LogWriter logWriter) + : base(options, visitedAssemblies, logWriter) { } @@ -31,9 +30,9 @@ protected override void VisitVariable(VariableDefinition variable) if (this.TryRewriteType(variable.VariableType, out TypeReference newVariableType) && this.TryResolve(newVariableType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] variable '{variable.VariableType}'"); + this.LogWriter.LogDebug("............. [-] variable '{0}'", variable.VariableType); variable.VariableType = newVariableType; - Debug.WriteLine($"............. [+] variable '{variable.VariableType}'"); + this.LogWriter.LogDebug("............. [+] variable '{0}'", variable.VariableType); } } @@ -54,19 +53,19 @@ protected override Instruction VisitInstruction(Instruction instruction) this.TryRewriteType(fd.FieldType, out TypeReference newFieldType) && this.TryResolve(newFieldType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] {instruction}"); + this.LogWriter.LogDebug("............. [-] {0}", instruction); fd.FieldType = newFieldType; this.IsMethodBodyModified = true; - Debug.WriteLine($"............. [+] {instruction}"); + this.LogWriter.LogDebug("............. [+] {0}", instruction); } else if (instruction.Operand is FieldReference fr && this.TryRewriteType(fr.FieldType, out newFieldType) && this.TryResolve(newFieldType, out TypeDefinition _)) { - Debug.WriteLine($"............. [-] {instruction}"); + this.LogWriter.LogDebug("............. [-] {0}", instruction); fr.FieldType = newFieldType; this.IsMethodBodyModified = true; - Debug.WriteLine($"............. [+] {instruction}"); + this.LogWriter.LogDebug("............. [+] {0}", instruction); } } else if (instruction.OpCode == OpCodes.Initobj) @@ -99,9 +98,9 @@ private Instruction VisitInitobjInstruction(Instruction instruction) var newInstruction = Instruction.Create(instruction.OpCode, newType); newInstruction.Offset = instruction.Offset; - Debug.WriteLine($"............. [-] {instruction}"); + this.LogWriter.LogDebug("............. [-] {0}", instruction); this.Replace(instruction, newInstruction); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); instruction = newInstruction; } @@ -123,9 +122,9 @@ private Instruction VisitNewobjInstruction(Instruction instruction) Instruction newInstruction = Instruction.Create(OpCodes.Call, newMethod); newInstruction.Offset = instruction.Offset; - Debug.WriteLine($"............. [-] {instruction}"); + this.LogWriter.LogDebug("............. [-] {0}", instruction); this.Replace(instruction, newInstruction); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); instruction = newInstruction; } @@ -147,9 +146,9 @@ private Instruction VisitCallInstruction(Instruction instruction, MethodReferenc OpCodes.Callvirt : OpCodes.Call, newMethod); newInstruction.Offset = instruction.Offset; - Debug.WriteLine($"............. [-] {instruction}"); + this.LogWriter.LogDebug("............. [-] {0}", instruction); this.Replace(instruction, newInstruction); - Debug.WriteLine($"............. [+] {newInstruction}"); + this.LogWriter.LogDebug("............. [+] {0}", newInstruction); instruction = newInstruction; } diff --git a/Source/Test/Rewriting/Passes/Rewriting/Types/TypeRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/Types/TypeRewritingPass.cs index 51976b90f..d498288af 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/Types/TypeRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/Types/TypeRewritingPass.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting.Types; using Mono.Cecil; @@ -22,8 +22,8 @@ internal abstract class TypeRewritingPass : RewritingPass /// /// Initializes a new instance of the class. /// - internal TypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal TypeRewritingPass(RewritingOptions options, IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { this.KnownTypes = new Dictionary(); diff --git a/Source/Test/Rewriting/Passes/Rewriting/UncontrolledInvocationRewritingPass.cs b/Source/Test/Rewriting/Passes/Rewriting/UncontrolledInvocationRewritingPass.cs index 150ae3ca6..7b274b5e8 100644 --- a/Source/Test/Rewriting/Passes/Rewriting/UncontrolledInvocationRewritingPass.cs +++ b/Source/Test/Rewriting/Passes/Rewriting/UncontrolledInvocationRewritingPass.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Mono.Cecil; using Mono.Cecil.Cil; @@ -18,8 +18,8 @@ internal class UncontrolledInvocationRewritingPass : RewritingPass /// /// Initializes a new instance of the class. /// - internal UncontrolledInvocationRewritingPass(IEnumerable visitedAssemblies, ILogger logger) - : base(visitedAssemblies, logger) + internal UncontrolledInvocationRewritingPass(IEnumerable visitedAssemblies, LogWriter logWriter) + : base(visitedAssemblies, logWriter) { } @@ -56,7 +56,7 @@ protected override Instruction VisitInstruction(Instruction instruction) if (isUncontrolledType) { - Debug.WriteLine($"............. [+] injected uncontrolled '{invocationName}' invocation exception"); + this.LogWriter.LogDebug("............. [+] injected uncontrolled '{0}' invocation exception", invocationName); var providerType = this.Method.Module.ImportReference(typeof(ExceptionProvider)).Resolve(); MethodReference providerMethod = providerType.Methods.FirstOrDefault( diff --git a/Source/Test/Rewriting/RewritingEngine.cs b/Source/Test/Rewriting/RewritingEngine.cs index 524903321..7d6a20bd1 100644 --- a/Source/Test/Rewriting/RewritingEngine.cs +++ b/Source/Test/Rewriting/RewritingEngine.cs @@ -11,7 +11,7 @@ using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Mono.Cecil; @@ -55,9 +55,9 @@ public class RewritingEngine private readonly HashSet ResolveWarnings; /// - /// The installed logger. + /// Responsible for writing to the installed . /// - private readonly ILogger Logger; + private readonly LogWriter LogWriter; /// /// The installed profiler. @@ -67,23 +67,22 @@ public class RewritingEngine /// /// Initializes a new instance of the class. /// - private RewritingEngine(RewritingOptions options, Configuration configuration, ILogger logger, Profiler profiler) + private RewritingEngine(RewritingOptions options, Configuration configuration, LogWriter logWriter, Profiler profiler) { this.Options = options.Sanitize(); this.Configuration = configuration; this.Passes = new LinkedList(); this.ResolveWarnings = new HashSet(); - this.Logger = logger; + this.LogWriter = logWriter; this.Profiler = profiler; } /// /// Runs the engine using the specified rewriting options. /// - internal static void Run(RewritingOptions options, Configuration configuration, Profiler profiler) + internal static void Run(RewritingOptions options, Configuration configuration, LogWriter logWriter, Profiler profiler) { - var logger = new ConsoleLogger() { LogLevel = configuration.LogLevel }; - var engine = new RewritingEngine(options, configuration, logger, profiler); + var engine = new RewritingEngine(options, configuration, logWriter, profiler); engine.Run(); } @@ -131,28 +130,28 @@ private void InitializePasses(IEnumerable assemblies) { // Add the default type rewriting passes. We must first rewrite member types, // such as fields and method signatures, before we can rewrite the method bodies. - this.Passes.AddFirst(new MemberTypeRewritingPass(this.Options, assemblies, this.Logger)); - this.Passes.AddLast(new MethodBodyTypeRewritingPass(this.Options, assemblies, this.Logger)); + this.Passes.AddFirst(new MemberTypeRewritingPass(this.Options, assemblies, this.LogWriter)); + this.Passes.AddLast(new MethodBodyTypeRewritingPass(this.Options, assemblies, this.LogWriter)); if (this.Options.IsRewritingUnitTests) { // We are running this pass last, as we are rewriting the original method, and // we need the other rewriting passes to happen before this pass. - this.Passes.AddLast(new MSTestRewritingPass(this.Configuration, assemblies, this.Logger)); + this.Passes.AddLast(new MSTestRewritingPass(this.Configuration, assemblies, this.LogWriter)); } - this.Passes.AddLast(new InterAssemblyInvocationRewritingPass(assemblies, this.Logger)); - this.Passes.AddLast(new UncontrolledInvocationRewritingPass(assemblies, this.Logger)); + this.Passes.AddLast(new InterAssemblyInvocationRewritingPass(assemblies, this.LogWriter)); + this.Passes.AddLast(new UncontrolledInvocationRewritingPass(assemblies, this.LogWriter)); // Add a pass that rewrites exception handlers to make sure that any exceptions // used internally by the runtime are not consumed by the user code. - this.Passes.AddLast(new ExceptionFilterRewritingPass(assemblies, this.Logger)); + this.Passes.AddLast(new ExceptionFilterRewritingPass(assemblies, this.LogWriter)); if (this.Options.IsLoggingAssemblyContents || this.Options.IsDiffingAssemblyContents) { // Parsing the contents of an assembly must happen before and after any other pass. - this.Passes.AddFirst(new AssemblyDiffingPass(assemblies, this.Logger)); - this.Passes.AddLast(new AssemblyDiffingPass(assemblies, this.Logger)); + this.Passes.AddFirst(new AssemblyDiffingPass(assemblies, this.LogWriter)); + this.Passes.AddLast(new AssemblyDiffingPass(assemblies, this.LogWriter)); } } @@ -163,17 +162,17 @@ private void RewriteAssembly(AssemblyInfo assembly, string outputPath) { try { - this.Logger.WriteLine($"... Rewriting the '{assembly.Name}' assembly ({assembly.FullName})"); + this.LogWriter.LogImportant("... Rewriting the '{0}' assembly ({1})", assembly.Name, assembly.FullName); if (assembly.IsRewritten) { - this.Logger.WriteLine($"..... Skipping as assembly is already rewritten with matching signature"); + this.LogWriter.LogImportant("..... Skipping as assembly is already rewritten with matching signature"); return; } // Traverse the assembly to invoke each pass. foreach (var pass in this.Passes) { - Debug.WriteLine($"..... Invoking the '{pass.GetType().Name}' pass"); + this.LogWriter.LogDebug("..... Invoking the '{0}' pass", pass.GetType().Name); assembly.Invoke(pass); } @@ -182,7 +181,7 @@ private void RewriteAssembly(AssemblyInfo assembly, string outputPath) // Write the binary in the output path with portable symbols enabled. string resolvedOutputPath = this.Options.IsReplacingAssemblies() ? assembly.FilePath : outputPath; - this.Logger.WriteLine($"..... Writing the modified '{assembly.Name}' assembly to {resolvedOutputPath}"); + this.LogWriter.LogImportant("..... Writing the modified '{0}' assembly to {1}", assembly.Name, resolvedOutputPath); assembly.Write(outputPath); if (this.Options.IsLoggingAssemblyContents) @@ -228,8 +227,8 @@ internal void WriteILToJson(AssemblyInfo assembly, bool isRewritten, string outp if (!string.IsNullOrEmpty(json)) { string jsonFile = Path.ChangeExtension(outputPath, $".{(isRewritten ? "rw" : "il")}.json"); - this.Logger.WriteLine($"..... Writing the {(isRewritten ? "rewritten" : "original")} IL " + - $"of '{assembly.Name}' as JSON to {jsonFile}"); + this.LogWriter.LogImportant("..... Writing the {0} IL of '{1}' as JSON to {2}", + isRewritten ? "rewritten" : "original", assembly.Name, jsonFile); File.WriteAllText(jsonFile, json); } } @@ -249,7 +248,7 @@ internal void WriteILDiffToJson(AssemblyInfo assembly, string outputPath) if (!string.IsNullOrEmpty(diffJson)) { string jsonFile = Path.ChangeExtension(outputPath, ".diff.json"); - this.Logger.WriteLine($"..... Writing the IL diff of '{assembly.Name}' as JSON to {jsonFile}"); + this.LogWriter.LogImportant("..... Writing the IL diff of '{0}' as JSON to {1}", assembly.Name, jsonFile); File.WriteAllText(jsonFile, diffJson); } } @@ -280,12 +279,12 @@ private string CreateOutputDirectoryAndCopyFiles() Path.Combine(this.Options.OutputDirectory, TempDirectory) : this.Options.OutputDirectory).FullName; if (!this.Options.IsReplacingAssemblies()) { - this.Logger.WriteLine($"... Copying all files to the '{outputDirectory}' directory"); + this.LogWriter.LogImportant("... Copying all files to the '{0}' directory", outputDirectory); // Copy all files to the output directory, skipping any nested directory files. foreach (string filePath in Directory.GetFiles(sourceDirectory, "*")) { - Debug.WriteLine($"..... Copying the '{filePath}' file"); + this.LogWriter.LogDebug("..... Copying the '{0}' file", filePath); CopyFile(filePath, outputDirectory); } @@ -295,13 +294,13 @@ private string CreateOutputDirectoryAndCopyFiles() // Avoid copying the output directory itself. if (!directoryPath.StartsWith(outputDirectory)) { - Debug.WriteLine($"..... Copying the '{directoryPath}' directory"); + this.LogWriter.LogDebug("..... Copying the '{0}' directory", directoryPath); string path = Path.Combine(outputDirectory, directoryPath.Remove(0, sourceDirectory.Length) .TrimStart('\\', '/')); Directory.CreateDirectory(path); foreach (string filePath in Directory.GetFiles(directoryPath, "*")) { - Debug.WriteLine($"....... Copying the '{filePath}' file"); + this.LogWriter.LogDebug("....... Copying the '{0}' file", filePath); CopyFile(filePath, path); } } @@ -351,7 +350,7 @@ private async Task CopyWithRetriesAsync(string srcFile, string targetFile) } await Task.Delay(100); - this.Logger.WriteLine(LogSeverity.Warning, $"... Retrying write to {targetFile}"); + this.LogWriter.LogWarning("... Retrying write to {0}", targetFile); } } } @@ -363,7 +362,7 @@ private AssemblyDefinition OnResolveAssemblyFailure(object sender, AssemblyNameR { if (!this.ResolveWarnings.Contains(reference.FullName)) { - this.Logger.WriteLine(LogSeverity.Warning, "Unable to resolve assembly: '{0}'", reference.FullName); + this.LogWriter.LogWarning("Unable to resolve assembly: '{0}'", reference.FullName); this.ResolveWarnings.Add(reference.FullName); } diff --git a/Source/Test/Rewriting/Types/Net/Http/HttpRequestMessage.cs b/Source/Test/Rewriting/Types/Net/Http/HttpRequestMessage.cs index dbc8d371e..ad37b163e 100644 --- a/Source/Test/Rewriting/Types/Net/Http/HttpRequestMessage.cs +++ b/Source/Test/Rewriting/Types/Net/Http/HttpRequestMessage.cs @@ -53,7 +53,7 @@ internal static SystemHttpRequestMessage WithRuntimeHeaders(SystemHttpRequestMes { // Assigns a header containing the identifier of the currently executing runtime. string runtimeId = runtime.Id.ToString(); - IO.Debug.WriteLine("[coyote::debug] Assigned runtime '{0}' to the '{1} {2}' request from thread '{3}'.", + runtime.LogWriter.LogDebug("[coyote::debug] Assigned runtime '{0}' to the '{1} {2}' request from thread '{3}'.", runtimeId, request.Method, request.RequestUri, SystemThread.CurrentThread.ManagedThreadId); request.Headers.Add(HttpRequestHeader.RuntimeId, runtimeId); } @@ -63,7 +63,7 @@ internal static SystemHttpRequestMessage WithRuntimeHeaders(SystemHttpRequestMes // Assigns a header containing the identifier of the currently executing operation. if (runtime.TryGetExecutingOperation(out ControlledOperation op)) { - IO.Debug.WriteLine("[coyote::debug] Assigned operation '{0}' to the '{1} {2}' request from thread '{3}'.", + runtime.LogWriter.LogDebug("[coyote::debug] Assigned operation '{0}' to the '{1} {2}' request from thread '{3}'.", op.Name, request.Method, request.RequestUri, SystemThread.CurrentThread.ManagedThreadId); request.Headers.Add(HttpRequestHeader.SourceOperationId, op.Id.ToString()); } diff --git a/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncTaskMethodBuilder.cs b/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncTaskMethodBuilder.cs index b314170cd..9f216510d 100644 --- a/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncTaskMethodBuilder.cs +++ b/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncTaskMethodBuilder.cs @@ -62,7 +62,7 @@ public static AsyncTaskMethodBuilder Create() public void Start(ref TStateMachine stateMachine) where TStateMachine : SystemCompiler.IAsyncStateMachine { - IO.Debug.WriteLine("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", this.Runtime?.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.Start(ref stateMachine); } @@ -78,7 +78,7 @@ public void SetStateMachine(SystemCompiler.IAsyncStateMachine stateMachine) => /// public void SetResult() { - IO.Debug.WriteLine("[coyote::debug] Set state machine task '{0}' from thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Set state machine task '{0}' from thread '{1}'.", this.MethodBuilder.Task.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.SetResult(); } @@ -125,7 +125,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// private SystemTask AssignStateMachineTask(SystemTask builderTask) { - IO.Debug.WriteLine("[coyote::debug] Assigned state machine task '{0}' from thread '{1}'.", + this.Runtime.LogWriter.LogDebug("[coyote::debug] Assigned state machine task '{0}' from thread '{1}'.", builderTask.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.Runtime.RegisterKnownControlledTask(builderTask); return builderTask; @@ -183,7 +183,7 @@ public static AsyncTaskMethodBuilder Create() public void Start(ref TStateMachine stateMachine) where TStateMachine : SystemCompiler.IAsyncStateMachine { - IO.Debug.WriteLine("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", this.Runtime?.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.Start(ref stateMachine); } @@ -200,7 +200,7 @@ public void SetStateMachine(SystemCompiler.IAsyncStateMachine stateMachine) => /// The result to use to complete the task. public void SetResult(TResult result) { - IO.Debug.WriteLine("[coyote::debug] Set state machine task '{0}' from thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Set state machine task '{0}' from thread '{1}'.", this.MethodBuilder.Task.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.SetResult(result); } @@ -247,7 +247,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// private SystemTasks.Task AssignStateMachineTask(SystemTasks.Task builderTask) { - IO.Debug.WriteLine("[coyote::debug] Assigned state machine task '{0}' from thread '{1}'.", + this.Runtime.LogWriter.LogDebug("[coyote::debug] Assigned state machine task '{0}' from thread '{1}'.", builderTask.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.Runtime.RegisterKnownControlledTask(builderTask); return builderTask; diff --git a/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs index 80ccdfdc5..a6077b526 100644 --- a/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs +++ b/Source/Test/Rewriting/Types/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -64,7 +64,7 @@ public static AsyncValueTaskMethodBuilder Create() public void Start(ref TStateMachine stateMachine) where TStateMachine : SystemCompiler.IAsyncStateMachine { - IO.Debug.WriteLine("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", this.Runtime?.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.Start(ref stateMachine); } @@ -80,7 +80,7 @@ public void SetStateMachine(SystemCompiler.IAsyncStateMachine stateMachine) => /// public void SetResult() { - IO.Debug.WriteLine("[coyote::debug] Set state machine value task from thread '{0}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Set state machine value task from thread '{0}'.", SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.SetResult(); } @@ -135,7 +135,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// private void AssignStateMachineTask(SystemTask builderTask) { - IO.Debug.WriteLine("[coyote::debug] Assigned state machine value task '{0}' from thread '{1}'.", + this.Runtime.LogWriter.LogDebug("[coyote::debug] Assigned state machine value task '{0}' from thread '{1}'.", builderTask.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.Runtime.RegisterKnownControlledTask(builderTask); } @@ -193,7 +193,7 @@ public static AsyncValueTaskMethodBuilder Create() public void Start(ref TStateMachine stateMachine) where TStateMachine : SystemCompiler.IAsyncStateMachine { - IO.Debug.WriteLine("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Started state machine on runtime '{0}' and thread '{1}'.", this.Runtime?.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.Start(ref stateMachine); } @@ -210,7 +210,7 @@ public void SetStateMachine(SystemCompiler.IAsyncStateMachine stateMachine) => /// The result to use to complete the task. public void SetResult(TResult result) { - IO.Debug.WriteLine("[coyote::debug] Set state machine value task from thread '{0}'.", + this.Runtime?.LogWriter.LogDebug("[coyote::debug] Set state machine value task from thread '{0}'.", SystemThreading.Thread.CurrentThread.ManagedThreadId); this.MethodBuilder.SetResult(result); } @@ -265,7 +265,7 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// private void AssignStateMachineTask(SystemTasks.Task builderTask) { - IO.Debug.WriteLine("[coyote::debug] Assigned state machine value task '{0}' from thread '{1}'.", + this.Runtime.LogWriter.LogDebug("[coyote::debug] Assigned state machine value task '{0}' from thread '{1}'.", builderTask.Id, SystemThreading.Thread.CurrentThread.ManagedThreadId); this.Runtime.RegisterKnownControlledTask(builderTask); } diff --git a/Source/Test/Rewriting/Types/Threading/Monitor.cs b/Source/Test/Rewriting/Types/Threading/Monitor.cs index 90723593d..0d2aa781b 100644 --- a/Source/Test/Rewriting/Types/Threading/Monitor.cs +++ b/Source/Test/Rewriting/Types/Threading/Monitor.cs @@ -557,7 +557,7 @@ private void Pulse(PulseOperation pulseOperation) var waitingOp = this.WaitQueue[0]; this.WaitQueue.RemoveAt(0); this.ReadyQueue.Add(waitingOp); - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' is pulsed by task '{1}'.", + this.Resource.Runtime.LogWriter.LogDebug("[coyote::debug] Operation '{0}' is pulsed by task '{1}'.", waitingOp.Id, SystemTask.CurrentId); } } @@ -566,7 +566,7 @@ private void Pulse(PulseOperation pulseOperation) foreach (var waitingOp in this.WaitQueue) { this.ReadyQueue.Add(waitingOp); - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' is pulsed by task '{1}'.", + this.Resource.Runtime.LogWriter.LogDebug("[coyote::debug] Operation '{0}' is pulsed by task '{1}'.", waitingOp.Id, SystemTask.CurrentId); } @@ -593,7 +593,7 @@ internal bool Wait() } this.UnlockNextReady(); - IO.Debug.WriteLine("[coyote::debug] Operation '{0}' with task id '{1}' is waiting.", + this.Resource.Runtime.LogWriter.LogDebug("[coyote::debug] Operation '{0}' with task id '{1}' is waiting.", op.Id, SystemTask.CurrentId); // Block this operation and schedule the next enabled operation. diff --git a/Source/Test/Rewriting/Types/Web/RequestControllerMiddleware.cs b/Source/Test/Rewriting/Types/Web/RequestControllerMiddleware.cs index 30adf61dc..eea757945 100644 --- a/Source/Test/Rewriting/Types/Web/RequestControllerMiddleware.cs +++ b/Source/Test/Rewriting/Types/Web/RequestControllerMiddleware.cs @@ -42,7 +42,7 @@ public async SystemTask InvokeAsync(WebFramework.HttpContext context) WebFramework.HttpRequest request = context.Request; if (request != null && TryExtractRuntime(request, out CoyoteRuntime runtime)) { - IO.Debug.WriteLine("[coyote::debug] Runtime '{0}' takes control of the '{1} {2}' handler on thread '{3}'.", + runtime.LogWriter.LogDebug("[coyote::debug] Runtime '{0}' takes control of the '{1} {2}' handler on thread '{3}'.", runtime.Id, request.Method, request.Path, SystemThread.CurrentThread.ManagedThreadId); TryExtractSourceOperation(request, runtime, out ControlledOperation source); var op = HttpOperation.Create(ToHttpMethod(request.Method), request.Path, runtime, source); @@ -60,8 +60,6 @@ await runtime.TaskFactory.StartNew(state => } else { - IO.Debug.WriteLine($"[coyote::debug] Unable to control the '{0} {1}' request on thread '{2}'.", - request?.Method, request?.Path, SystemThread.CurrentThread.ManagedThreadId); await this.Next(context); } } diff --git a/Source/Test/SystematicTesting/Reports/UncontrolledInvocationsReport.cs b/Source/Test/SystematicTesting/Reports/UncontrolledInvocationsReport.cs index 15459d710..2d56b661f 100644 --- a/Source/Test/SystematicTesting/Reports/UncontrolledInvocationsReport.cs +++ b/Source/Test/SystematicTesting/Reports/UncontrolledInvocationsReport.cs @@ -7,7 +7,6 @@ using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Text; -using Microsoft.Coyote.IO; namespace Microsoft.Coyote.SystematicTesting { @@ -34,8 +33,7 @@ internal static string ToJSON(HashSet uncontrolledInvocations) } catch (Exception ex) { - Debug.WriteLine(ex.Message); - throw new InvalidOperationException($"Unexpected JSON format.\n{ex.Message}"); + throw new InvalidOperationException($"Unexpected JSON format: {ex.Message}"); } } diff --git a/Source/Test/SystematicTesting/TestMethodInfo.cs b/Source/Test/SystematicTesting/TestMethodInfo.cs index 67729d08b..9f028fed3 100644 --- a/Source/Test/SystematicTesting/TestMethodInfo.cs +++ b/Source/Test/SystematicTesting/TestMethodInfo.cs @@ -13,7 +13,7 @@ #endif using System.Threading.Tasks; using Microsoft.Coyote.Actors; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; #if NET || NETCOREAPP3_1 using Microsoft.Extensions.DependencyModel; @@ -74,20 +74,27 @@ internal sealed class TestMethodInfo : IDisposable private readonly ICompilationAssemblyResolver AssemblyResolver; #endif + /// + /// Responsible for writing to the installed . + /// + private readonly LogWriter LogWriter; + /// /// Initializes a new instance of the class. /// - private TestMethodInfo(Delegate method) + private TestMethodInfo(Delegate method, LogWriter logWriter) { this.Assembly = method.GetMethodInfo().Module.Assembly; this.Method = method; + this.LogWriter = logWriter; } /// /// Initializes a new instance of the class. /// - private TestMethodInfo(Configuration configuration) + private TestMethodInfo(Configuration configuration, LogWriter logWriter) { + this.LogWriter = logWriter; #if NET || NETCOREAPP3_1 this.Assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(configuration.AssemblyToBeAnalyzed); this.LoadContext = AssemblyLoadContext.GetLoadContext(this.Assembly); @@ -104,21 +111,21 @@ private TestMethodInfo(Configuration configuration) this.Assembly = Assembly.LoadFrom(configuration.AssemblyToBeAnalyzed); #endif - (this.Method, this.Name) = GetTestMethod(this.Assembly, configuration.TestMethodName); - this.InitMethod = GetTestSetupMethod(this.Assembly, typeof(TestInitAttribute)); - this.DisposeMethod = GetTestSetupMethod(this.Assembly, typeof(TestDisposeAttribute)); - this.IterationDisposeMethod = GetTestSetupMethod(this.Assembly, typeof(TestIterationDisposeAttribute)); + (this.Method, this.Name) = GetTestMethod(this.Assembly, configuration.TestMethodName, logWriter); + this.InitMethod = GetTestSetupMethod(this.Assembly, typeof(TestInitAttribute), logWriter); + this.DisposeMethod = GetTestSetupMethod(this.Assembly, typeof(TestDisposeAttribute), logWriter); + this.IterationDisposeMethod = GetTestSetupMethod(this.Assembly, typeof(TestIterationDisposeAttribute), logWriter); } /// /// Creates a instance from the specified delegate. /// - internal static TestMethodInfo Create(Delegate method) => new TestMethodInfo(method); + internal static TestMethodInfo Create(Delegate method, LogWriter logWriter) => new TestMethodInfo(method, logWriter); /// /// Creates a instance from assembly specified in the configuration. /// - internal static TestMethodInfo Create(Configuration configuration) => new TestMethodInfo(configuration); + internal static TestMethodInfo Create(Configuration configuration, LogWriter logWriter) => new TestMethodInfo(configuration, logWriter); /// /// Invokes the user-specified initialization method for all iterations executing this test. @@ -139,10 +146,10 @@ private TestMethodInfo(Configuration configuration) /// Returns the test method with the specified name. A test method must /// be annotated with the attribute. /// - private static (Delegate testMethod, string testName) GetTestMethod(Assembly assembly, string methodName) + private static (Delegate testMethod, string testName) GetTestMethod(Assembly assembly, string methodName, LogWriter logWriter) { BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.InvokeMethod; - List testMethods = FindTestMethodsWithAttribute(typeof(TestAttribute), flags, assembly); + List testMethods = FindTestMethodsWithAttribute(typeof(TestAttribute), flags, assembly, logWriter); if (testMethods.Count > 0) { @@ -266,10 +273,10 @@ private static (Delegate testMethod, string testName) GetTestMethod(Assembly ass /// Returns the test method with the specified attribute. /// Returns null if no such method is found. /// - private static MethodInfo GetTestSetupMethod(Assembly assembly, Type attribute) + private static MethodInfo GetTestSetupMethod(Assembly assembly, Type attribute, LogWriter logWriter) { BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.InvokeMethod; - List testMethods = FindTestMethodsWithAttribute(attribute, flags, assembly); + List testMethods = FindTestMethodsWithAttribute(attribute, flags, assembly, logWriter); if (testMethods.Count is 0) { @@ -326,7 +333,7 @@ private static MethodInfo GetTestSetupMethod(Assembly assembly, Type attribute) /// Finds the test methods with the specified attribute in the given assembly. /// Returns an empty list if no such methods are found. /// - private static List FindTestMethodsWithAttribute(Type attribute, BindingFlags bindingFlags, Assembly assembly) + private static List FindTestMethodsWithAttribute(Type attribute, BindingFlags bindingFlags, Assembly assembly, LogWriter logWriter) { List testMethods = null; @@ -339,14 +346,14 @@ private static List FindTestMethodsWithAttribute(Type attribute, Bin { foreach (var le in ex.LoaderExceptions) { - Debug.WriteLine(le.Message); + logWriter.LogDebug(le.Message); } throw; } catch (Exception ex) { - Debug.WriteLine(ex.Message); + logWriter.LogDebug(ex.Message); throw; } @@ -359,7 +366,7 @@ private static List FindTestMethodsWithAttribute(Type attribute, Bin /// private Assembly OnResolving(AssemblyLoadContext context, AssemblyName assemblyName) { - Debug.WriteLine($"[coyote::debug] Resolving assembly '{assemblyName.Name}'."); + this.LogWriter.LogDebug("[coyote::debug] Resolving assembly '{0}'.", assemblyName.Name); RuntimeLibrary runtimeLibrary = this.DependencyContext.RuntimeLibraries.FirstOrDefault( runtime => string.Equals(runtime.Name, assemblyName.Name, StringComparison.OrdinalIgnoreCase)); if (runtimeLibrary != null) diff --git a/Source/Test/SystematicTesting/TestingEngine.cs b/Source/Test/SystematicTesting/TestingEngine.cs index 4a0aa61be..f29f3eef8 100644 --- a/Source/Test/SystematicTesting/TestingEngine.cs +++ b/Source/Test/SystematicTesting/TestingEngine.cs @@ -14,7 +14,7 @@ using System.Xml; using Microsoft.Coyote.Actors; using Microsoft.Coyote.Actors.Coverage; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting; using Microsoft.Coyote.Runtime; using Microsoft.Coyote.Telemetry; @@ -30,23 +30,13 @@ namespace Microsoft.Coyote.SystematicTesting #endif public sealed class TestingEngine : IDisposable { - /// - /// Url with information about the rewriting process. - /// - private const string LearnAboutRewritingUrl = "https://aka.ms/coyote-rewrite"; - - /// - /// Url with information about the gathered telemetry. - /// - private const string LearnAboutTelemetryUrl = "https://aka.ms/coyote-telemetry"; - /// /// The client used to optionally send anonymized telemetry data. /// private static TelemetryClient TelemetryClient; /// - /// The project configuration. + /// The test configuration. /// private readonly Configuration Configuration; @@ -87,46 +77,9 @@ public sealed class TestingEngine : IDisposable public TestReport TestReport { get; set; } /// - /// The installed logger. + /// Responsible for writing to the installed . /// - /// - /// See Logging for more information. - /// - private ILogger InstalledLogger; - - /// - /// The default logger that is used during testing. - /// - private readonly ILogger DefaultLogger; - - /// - /// Get or set the used to log messages during testing. - /// - /// - /// See Logging for more information. - /// - public ILogger Logger - { - get - { - return this.InstalledLogger; - } - - set - { - var old = this.InstalledLogger; - if (value is null) - { - this.InstalledLogger = new NullLogger(); - } - else - { - this.InstalledLogger = value; - } - - using var v = old; - } - } + private readonly LogWriter LogWriter; /// /// The DGML graph of the execution path explored in the last iteration. @@ -155,64 +108,59 @@ public ILogger Logger private int PrintGuard; /// - /// Creates a new systematic testing engine. + /// Serializes access to the engine. /// - public static TestingEngine Create(Configuration configuration) - { - try - { - ExecutionTrace prefixTrace = TraceReport.FromJson(configuration); - TestMethodInfo testMethodInfo = TestMethodInfo.Create(configuration); - return new TestingEngine(configuration, testMethodInfo, prefixTrace); - } - catch (Exception ex) - { - Error.Report(ex.Message); - throw; - } - } + private readonly object EngineLock; /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Action test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Action test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Action test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Func test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Func test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Creates a new systematic testing engine. /// public static TestingEngine Create(Configuration configuration, Func test) => - new TestingEngine(configuration, test); + new TestingEngine(configuration, test, new LogWriter(configuration, true)); /// /// Initializes a new instance of the class. /// - internal TestingEngine(Configuration configuration, Delegate test) - : this(configuration, TestMethodInfo.Create(test), TraceReport.FromJson(configuration)) + internal TestingEngine(Configuration configuration, LogWriter logWriter) + : this(configuration, TestMethodInfo.Create(configuration, logWriter), TraceReport.FromJson(configuration), logWriter) + { + } + + /// + /// Initializes a new instance of the class. + /// + internal TestingEngine(Configuration configuration, Delegate test, LogWriter logWriter) + : this(configuration, TestMethodInfo.Create(test, logWriter), TraceReport.FromJson(configuration), logWriter) { } @@ -223,48 +171,34 @@ internal TestingEngine(Configuration configuration, Delegate test) /// If a non-empty prefix trace is provided, then the testing engine will attempt /// to replay it before performing any new exploration. /// - private TestingEngine(Configuration configuration, TestMethodInfo testMethodInfo, ExecutionTrace prefixTrace) + private TestingEngine(Configuration configuration, TestMethodInfo testMethodInfo, ExecutionTrace prefixTrace, LogWriter logWriter) { this.Configuration = configuration; this.TestMethodInfo = testMethodInfo; - - this.DefaultLogger = new ConsoleLogger() { LogLevel = configuration.LogLevel }; - this.Logger = this.DefaultLogger; + this.LogWriter = logWriter; this.Profiler = new Profiler(); - this.StartIterationCallbacks = new HashSet>(); this.EndIterationCallbacks = new HashSet>(); - this.TestReport = new TestReport(configuration); this.ReadableTrace = string.Empty; this.ReproducibleTrace = string.Empty; - this.CancellationTokenSource = new CancellationTokenSource(); this.PrintGuard = 1; + this.EngineLock = new object(); - if (configuration.IsDebugVerbosityEnabled) - { - IO.Debug.IsEnabled = true; - } + AppDomain.CurrentDomain.UnhandledException += this.OnUnhandledException; // Do some sanity checking. - string error = string.Empty; if (configuration.IsSystematicFuzzingEnabled && prefixTrace.Length > 0) { - error = "Replaying an execution trace is not supported in systematic fuzzing."; - } - - if (!string.IsNullOrEmpty(error)) - { - Error.Report(error); - throw new InvalidOperationException(error); + throw new InvalidOperationException("Replaying an execution trace is not supported in systematic fuzzing."); } // Parse the trace if one is provided, and update any configuration values. this.Scheduler = OperationScheduler.Setup(configuration, prefixTrace); // Create a client for gathering and sending optional telemetry data. - TelemetryClient = TelemetryClient.GetOrCreate(this.Configuration); + TelemetryClient = TelemetryClient.GetOrCreate(this.Configuration, this.LogWriter); } /// @@ -276,14 +210,13 @@ public void Run() { if (this.Configuration.IsTelemetryEnabled) { - this.Logger.WriteLine(LogSeverity.Important, $"..... Anonymized telemetry is enabled, see {LearnAboutTelemetryUrl}."); + this.LogWriter.LogImportant("..... Anonymized telemetry is enabled, see {0}.", Documentation.LearnAboutTelemetryUrl); } if (!this.IsTestRewritten()) { // TODO: eventually will throw an exception; we allow this for now. - this.Logger.WriteLine(LogSeverity.Error, - $"... Assembly is not rewritten for testing, see {LearnAboutRewritingUrl}."); + this.LogWriter.LogError("... Assembly is not rewritten for testing, see {0}.", Documentation.LearnAboutRewritingUrl); } Task task = this.CreateTestingTask(this.TestMethodInfo); @@ -304,30 +237,30 @@ public void Run() { if (this.CancellationTokenSource.IsCancellationRequested) { - this.Logger.WriteLine(LogSeverity.Warning, $"... Test timed out."); + this.LogWriter.LogWarning("... Test timed out."); } } catch (AggregateException aex) { aex.Handle((ex) => { - IO.Debug.WriteLine(ex.Message); - IO.Debug.WriteLine(ex.StackTrace); + this.LogWriter.LogDebug(ex.Message); + this.LogWriter.LogDebug(ex.StackTrace); return true; }); if (aex.InnerException is FileNotFoundException) { - Error.Report($"{aex.InnerException.Message}"); + this.LogWriter.LogError(aex.InnerException.Message); throw; } - Error.Report("Unhandled or internal exception was thrown. Please enable debug verbosity to print more information."); + this.LogWriter.LogError("Unhandled or internal exception was thrown. Please enable debug verbosity to print more information."); throw; } catch (Exception ex) { - this.Logger.WriteLine(LogSeverity.Error, $"... Test failed due to an internal error: {ex}"); + this.LogWriter.LogError("... Test failed due to an internal error: {0}", ex); this.TestReport.InternalErrors.Add(ex.ToString()); } finally @@ -348,14 +281,11 @@ private Task CreateTestingTask(TestMethodInfo methodInfo) { return new Task(() => { - this.Logger.WriteLine(LogSeverity.Important, "... Setting up the{0} test:", - string.IsNullOrEmpty(methodInfo.Name) ? string.Empty : $" '{methodInfo.Name}'"); - this.Logger.WriteLine(LogSeverity.Important, - $"..... Using the {this.Scheduler.GetDescription()} exploration strategy."); + this.LogWriter.LogImportant("... Setting up the{0} test:", string.IsNullOrEmpty(methodInfo.Name) ? string.Empty : $" '{methodInfo.Name}'"); + this.LogWriter.LogImportant("..... Using the {0} exploration strategy.", this.Scheduler.GetDescription()); if (this.Configuration.AttachDebugger) { - this.Logger.WriteLine(LogSeverity.Important, - $"..... Launching and attaching the debugger."); + this.LogWriter.LogImportant("..... Launching and attaching the debugger."); Debugger.Launch(); } @@ -364,9 +294,7 @@ private Task CreateTestingTask(TestMethodInfo methodInfo) // Invokes the user-specified initialization method. methodInfo.InitializeAllIterations(); - this.Logger.WriteLine(LogSeverity.Important, this.Scheduler.IsReplaying ? - "... Running test." : "... Running test iterations:"); - + this.LogWriter.LogImportant(this.Scheduler.IsReplaying ? "... Running test." : "... Running test iterations:"); uint iteration = 0; while (iteration < this.Configuration.TestingIterations || this.Configuration.TestingTimeout > 0) { @@ -418,7 +346,15 @@ private Task CreateTestingTask(TestMethodInfo methodInfo) /// private bool RunNextIteration(TestMethodInfo methodInfo, uint iteration) { - if (!this.Scheduler.InitializeNextIteration(iteration)) + // Log writer used to observe all test iteration output and write it in memory. + using MemoryLogWriter iterationLogWriter = new MemoryLogWriter(this.Configuration); + if (!this.LogWriter.IsRuntimeLogger()) + { + // Override the default logger associated with this test iteration. + iterationLogWriter.SetLogger(this.LogWriter.Logger); + } + + if (!this.Scheduler.InitializeNextIteration(iteration, iterationLogWriter)) { // The next iteration cannot run, so stop exploring. return false; @@ -426,53 +362,20 @@ private bool RunNextIteration(TestMethodInfo methodInfo, uint iteration) if (!this.Scheduler.IsReplaying && this.ShouldPrintIteration(iteration + 1)) { - this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1}"); - - // Flush when logging to console. - if (this.Logger is ConsoleLogger) - { - Console.Out.Flush(); - } + this.LogWriter.LogImportant("..... Iteration #{0}", iteration + 1); } // Runtime used to serialize and test the program in this iteration. CoyoteRuntime runtime = null; - // Logger used to intercept the program output if no custom logger - // is installed and if verbosity is turned off. - InMemoryLogger runtimeLogger = null; - - // Gets a handle to the standard output and error streams. - var stdOut = Console.Out; - var stdErr = Console.Error; - try { // Invoke any registered callbacks at the start of this iteration. this.InvokeStartIterationCallBacks(iteration); // Creates a new instance of the controlled runtime. - runtime = new CoyoteRuntime(this.Configuration, this.Scheduler); - - // If verbosity is turned off, then intercept the program log, and also redirect - // the standard output and error streams to the runtime logger. - if (!this.Configuration.IsVerbose) - { - runtimeLogger = new InMemoryLogger(); - if (this.Logger != this.DefaultLogger) - { - runtimeLogger.UserLogger = this.Logger; - } - - runtime.Logger = runtimeLogger; - - Console.SetOut(runtimeLogger.TextWriter); - Console.SetError(runtimeLogger.TextWriter); - } - else if (this.Logger != this.DefaultLogger) - { - runtime.Logger = this.Logger; - } + runtime = CoyoteRuntime.Create(this.Configuration, this.Scheduler, iterationLogWriter, + RuntimeFactory.CreateLogManager(iterationLogWriter)); this.InitializeCustomActorLogging(runtime.DefaultActorExecutionContext); @@ -480,24 +383,24 @@ private bool RunNextIteration(TestMethodInfo methodInfo, uint iteration) Task task = runtime.RunTestAsync(methodInfo.Method, methodInfo.Name); task.Wait(); + // Turn off runtime logging for the current iteration. + iterationLogWriter.Close(); + // Invokes the user-specified iteration disposal method. methodInfo.DisposeCurrentIteration(); // Invoke any registered callbacks at the end of this iteration. this.InvokeEndIterationCallBacks(iteration); - runtime.LogWriter.LogCompletion(); + runtime.LogManager.LogCompletion(); this.GatherTestingStatistics(runtime); if (!this.Scheduler.IsReplaying && this.TestReport.NumOfFoundBugs > 0) { - if (runtimeLogger != null) - { - this.ReadableTrace = string.Empty; - this.ReadableTrace += runtimeLogger.ToString(); - this.ReadableTrace += this.TestReport.GetText(this.Configuration, "[coyote::report]"); - } + this.ReadableTrace = string.Empty; + this.ReadableTrace += iterationLogWriter.GetObservedMessages(); + this.ReadableTrace += this.TestReport.GetText(this.Configuration, "[coyote::report]"); if (runtime.SchedulingPolicy is SchedulingPolicy.Interleaving) { @@ -507,47 +410,36 @@ private bool RunNextIteration(TestMethodInfo methodInfo, uint iteration) } finally { - if (!this.Configuration.IsVerbose) - { - // Restores the standard output and error streams. - Console.SetOut(stdOut); - Console.SetError(stdErr); - } - if (this.Configuration.IsSystematicFuzzingFallbackEnabled && runtime.SchedulingPolicy is SchedulingPolicy.Interleaving && (runtime.ExecutionStatus is ExecutionStatus.ConcurrencyUncontrolled || runtime.ExecutionStatus is ExecutionStatus.Deadlocked)) { // Detected uncontrolled concurrency or deadlock, so switch to systematic fuzzing. - this.Scheduler = OperationScheduler.Setup(this.Configuration, SchedulingPolicy.Fuzzing, - this.Scheduler.ValueGenerator); - this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " + - $"enables systematic fuzzing due to uncontrolled concurrency"); + this.Scheduler = OperationScheduler.Setup(this.Configuration, SchedulingPolicy.Fuzzing, this.Scheduler.ValueGenerator); + this.LogWriter.LogImportant("..... Iteration #{0} enables systematic fuzzing due to uncontrolled concurrency", + iteration + 1); } else if (runtime.ExecutionStatus is ExecutionStatus.BoundReached) { - this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " + - $"hit bound of '{this.Scheduler.StepCount}' scheduling steps"); + this.LogWriter.LogImportant("..... Iteration #{0} hit bound of '{1}' scheduling steps", + iteration + 1, this.Scheduler.StepCount); } else if (runtime.ExecutionStatus is ExecutionStatus.BugFound) { if (!this.Scheduler.IsReplaying) { - this.Logger.WriteLine(LogSeverity.Important, $"..... Iteration #{iteration + 1} " + - $"found bug #{this.TestReport.NumOfFoundBugs}"); + this.LogWriter.LogImportant("..... Iteration #{0} found bug #{1}", iteration + 1, this.TestReport.NumOfFoundBugs); } - this.Logger.WriteLine(LogSeverity.Error, runtime.BugReport); + this.LogWriter.LogError(runtime.BugReport); } else if (this.Scheduler.IsReplaying) { - this.Logger.WriteLine(LogSeverity.Error, "Failed to reproduce the bug."); + this.LogWriter.LogError("Failed to reproduce the bug."); } - // Cleans up the runtime before the next iteration starts. - runtimeLogger?.Close(); - runtimeLogger?.Dispose(); + // Clean up runtime resources before the next iteration starts. runtime?.Dispose(); } @@ -806,36 +698,38 @@ private bool ShouldPrintIteration(uint iteration) public bool IsTestRewritten() => RewritingEngine.IsAssemblyRewritten(this.TestMethodInfo.Assembly); /// - /// Installs the specified TextWriter for logging. + /// Installs the specified to log messages during testing. /// - /// - /// This writer will be wrapped in an object that implements the interface which - /// will have a minor performance overhead, so it is better to set the property instead. - /// - /// The writer to use for logging. - /// The previously installed logger. - [Obsolete("Use the new ILogger version of SetLogger")] - public TextWriter SetLogger(TextWriter writer) + public void SetLogger(ILogger logger) => this.LogWriter.SetLogger(logger); + + /// + /// Callback invoked when an unhandled exception occurs during testing. + /// + private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args) { - ILogger oldLogger = this.Logger; - if (oldLogger == this.DefaultLogger) + lock (this.EngineLock) { - oldLogger = null; - } - - this.Logger = new TextWriterLogger(writer); + Exception exception = args.ExceptionObject as Exception; + if (exception is AggregateException aggregateException) + { + exception = aggregateException.Flatten().InnerException; + } - if (oldLogger != null) - { - return oldLogger.TextWriter; + this.LogWriter.LogError("[coyote::error] Unhandled exception: {0}", exception); } - - return null; } /// /// Releases any held resources. /// - public void Dispose() => this.TestMethodInfo.Dispose(); + public void Dispose() + { + lock (this.EngineLock) + { + AppDomain.CurrentDomain.UnhandledException -= this.OnUnhandledException; + this.TestMethodInfo.Dispose(); + this.LogWriter.Dispose(); + } + } } } diff --git a/Source/Test/Telemetry/TelemetryClient.cs b/Source/Test/Telemetry/TelemetryClient.cs index 66f8ddc95..562d1022e 100644 --- a/Source/Test/Telemetry/TelemetryClient.cs +++ b/Source/Test/Telemetry/TelemetryClient.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.Coyote.Logging; using AppInsightsClient = Microsoft.ApplicationInsights.TelemetryClient; namespace Microsoft.Coyote.Telemetry @@ -46,6 +47,11 @@ internal class TelemetryClient /// private readonly AppInsightsClient Client; + /// + /// Responsible for writing to the installed . + /// + private readonly LogWriter LogWriter; + /// /// True if telemetry is enabled, else false. /// @@ -54,13 +60,14 @@ internal class TelemetryClient /// /// Initializes a new instance of the class. /// - private TelemetryClient(bool isEnabled) + private TelemetryClient(LogWriter logWriter, bool isEnabled) { if (isEnabled) { TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault(); configuration.InstrumentationKey = "17a6badb-bf2d-4f5d-959b-6843b8bb1f7f"; this.Client = new AppInsightsClient(configuration); + this.LogWriter = logWriter; string version = typeof(Runtime.CoyoteRuntime).Assembly.GetName().Version.ToString(); this.Client.Context.GlobalProperties["coyote"] = version; @@ -86,11 +93,11 @@ private TelemetryClient(bool isEnabled) /// Returns the existing telemetry client if one has already been created for this process, /// or creates and returns a new one with the specified configuration. /// - internal static TelemetryClient GetOrCreate(Configuration configuration) + internal static TelemetryClient GetOrCreate(Configuration configuration, LogWriter logWriter) { lock (SyncObject) { - Current ??= new TelemetryClient(configuration.IsTelemetryEnabled); + Current ??= new TelemetryClient(logWriter, configuration.IsTelemetryEnabled); return Current; } } @@ -106,12 +113,12 @@ internal void TrackEvent(string name) { try { - IO.Debug.WriteLine("[coyote::telemetry] Tracking event: {0}.", name); + this.LogWriter.LogDebug("[coyote::telemetry] Tracking event: {0}.", name); this.Client.TrackEvent(new EventTelemetry(name)); } catch (Exception ex) { - IO.Debug.WriteLine("[coyote::telemetry] Error sending event: {0}", ex.Message); + this.LogWriter.LogDebug("[coyote::telemetry] Unable to send event: {0}", ex.Message); } } } @@ -128,12 +135,12 @@ internal void TrackMetric(string name, double value) { try { - IO.Debug.WriteLine("[coyote::telemetry] Tracking metric: {0}={1}.", name, value); + this.LogWriter.LogDebug("[coyote::telemetry] Tracking metric: {0}={1}.", name, value); this.Client.TrackMetric(new MetricTelemetry(name, value)); } catch (Exception ex) { - IO.Debug.WriteLine("[coyote::telemetry] Error sending metric: {0}", ex.Message); + this.LogWriter.LogDebug("[coyote::telemetry] Unable to send metric: {0}", ex.Message); } } } @@ -154,7 +161,7 @@ internal void Flush() } catch (Exception ex) { - IO.Debug.WriteLine("[coyote::telemetry] Error flushing: {0}", ex.Message); + this.LogWriter.LogDebug("[coyote::telemetry] Error flushing: {0}", ex.Message); } } } diff --git a/Tests/Tests.Actors.BugFinding/Logging/CustomActorRuntimeLogTests.cs b/Tests/Tests.Actors.BugFinding/Logging/ActorLoggingTests.cs similarity index 59% rename from Tests/Tests.Actors.BugFinding/Logging/CustomActorRuntimeLogTests.cs rename to Tests/Tests.Actors.BugFinding/Logging/ActorLoggingTests.cs index de8dc85c8..0e860d9cb 100644 --- a/Tests/Tests.Actors.BugFinding/Logging/CustomActorRuntimeLogTests.cs +++ b/Tests/Tests.Actors.BugFinding/Logging/ActorLoggingTests.cs @@ -4,11 +4,11 @@ using Microsoft.Coyote.Runtime; using Xunit.Abstractions; -namespace Microsoft.Coyote.Actors.BugFinding.Tests +namespace Microsoft.Coyote.Actors.BugFinding.Tests.Logging { - public class CustomActorRuntimeLogTests : Actors.Tests.CustomActorRuntimeLogTests + public class ActorLoggingTests : Actors.Tests.Logging.ActorLoggingTests { - public CustomActorRuntimeLogTests(ITestOutputHelper output) + public ActorLoggingTests(ITestOutputHelper output) : base(output) { } diff --git a/Tests/Tests.Actors.BugFinding/Runtime/EntryPointTests.cs b/Tests/Tests.Actors.BugFinding/Runtime/EntryPointTests.cs index 4b74f0a02..36c8869e8 100644 --- a/Tests/Tests.Actors.BugFinding/Runtime/EntryPointTests.cs +++ b/Tests/Tests.Actors.BugFinding/Runtime/EntryPointTests.cs @@ -5,8 +5,10 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Microsoft.Coyote.SystematicTesting; +using Microsoft.Coyote.Tests.Common; using Xunit; using Xunit.Abstractions; @@ -162,7 +164,9 @@ private void CheckTestMethod(string name) Configuration config = this.GetConfiguration(); config.AssemblyToBeAnalyzed = Assembly.GetExecutingAssembly().Location; config.TestMethodName = name; - using var testMethodInfo = TestMethodInfo.Create(config); + var logWriter = new LogWriter(config); + logWriter.SetLogger(new TestOutputLogger(this.TestOutput)); + using var testMethodInfo = TestMethodInfo.Create(config, logWriter); Assert.Equal(Assembly.GetExecutingAssembly(), testMethodInfo.Assembly); Assert.Equal($"{typeof(EntryPointTests).FullName}.{name}", testMethodInfo.Name); } diff --git a/Tests/Tests.Actors/BaseActorTest.cs b/Tests/Tests.Actors/BaseActorTest.cs index 4721b14ec..37d04e139 100644 --- a/Tests/Tests.Actors/BaseActorTest.cs +++ b/Tests/Tests.Actors/BaseActorTest.cs @@ -39,7 +39,7 @@ protected async Task WaitAsync(Task task, int millisecondsDelay = 5000) { if (this.SchedulingPolicy is SchedulingPolicy.None) { - await Task.WhenAny(task, Task.Delay(GetErrorWaitingTimeout(millisecondsDelay))); + await await Task.WhenAny(task, Task.Delay(GetErrorWaitingTimeout(millisecondsDelay))); } else { @@ -66,7 +66,7 @@ protected async Task GetResultAsync(Task task, int mi { if (this.SchedulingPolicy is SchedulingPolicy.None) { - await Task.WhenAny(task, Task.Delay(GetErrorWaitingTimeout(millisecondsDelay))); + await await Task.WhenAny(task, Task.Delay(GetErrorWaitingTimeout(millisecondsDelay))); } else { diff --git a/Tests/Tests.Actors/Common/CustomLogger.cs b/Tests/Tests.Actors/Common/CustomLogger.cs deleted file mode 100644 index 8ab8de2f2..000000000 --- a/Tests/Tests.Actors/Common/CustomLogger.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.IO; -using System.Text; -using Microsoft.Coyote.IO; - -namespace Microsoft.Coyote.Actors.Tests -{ - public class CustomLogger : TextWriter, ILogger - { - private StringBuilder StringBuilder; - - public CustomLogger() - { - this.StringBuilder = new StringBuilder(); - } - - /// - public TextWriter TextWriter => this; - - /// - public override Encoding Encoding => Encoding.Unicode; - - /// - public override string ToString() - { - if (this.StringBuilder != null) - { - return this.StringBuilder.ToString(); - } - - return string.Empty; - } - - /// - public override void Write(string value) - { - this.Write(LogSeverity.Informational, value); - } - - /// - public override void Write(string format, params object[] args) - { - this.Write(LogSeverity.Informational, string.Format(format, args)); - } - - /// - public void Write(LogSeverity severity, string value) - { - if (this.StringBuilder != null) - { - this.StringBuilder.Append(value); - } - } - - /// - public void Write(LogSeverity severity, string format, params object[] args) - { - if (this.StringBuilder != null) - { - this.StringBuilder.Append(string.Format(format, args)); - } - } - - /// - public override void WriteLine(string value) - { - this.WriteLine(LogSeverity.Informational, value); - } - - /// - public override void WriteLine(string format, params object[] args) - { - this.WriteLine(LogSeverity.Informational, string.Format(format, args)); - } - - /// - public void WriteLine(LogSeverity severity, string value) - { - if (this.StringBuilder != null) - { - this.StringBuilder.AppendLine(value); - } - } - - /// - public void WriteLine(LogSeverity severity, string format, params object[] args) - { - if (this.StringBuilder != null) - { - this.StringBuilder.AppendLine(string.Format(format, args)); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - this.StringBuilder.Clear(); - this.StringBuilder = null; - } - } - } -} diff --git a/Tests/Tests.Actors/EventQueues/EventQueueStressTests.cs b/Tests/Tests.Actors/EventQueues/EventQueueStressTests.cs index 37047d8ea..42425320d 100644 --- a/Tests/Tests.Actors/EventQueues/EventQueueStressTests.cs +++ b/Tests/Tests.Actors/EventQueues/EventQueueStressTests.cs @@ -55,7 +55,7 @@ public async Task TestEnqueueDequeueEvents() } }); - await Task.WhenAny(Task.WhenAll(enqueueTask, dequeueTask), Task.Delay(3000)); + await await Task.WhenAny(Task.WhenAll(enqueueTask, dequeueTask), Task.Delay(3000)); Assert.True(enqueueTask.IsCompleted); Assert.True(dequeueTask.IsCompleted); } @@ -84,7 +84,7 @@ public async Task TestEnqueueReceiveEvents() } }); - await Task.WhenAny(Task.WhenAll(enqueueTask, receiveTask), Task.Delay(3000)); + await await Task.WhenAny(Task.WhenAll(enqueueTask, receiveTask), Task.Delay(3000)); Assert.True(enqueueTask.IsCompleted); Assert.True(receiveTask.IsCompleted); } @@ -129,7 +129,7 @@ public async Task TestEnqueueReceiveEventsAlternateType() } }); - await Task.WhenAny(Task.WhenAll(enqueueTask, receiveTask), Task.Delay(3000)); + await await Task.WhenAny(Task.WhenAll(enqueueTask, receiveTask), Task.Delay(3000)); Assert.True(enqueueTask.IsCompleted); Assert.True(receiveTask.IsCompleted); } diff --git a/Tests/Tests.Actors/EventQueues/EventQueueTests.cs b/Tests/Tests.Actors/EventQueues/EventQueueTests.cs index 536aab9f0..bd2298eb3 100644 --- a/Tests/Tests.Actors/EventQueues/EventQueueTests.cs +++ b/Tests/Tests.Actors/EventQueues/EventQueueTests.cs @@ -162,7 +162,7 @@ public async Task TestReceiveEvent() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -208,7 +208,7 @@ public async Task TestReceiveEventWithPredicate() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -252,7 +252,7 @@ public async Task TestReceiveEventWithoutWaiting() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -285,7 +285,7 @@ public async Task TestReceiveEventWithPredicateWithoutWaiting() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -320,7 +320,7 @@ public async Task TestReceiveEventMultipleTypes() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -369,7 +369,7 @@ public async Task TestReceiveEventAfterMultipleEnqueues() Assert.Equal(DequeueStatus.Success, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } @@ -420,7 +420,7 @@ public async Task TestReceiveEventWithoutWaitingAndWithMultipleEventsInQueue() Assert.Equal(DequeueStatus.Unavailable, deqeueStatus); Assert.Equal(0, queue.Size); - await Task.WhenAny(tcs.Task, Task.Delay(500)); + await await Task.WhenAny(tcs.Task, Task.Delay(500)); Assert.True(tcs.Task.IsCompleted); } diff --git a/Tests/Tests.Actors/EventQueues/TestEventQueue.cs b/Tests/Tests.Actors/EventQueues/TestEventQueue.cs index 4422313d2..ac804b189 100644 --- a/Tests/Tests.Actors/EventQueues/TestEventQueue.cs +++ b/Tests/Tests.Actors/EventQueues/TestEventQueue.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; namespace Microsoft.Coyote.Actors.Tests { diff --git a/Tests/Tests.Actors/Logging/ActorLoggingTests.cs b/Tests/Tests.Actors/Logging/ActorLoggingTests.cs new file mode 100644 index 000000000..a4408bce5 --- /dev/null +++ b/Tests/Tests.Actors/Logging/ActorLoggingTests.cs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class ActorLoggingTests : BaseActorLoggingTests + { + public ActorLoggingTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact(Timeout = 5000)] + public void TestDefaultActorLogger() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + var runtimeLogger = (runtime.Logger as LogWriter).Logger; + Assert.True(runtimeLogger is NullLogger || runtimeLogger is TestOutputLogger); + + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines() + .RemoveNonDeterministicValues().SortLines(); + this.TestOutput.WriteLine(result); + Assert.Equal(string.Empty, result); + }, config); + } + + [Fact(Timeout = 5000)] + public void TestConsoleActorLogger() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info) + .WithConsoleLoggingEnabled(); + this.Test(async runtime => + { + Assert.IsType((runtime.Logger as LogWriter).Logger); + + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines() + .RemoveNonDeterministicValues().SortLines(); + string expected = StringExtensions.FormatLines( + " TestMonitor was created.", + " TestMonitor enters state 'Init'.", + " TestMonitor is processing event 'SetupEvent' in state 'Init'.", + " TestMonitor executed action 'OnSetup' in state 'Init'.", + " M() was created by thread ''.", + " N() was created by M().", + " M() sent event 'E' to N().", + " N() enqueued event 'E'.", + " N() enters state 'Init'.", + " N() invoked action 'OnInitEntry' in state 'Init'.", + " N() dequeued event 'E' in state 'Init'.", + " N() is transitioning from state 'Init' to state 'N.Act'.", + " N() exits state 'Init'.", + " N() enters state 'Act'.", + " N() invoked action 'ActOnEntry' in state 'Act'.", + " N() in state 'Act' sent event 'E' to M().", + " M() enqueued event 'E'.", + " M() dequeued event 'E'.", + " M() invoked action 'Act'.", + " TestMonitor is processing event 'CompletedEvent' in state 'Init'.", + " TestMonitor executed action 'OnCompleted' in state 'Init'."); + expected = expected.NormalizeNewLines().SortLines(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + + [Fact(Timeout = 5000)] + public void TestCustomActorLogger() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + using var logger = new MemoryLogger(config.VerbosityLevel); + this.Test(async runtime => + { + var runtimeLogger = (runtime.Logger as LogWriter).Logger; + Assert.True(runtimeLogger is NullLogger || runtimeLogger is TestOutputLogger); + + runtime.Logger = logger; + Assert.IsType((runtime.Logger as LogWriter).Logger); + + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = logger.ToString().RemoveNonDeterministicValues().SortLines(); + string expected = StringExtensions.FormatLines( + " TestMonitor was created.", + " TestMonitor enters state 'Init'.", + " TestMonitor is processing event 'SetupEvent' in state 'Init'.", + " TestMonitor executed action 'OnSetup' in state 'Init'.", + " M() was created by thread ''.", + " N() was created by M().", + " M() sent event 'E' to N().", + " N() enqueued event 'E'.", + " N() enters state 'Init'.", + " N() invoked action 'OnInitEntry' in state 'Init'.", + " N() dequeued event 'E' in state 'Init'.", + " N() is transitioning from state 'Init' to state 'N.Act'.", + " N() exits state 'Init'.", + " N() enters state 'Act'.", + " N() invoked action 'ActOnEntry' in state 'Act'.", + " N() in state 'Act' sent event 'E' to M().", + " M() enqueued event 'E'.", + " M() dequeued event 'E'.", + " M() invoked action 'Act'.", + " TestMonitor is processing event 'CompletedEvent' in state 'Init'.", + " TestMonitor executed action 'OnCompleted' in state 'Init'."); + expected = expected.NormalizeNewLines().SortLines(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + } +} diff --git a/Tests/Tests.Actors/Logging/BaseActorLoggingTests.cs b/Tests/Tests.Actors/Logging/BaseActorLoggingTests.cs new file mode 100644 index 000000000..2a04216dd --- /dev/null +++ b/Tests/Tests.Actors/Logging/BaseActorLoggingTests.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.Coyote.Specifications; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class BaseActorLoggingTests : BaseActorTest + { + public BaseActorLoggingTests(ITestOutputHelper output) + : base(output) + { + } + + internal class SetupEvent : Event + { + } + + internal class CompletedEvent : Event + { + } + + internal class TestMonitor : Monitor + { + [Start] + [OnEventDoAction(typeof(SetupEvent), nameof(OnSetup))] + [OnEventDoAction(typeof(CompletedEvent), nameof(OnCompleted))] + private class Init : State + { + } + +#pragma warning disable CA1822 // Mark members as static + private void OnSetup() + { + } + + private void OnCompleted() + { + } +#pragma warning restore CA1822 // Mark members as static + } + + internal class E : Event + { + public ActorId Id; + + public E(ActorId id) + { + this.Id = id; + } + } + + [OnEventDoAction(typeof(E), nameof(Act))] + internal class M : Actor + { + protected override async Task OnInitializeAsync(Event e) + { + await base.OnInitializeAsync(e); + var n = this.CreateActor(typeof(N)); + this.SendEvent(n, new E(this.Id)); + } + + private void Act() + { + this.Monitor(new CompletedEvent()); + } + } + + internal class S : Monitor + { + [Start] + [Hot] + [OnEventDoAction(typeof(E), nameof(OnE))] + private class Init : State + { + } + + [Cold] + private class Done : State + { + } + + private void OnE() => this.RaiseGotoStateEvent(); + } + + internal class N : StateMachine + { + [Start] + [OnEntry(nameof(OnInitEntry))] + [OnEventGotoState(typeof(E), typeof(Act))] + private class Init : State + { + } + +#pragma warning disable CA1822 // Mark members as static + private void OnInitEntry() +#pragma warning restore CA1822 // Mark members as static + { + } + + [OnEntry(nameof(ActOnEntry))] + private class Act : State + { + } + + private void ActOnEntry(Event e) + { + this.Monitor(e); + ActorId m = (e as E).Id; + this.SendEvent(m, new E(this.Id)); + } + } + } +} diff --git a/Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLog.cs b/Tests/Tests.Actors/Logging/CustomActorRuntimeLog.cs similarity index 98% rename from Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLog.cs rename to Tests/Tests.Actors/Logging/CustomActorRuntimeLog.cs index 50ad7ee13..2fbbcb8dc 100644 --- a/Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLog.cs +++ b/Tests/Tests.Actors/Logging/CustomActorRuntimeLog.cs @@ -3,10 +3,9 @@ using System; using System.Text; -using Microsoft.Coyote.Actors; using Microsoft.Coyote.Actors.Timers; -namespace Microsoft.Coyote.Tests.Common.Runtime +namespace Microsoft.Coyote.Actors.Tests.Logging { public class CustomActorRuntimeLog : IActorRuntimeLog { diff --git a/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTests.cs b/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTests.cs deleted file mode 100644 index 8fe6cb67e..000000000 --- a/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTests.cs +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Threading.Tasks; -using Microsoft.Coyote.Actors.Coverage; -using Microsoft.Coyote.IO; -using Microsoft.Coyote.Specifications; -using Microsoft.Coyote.Tests.Common; -using Microsoft.Coyote.Tests.Common.Runtime; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.Coyote.Actors.Tests -{ - public class CustomActorRuntimeLogTests : BaseActorTest - { - public CustomActorRuntimeLogTests(ITestOutputHelper output) - : base(output) - { - } - - internal class SetupEvent : Event - { - public TaskCompletionSource Tcs; - - public SetupEvent(TaskCompletionSource tcs) - { - this.Tcs = tcs; - } - } - - internal class CompletedEvent : Event - { - } - - internal class TestMonitor : Monitor - { - private TaskCompletionSource Tcs; - - [Start] - [OnEventDoAction(typeof(SetupEvent), nameof(OnSetup))] - [OnEventDoAction(typeof(CompletedEvent), nameof(OnCompleted))] - private class Init : State - { - } - - private void OnSetup(Event e) - { - this.Tcs = ((SetupEvent)e).Tcs; - } - - private void OnCompleted() - { - this.Tcs.TrySetResult(true); - } - } - - internal class E : Event - { - public ActorId Id; - - public E(ActorId id) - { - this.Id = id; - } - } - - [OnEventDoAction(typeof(E), nameof(Act))] - internal class M : Actor - { - protected override async Task OnInitializeAsync(Event e) - { - await base.OnInitializeAsync(e); - var n = this.CreateActor(typeof(N)); - this.SendEvent(n, new E(this.Id)); - } - - private void Act() - { - this.Monitor(new CompletedEvent()); - } - } - - internal class S : Monitor - { - [Start] - [Hot] - [OnEventDoAction(typeof(E), nameof(OnE))] - private class Init : State - { - } - - [Cold] - private class Done : State - { - } - - private void OnE() => this.RaiseGotoStateEvent(); - } - - internal class N : StateMachine - { - [Start] - [OnEntry(nameof(OnInitEntry))] - [OnEventGotoState(typeof(E), typeof(Act))] - private class Init : State - { - } - -#pragma warning disable CA1822 // Mark members as static - private void OnInitEntry() -#pragma warning restore CA1822 // Mark members as static - { - } - - [OnEntry(nameof(ActOnEntry))] - private class Act : State - { - } - - private void ActOnEntry(Event e) - { - this.Monitor(e); - ActorId m = (e as E).Id; - this.SendEvent(m, new E(this.Id)); - } - } - - [Fact(Timeout = 5000)] - public void TestCustomLogger() - { - var config = this.GetConfiguration(); - this.Test(async runtime => - { - using CustomLogger logger = new CustomLogger(); - runtime.Logger = logger; - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.CreateActor(typeof(M)); - await this.WaitAsync(tcs.Task); - await Task.Delay(200); - Assert.True(tcs.Task.IsCompleted, "The task await returned but the task is not completed???"); - - string result = logger.ToString(); - string expected = @" TestMonitor was created. - TestMonitor enters state 'Init'. - TestMonitor is processing event 'SetupEvent' in state 'Init'. - TestMonitor executed action 'OnSetup' in state 'Init'. - M() was created by thread ''. - N() was created by M(). - M() sent event 'E' to N(). - N() enqueued event 'E'. - N() enters state 'Init'. - N() invoked action 'OnInitEntry' in state 'Init'. - N() dequeued event 'E' in state 'Init'. - N() is transitioning from state 'Init' to state 'N.Act'. - N() exits state 'Init'. - N() enters state 'Act'. - N() invoked action 'ActOnEntry' in state 'Act'. - N() in state 'Act' sent event 'E' to M(). - M() enqueued event 'E'. - M() dequeued event 'E'. - M() invoked action 'Act'. - TestMonitor is processing event 'CompletedEvent' in state 'Init'. - TestMonitor executed action 'OnCompleted' in state 'Init'. - Thread '' nondeterministically chose ''."; - - result = result.RemoveNonDeterministicValues().RemoveDebugLines().SortLines(); - expected = expected.NormalizeNewLines().SortLines(); - - Assert.Equal(expected, result); - }, config); - } - - [Fact(Timeout = 5000)] - public void TestGraphLogger() - { - var config = this.GetConfiguration(); - this.Test(async runtime => - { - using CustomLogger logger = new CustomLogger(); - runtime.Logger = logger; - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - var graphBuilder = new ActorRuntimeLogGraphBuilder(false, false); - runtime.RegisterLog(graphBuilder); - runtime.CreateActor(typeof(M)); - await this.WaitAsync(tcs.Task); - await Task.Delay(200); - Assert.True(tcs.Task.IsCompleted, "The task await returned but the task is not completed???"); - - string result = graphBuilder.Graph.ToString(); - string expected = @" - - - - - - - - - - - - - - - - - - - - - - -"; - - result = result.RemoveNonDeterministicValues(); - expected = expected.RemoveNonDeterministicValues(); - - Assert.Equal(expected, result); - }, config); - } - - [Fact(Timeout = 5000)] - public void TestCustomLoggerNoVerbosity() - { - Configuration config = this.GetConfiguration(); - this.Test(async runtime => - { - runtime.Logger = new NullLogger(); - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.CreateActor(typeof(M)); - await this.WaitAsync(tcs.Task); - Assert.Equal("Microsoft.Coyote.IO.NullLogger", runtime.Logger.ToString()); - }, config); - } - - [Fact(Timeout = 5000)] - public void TestNullCustomLogger() - { - Configuration config = this.GetConfiguration(); - this.Test(async runtime => - { - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.Logger = null; - runtime.CreateActor(typeof(M)); - await this.WaitAsync(tcs.Task); - Assert.Equal("Microsoft.Coyote.IO.NullLogger", runtime.Logger.ToString()); - }, config); - } - - [Fact(Timeout = 5000)] - public void TestCustomActorRuntimeLogFormatter() - { - var config = this.GetConfiguration(); - this.Test(async runtime => - { - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.RegisterMonitor(); - runtime.Logger = null; - - var logger = new CustomActorRuntimeLog(); - runtime.RegisterLog(logger); - - runtime.CreateActor(typeof(M)); - - await this.WaitAsync(tcs.Task, 5000); - await Task.Delay(200); - - string result = logger.ToString(); - string expected = @"CreateActor -CreateStateMachine -StateTransition -StateTransition -StateTransition"; - - result = result.RemoveNonDeterministicValues().RemoveDebugLines(); - expected = expected.NormalizeNewLines(); - - Assert.Equal(expected, result); - }, config); - } - - internal class PingEvent : Event - { - public readonly ActorId Caller; - - public PingEvent(ActorId caller) - { - this.Caller = caller; - } - } - - internal class PongEvent : Event - { - } - - internal class ClientSetupEvent : Event - { - public readonly ActorId ServerId; - - public ClientSetupEvent(ActorId server) - { - this.ServerId = server; - } - } - - [OnEventDoAction(typeof(PongEvent), nameof(HandlePong))] - internal class Client : Actor - { - public ActorId ServerId; - - protected override Task OnInitializeAsync(Event initialEvent) - { - this.Logger.WriteLine("{0} initializing", this.Id); - this.ServerId = ((ClientSetupEvent)initialEvent).ServerId; - this.Logger.WriteLine("{0} sending ping event to server", this.Id); - this.SendEvent(this.ServerId, new PingEvent(this.Id)); - return base.OnInitializeAsync(initialEvent); - } - - private void HandlePong() - { - this.Logger.WriteLine("{0} received pong event", this.Id); - } - } - - internal class Server : StateMachine - { - private int Count; - - [Start] - [OnEventGotoState(typeof(PingEvent), typeof(Pong))] - private class Init : State - { - } - - [OnEntry(nameof(HandlePing))] - [OnEventDoAction(typeof(PingEvent), nameof(HandlePing))] - private class Pong : State - { - } - - private void HandlePing(Event e) - { - this.Count++; - PingEvent ping = (PingEvent)e; - this.Logger.WriteLine("Server handling ping"); - this.Logger.WriteLine("Server sending pong back to caller"); - this.SendEvent(ping.Caller, new PongEvent()); - - if (this.Count is 3) - { - this.RaiseGotoStateEvent(); - } - } - - [OnEntry(nameof(HandleComplete))] - private class Complete : State - { - } - - private void HandleComplete() - { - this.Logger.WriteLine("Test Complete"); - this.Monitor(new CompletedEvent()); - } - } - - [Fact(Timeout = 5000)] - public void TestGraphLoggerInstances() - { - var config = this.GetConfiguration(); - this.Test(async runtime => - { - using CustomLogger logger = new CustomLogger(); - runtime.Logger = logger; - - var graphBuilder = new ActorRuntimeLogGraphBuilder(false, false); - - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.RegisterLog(graphBuilder); - - ActorId serverId = runtime.CreateActor(typeof(Server)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - - await this.WaitAsync(tcs.Task); - await Task.Delay(1000); - Assert.True(tcs.Task.IsCompleted, "The task await returned but the task is not completed???"); - - string result = graphBuilder.Graph.ToString(); - result = result.RemoveInstanceIds(); - - Assert.Contains("", result); - Assert.Contains("", result); - Assert.Contains("", result); - }, config); - } - - [Fact(Timeout = 5000)] - public void TestGraphLoggerCollapsed() - { - var config = this.GetConfiguration(); - this.Test(async runtime => - { - using CustomLogger logger = new CustomLogger(); - runtime.Logger = logger; - - var graphBuilder = new ActorRuntimeLogGraphBuilder(false, true); - - var tcs = new TaskCompletionSource(); - runtime.RegisterMonitor(); - runtime.Monitor(new SetupEvent(tcs)); - runtime.RegisterLog(graphBuilder); - - ActorId serverId = runtime.CreateActor(typeof(Server)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); - - await this.WaitAsync(tcs.Task, 5000); - await Task.Delay(1000); - Assert.True(tcs.Task.IsCompleted, "The task await returned but the task is not completed???"); - - string result = graphBuilder.Graph.ToString(); - - Assert.Contains("", result); - Assert.Contains("", result); - }, config); - } - } -} diff --git a/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTextFormatter.cs b/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTextFormatter.cs new file mode 100644 index 000000000..176b3ca34 --- /dev/null +++ b/Tests/Tests.Actors/Logging/CustomActorRuntimeLogTextFormatter.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class CustomActorRuntimeLogTextFormatter : ActorRuntimeLogTextFormatter + { + /// + public override void OnCreateActor(ActorId id, string creatorName, string creatorType) => + this.Logger.WriteLine(""); + + /// + public override void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) => + this.Logger.WriteLine(""); + + /// + public override void OnEnqueueEvent(ActorId id, Event e) => + this.Logger.WriteLine(""); + + /// + public override void OnDequeueEvent(ActorId id, string stateName, Event e) => + this.Logger.WriteLine(""); + + /// + public override void OnSendEvent(ActorId targetActorId, string senderName, string senderType, + string senderStateName, Event e, Guid eventGroupId, bool isTargetHalted) => + this.Logger.WriteLine(""); + + /// + public override void OnGotoState(ActorId id, string currentStateName, string newStateName) => + this.Logger.WriteLine(""); + + /// + public override void OnStateTransition(ActorId id, string stateName, bool isEntry) => + this.Logger.WriteLine(""); + + /// + public override void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName) => + this.Logger.WriteLine(""); + + /// + public override void OnCreateMonitor(string monitorType) => + this.Logger.WriteLine(""); + + /// + public override void OnMonitorExecuteAction(string monitorType, string stateName, string actionName) => + this.Logger.WriteLine(""); + + /// + public override void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, + string senderType, string senderStateName, Event e) => + this.Logger.WriteLine(""); + + /// + public override void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) => + this.Logger.WriteLine(""); + + /// + public override void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState) => + this.Logger.WriteLine(""); + } +} diff --git a/Tests/Tests.Actors/Logging/GraphLoggingTests.cs b/Tests/Tests.Actors/Logging/GraphLoggingTests.cs new file mode 100644 index 000000000..b98f1f7c4 --- /dev/null +++ b/Tests/Tests.Actors/Logging/GraphLoggingTests.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.Coyote.Actors.Coverage; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class GraphLoggingTests : BaseActorLoggingTests + { + public GraphLoggingTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact(Timeout = 5000)] + public void TestGraphLogger() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + + var graphBuilder = new ActorRuntimeLogGraphBuilder(false, false); + runtime.RegisterLog(graphBuilder); + + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = graphBuilder.Graph.ToString().RemoveNonDeterministicValues(); + string expected = StringExtensions.FormatLines( + "", + " ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + " ", + " ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + $" ", + " ", + ""); + expected = expected.RemoveNonDeterministicValues(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + + internal class PingEvent : Event + { + public readonly ActorId Caller; + + public PingEvent(ActorId caller) + { + this.Caller = caller; + } + } + + internal class PongEvent : Event + { + } + + internal class ClientSetupEvent : Event + { + public readonly ActorId ServerId; + + public ClientSetupEvent(ActorId server) + { + this.ServerId = server; + } + } + + [OnEventDoAction(typeof(PongEvent), nameof(HandlePong))] + internal class Client : Actor + { + public ActorId ServerId; + + protected override Task OnInitializeAsync(Event initialEvent) + { + this.Logger.WriteLine("{0} initializing", this.Id); + this.ServerId = ((ClientSetupEvent)initialEvent).ServerId; + this.Logger.WriteLine("{0} sending ping event to server", this.Id); + this.SendEvent(this.ServerId, new PingEvent(this.Id)); + return base.OnInitializeAsync(initialEvent); + } + + private void HandlePong() + { + this.Logger.WriteLine("{0} received pong event", this.Id); + } + } + + internal class Server : StateMachine + { + private int Count; + + [Start] + [OnEventGotoState(typeof(PingEvent), typeof(Pong))] + private class Init : State + { + } + + [OnEntry(nameof(HandlePing))] + [OnEventDoAction(typeof(PingEvent), nameof(HandlePing))] + private class Pong : State + { + } + + private void HandlePing(Event e) + { + this.Count++; + PingEvent ping = (PingEvent)e; + this.Logger.WriteLine("Server handling ping"); + this.Logger.WriteLine("Server sending pong back to caller"); + this.SendEvent(ping.Caller, new PongEvent()); + + if (this.Count is 3) + { + this.RaiseGotoStateEvent(); + } + } + + [OnEntry(nameof(HandleComplete))] + private class Complete : State + { + } + + private void HandleComplete() + { + this.Logger.WriteLine("Test Complete"); + this.Monitor(new CompletedEvent()); + } + } + + [Fact(Timeout = 5000)] + public void TestGraphLoggerInstances() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + + var graphBuilder = new ActorRuntimeLogGraphBuilder(false, false); + runtime.RegisterLog(graphBuilder); + + ActorId serverId = runtime.CreateActor(typeof(Server)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = graphBuilder.Graph.ToString().RemoveInstanceIds(); + this.TestOutput.WriteLine(result); + + Assert.Contains($"", result); + Assert.Contains($"", result); + Assert.Contains($"", result); + }, config); + } + + [Fact(Timeout = 5000)] + public void TestGraphLoggerCollapsed() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + + var graphBuilder = new ActorRuntimeLogGraphBuilder(false, true); + runtime.RegisterLog(graphBuilder); + + ActorId serverId = runtime.CreateActor(typeof(Server)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + runtime.CreateActor(typeof(Client), new ClientSetupEvent(serverId)); + + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = graphBuilder.Graph.ToString().RemoveInstanceIds(); + this.TestOutput.WriteLine(result); + + Assert.Contains($"", result); + Assert.Contains($"", result); + }, config); + } + } +} diff --git a/Tests/Tests.Actors/Logging/RuntimeLogTests.cs b/Tests/Tests.Actors/Logging/RuntimeLogTests.cs new file mode 100644 index 000000000..6bc514ad5 --- /dev/null +++ b/Tests/Tests.Actors/Logging/RuntimeLogTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class RuntimeLogTests : BaseActorLoggingTests + { + public RuntimeLogTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact(Timeout = 5000)] + public void TestCustomActorRuntimeLog() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + Assert.IsType((runtime.Logger as LogWriter).Logger); + + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.RegisterMonitor(); + + var log = new CustomActorRuntimeLog(); + runtime.RegisterLog(log); + + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = log.ToString().RemoveNonDeterministicValues().FormatNewLine(); + string expected = StringExtensions.FormatLines( + "CreateActor", + "CreateStateMachine", + "StateTransition", + "StateTransition", + "StateTransition"); + expected = expected.NormalizeNewLines(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + } +} diff --git a/Tests/Tests.Actors/Logging/RuntimeLogTextFormattingTests.cs b/Tests/Tests.Actors/Logging/RuntimeLogTextFormattingTests.cs new file mode 100644 index 000000000..1512634ba --- /dev/null +++ b/Tests/Tests.Actors/Logging/RuntimeLogTextFormattingTests.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Actors.Tests.Logging +{ + public class RuntimeLogTextFormattingTests : BaseActorLoggingTests + { + public RuntimeLogTextFormattingTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact(Timeout = 5000)] + public void TestDefaultActorLoggerWithCustomFormatter() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + this.Test(async runtime => + { + Assert.IsType((runtime.Logger as LogWriter).Logger); + + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + var log = new CustomActorRuntimeLogTextFormatter(); + runtime.RegisterLog(log); + + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines() + .RemoveNonDeterministicValues().SortLines(); + this.TestOutput.WriteLine(result); + Assert.Equal(string.Empty, result); + }, config); + } + + [Fact(Timeout = 5000)] + public void TestConsoleActorLoggerWithCustomFormatter() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info) + .WithConsoleLoggingEnabled(); + this.Test(async runtime => + { + Assert.IsType((runtime.Logger as LogWriter).Logger); + + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + var log = new CustomActorRuntimeLogTextFormatter(); + runtime.RegisterLog(log); + + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines() + .RemoveNonDeterministicValues().SortLines(); + string expected = StringExtensions.FormatLines( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""); + expected = expected.NormalizeNewLines().SortLines(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + + [Fact(Timeout = 5000)] + public void TestCustomActorLoggerWithCustomFormatter() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(VerbosityLevel.Info); + using var logger = new MemoryLogger(config.VerbosityLevel); + this.Test(async runtime => + { + Assert.IsType((runtime.Logger as LogWriter).Logger); + runtime.Logger = logger; + Assert.IsType((runtime.Logger as LogWriter).Logger); + + var log = new CustomActorRuntimeLogTextFormatter(); + runtime.RegisterLog(log); + + runtime.RegisterMonitor(); + runtime.Monitor(new SetupEvent()); + runtime.CreateActor(typeof(M)); + await (runtime as ActorExecutionContext).WaitUntilQuiescenceAsync(); + + string result = logger.ToString().RemoveNonDeterministicValues().SortLines(); + string expected = StringExtensions.FormatLines( + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""); + expected = expected.NormalizeNewLines().SortLines(); + + this.TestOutput.WriteLine(result); + Assert.Equal(expected, result); + }, config); + } + } +} diff --git a/Tests/Tests.Actors/Operations/CreateActorIdFromNameTests.cs b/Tests/Tests.Actors/Operations/CreateActorIdFromNameTests.cs index 7bec2109b..0f1c22217 100644 --- a/Tests/Tests.Actors/Operations/CreateActorIdFromNameTests.cs +++ b/Tests/Tests.Actors/Operations/CreateActorIdFromNameTests.cs @@ -87,7 +87,7 @@ public void TestCreateActorIdFromName1() r.CreateActor(m2, typeof(M)); await setup.Completed.Task; }, - Configuration.Create().WithProductionMonitorEnabled()); + Configuration.Create()); } [Fact(Timeout = 5000)] @@ -105,7 +105,7 @@ public void TestCreateActorIdFromName2() r.CreateActor(m2, typeof(M)); await setup.Completed.Task; }, - Configuration.Create().WithProductionMonitorEnabled()); + Configuration.Create()); } private class M2 : StateMachine @@ -273,7 +273,7 @@ public void TestCreateActorIdFromName8() r.Monitor(setup); r.CreateActor(typeof(M7)); await setup.Completed.Task; - }, Configuration.Create().WithProductionMonitorEnabled()); + }, Configuration.Create()); } } } diff --git a/Tests/Tests.Actors/Operations/SendAndExecuteTests.cs b/Tests/Tests.Actors/Operations/SendAndExecuteTests.cs index 638225336..2c79d96f3 100644 --- a/Tests/Tests.Actors/Operations/SendAndExecuteTests.cs +++ b/Tests/Tests.Actors/Operations/SendAndExecuteTests.cs @@ -270,7 +270,6 @@ private void OnSEReturns() public async Task TestMachineHaltsOnSendExec() { var config = this.GetConfiguration(); - config.IsMonitoringEnabledInInProduction = true; await this.RunAsync(async r => { var failed = false; diff --git a/Tests/Tests.Actors/Runtime/OnEventDroppedTests.cs b/Tests/Tests.Actors/Runtime/OnEventDroppedTests.cs index 161d6146e..dbdd1273e 100644 --- a/Tests/Tests.Actors/Runtime/OnEventDroppedTests.cs +++ b/Tests/Tests.Actors/Runtime/OnEventDroppedTests.cs @@ -219,7 +219,6 @@ private void Processed() public async Task TestProcessedOrDropped() { var config = this.GetConfiguration(); - config.IsMonitoringEnabledInInProduction = true; await this.RunAsync(async r => { var tcs = new TaskCompletionSource(); diff --git a/Tests/Tests.Actors/Timers/TimerTests.cs b/Tests/Tests.Actors/Timers/TimerTests.cs index 654c2a8b3..5c8c4029e 100644 --- a/Tests/Tests.Actors/Timers/TimerTests.cs +++ b/Tests/Tests.Actors/Timers/TimerTests.cs @@ -4,7 +4,7 @@ using System; using System.Threading.Tasks; using Microsoft.Coyote.Actors.Timers; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Xunit; using Xunit.Abstractions; diff --git a/Tests/Tests.BugFinding/Tasks/Join/TaskWhenAnyTests.cs b/Tests/Tests.BugFinding/Tasks/Join/TaskWhenAnyTests.cs index cc3b33fd9..de555e8a9 100644 --- a/Tests/Tests.BugFinding/Tasks/Join/TaskWhenAnyTests.cs +++ b/Tests/Tests.BugFinding/Tasks/Join/TaskWhenAnyTests.cs @@ -201,7 +201,7 @@ public void TestWhenAnyWithAsyncCaller() } entry.Value = 3; - await Task.WhenAny(tasks); + await await Task.WhenAny(tasks); entry.Value = 1; }; @@ -229,7 +229,7 @@ public void TestWhenAnyWithResultAndAsyncCaller() } entry.Value = 3; - await Task.WhenAny(tasks); + await await Task.WhenAny(tasks); entry.Value = 1; }; @@ -313,7 +313,7 @@ public void TestWhenAnyWithIncompleteTask() { // Test that `WhenAny` can complete even if one of the tasks cannot complete until later. var tcs = new TaskCompletionSource(); - await Task.WhenAny(tcs.Task, Task.Delay(1)); + await await Task.WhenAny(tcs.Task, Task.Delay(1)); tcs.SetResult(true); await tcs.Task; }, @@ -327,7 +327,7 @@ public void TestWhenAnyWithIncompleteGenericTask() { // Test that `WhenAny` can complete even if one of the tasks cannot complete until later. var tcs = new TaskCompletionSource(); - await Task.WhenAny(tcs.Task, Task.FromResult(true)); + await await Task.WhenAny(tcs.Task, Task.FromResult(true)); tcs.SetResult(true); await tcs.Task; }, diff --git a/Tests/Tests.BugFinding/Tasks/Logging/CustomTaskLogTests.cs b/Tests/Tests.BugFinding/Tasks/Logging/CustomTaskLogTests.cs deleted file mode 100644 index 3c744665c..000000000 --- a/Tests/Tests.BugFinding/Tasks/Logging/CustomTaskLogTests.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Threading.Tasks; -using Microsoft.Coyote.IO; -using Microsoft.Coyote.Runtime; -using Microsoft.Coyote.Specifications; -using Microsoft.Coyote.SystematicTesting; -using Microsoft.Coyote.Tests.Common; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.Coyote.BugFinding.Tests -{ - public class CustomTaskLogTests : BaseBugFindingTest - { - public CustomTaskLogTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact(Timeout = 5000)] - public void TestCustomLogger() - { - InMemoryLogger log = new InMemoryLogger(); - - var config = this.GetConfiguration() - .WithTestingIterations(3) - .WithRandomGeneratorSeed(0); - using TestingEngine engine = TestingEngine.Create(config, (ICoyoteRuntime runtime) => - { - runtime.Logger.WriteLine("Hello world!"); - }); - - engine.Logger = log; - engine.Run(); - - string result = log.ToString(); - string expected = @"... Setting up the test: -..... Using the random[seed:0] exploration strategy. -... Running test iterations: -..... Iteration #1 -[coyote::test] Runtime '' started test on thread ''. -Hello world! -[coyote::test] Exploration finished in runtime '' [reached the end of the test method]. -..... Iteration #2 -[coyote::test] Runtime '' started test on thread ''. -Hello world! -[coyote::test] Exploration finished in runtime '' [reached the end of the test method]. -..... Iteration #3 -[coyote::test] Runtime '' started test on thread ''. -Hello world! -[coyote::test] Exploration finished in runtime '' [reached the end of the test method]. -"; - - result = result.RemoveNonDeterministicValues().RemoveDebugLines(); - expected = expected.RemoveNonDeterministicValues(); - - Assert.Equal(expected, result); - } - - private async Task RunAsync(ICoyoteRuntime r) - { - await Task.Run(async () => - { - r.Logger.WriteLine($"Task '{Task.CurrentId}' is running."); - await Task.Delay(0); - r.Logger.WriteLine($"Task '{Task.CurrentId}' completed."); - }); - - await Task.Run(async () => - { - r.Logger.WriteLine($"Task '{Task.CurrentId}' is running."); - await Task.Delay(0); - r.Logger.WriteLine($"Task '{Task.CurrentId}' completed."); - }); - - Specification.Assert(false, "Reached test assertion."); - } - - [Fact(Timeout = 5000)] - public void TestCustomTaskRuntimeLog() - { - var config = this.GetConfiguration() - .WithRandomGeneratorSeed(0); - using TestingEngine engine = TestingEngine.Create(config, this.RunAsync); - - try - { - engine.Run(); - - var numErrors = engine.TestReport.NumOfFoundBugs; - Assert.True(numErrors is 1, GetBugReport(engine)); - Assert.True(engine.ReadableTrace != null, "Readable trace is null."); - Assert.True(engine.ReadableTrace.Length > 0, "Readable trace is empty."); - - string result = engine.ReadableTrace.ToString(); - string expected = @"[coyote::test] Runtime '' started test on thread ''. -Task '' is running. -Task '' completed. -Task '' is running. -Task '' completed. -[coyote::error] Reached test assertion. -[coyote::test] Exploration finished in runtime '' [found a bug using the '' strategy]. -[coyote::report] Testing statistics: -[coyote::report] Found 1 bug. -[coyote::report] Scheduling statistics: -[coyote::report] Explored 1 schedule: 1 fair and 0 unfair. -[coyote::report] Found 100.00% buggy schedules. -[coyote::report] Controlled 3 operations: 3 (), 3 (), 3 (). -[coyote::report] Degree of concurrency: 2 (), 2 (), 2 (). -[coyote::report] Number of scheduling decisions in fair terminating schedules: 4 (), 4 (), 4 ()."; - - result = result.RemoveNonDeterministicValues().RemoveDebugLines(); - expected = expected.RemoveNonDeterministicValues(); - - Assert.Equal(expected, result); - } - catch (Exception ex) - { - Assert.False(true, ex.Message + "\n" + ex.StackTrace); - } - } - } -} diff --git a/Tests/Tests.BugFinding/Tasks/TaskInterleavingsTests.cs b/Tests/Tests.BugFinding/Tasks/TaskInterleavingsTests.cs index 8de935f97..fd867c0f6 100644 --- a/Tests/Tests.BugFinding/Tasks/TaskInterleavingsTests.cs +++ b/Tests/Tests.BugFinding/Tasks/TaskInterleavingsTests.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Specifications; using Microsoft.Coyote.Tests.Common; using Xunit; @@ -181,30 +181,30 @@ public void TestInterleavingsWithNestedParallelTasks() public void TestExploreAllInterleavings() { SortedSet results = new SortedSet(); - string success = "Explored interleavings."; + + var config = this.GetConfiguration().WithTestingIterations(100); this.TestWithError(async (runtime) => { - InMemoryLogger log = new InMemoryLogger(); - + var logger = new MemoryLogger(VerbosityLevel.Info); Task task1 = Task.Run(async () => { - log.WriteLine("1"); + logger.WriteLine("1"); await Task.Delay(runtime.RandomInteger(10)); - log.WriteLine("2"); + logger.WriteLine("2"); }); Task task2 = Task.Run(() => { - log.WriteLine("3"); + logger.WriteLine("3"); }); await Task.WhenAll(task1, task2); - results.Add(log.ToString()); + results.Add(logger.ToString()); Specification.Assert(results.Count < 3, success); }, - configuration: this.GetConfiguration().WithTestingIterations(100), + configuration: config, expectedError: success); string expected = @"1 diff --git a/Tests/Tests.Common/BaseTest.cs b/Tests/Tests.Common/BaseTest.cs index 4caa39b07..795a1fd59 100644 --- a/Tests/Tests.Common/BaseTest.cs +++ b/Tests/Tests.Common/BaseTest.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Coyote.Actors; using Microsoft.Coyote.Actors.Coverage; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Runtime; using Microsoft.Coyote.SystematicTesting; using Xunit; @@ -92,7 +93,7 @@ private TestReport RunSystematicTest(Delegate test, Configuration configuration) { configuration ??= this.GetConfiguration(); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { using TestingEngine engine = RunTestingEngine(test, configuration, logger); @@ -104,10 +105,6 @@ private TestReport RunSystematicTest(Delegate test, Configuration configuration) { Assert.False(true, ex.Message + "\n" + ex.StackTrace); } - finally - { - logger.Dispose(); - } return null; } @@ -287,7 +284,7 @@ private void RunSystematicTestWithErrors(Delegate test, Configuration configurat configuration = configuration.WithTestingIterations(configuration.TestingIterations * 50); } - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { using var engine = RunTestingEngine(test, configuration, logger); @@ -306,10 +303,6 @@ private void RunSystematicTestWithErrors(Delegate test, Configuration configurat { Assert.False(true, ex.Message + "\n" + ex.StackTrace); } - finally - { - logger.Dispose(); - } } protected void TestWithException(Action test, Configuration configuration = null, bool replay = false) @@ -380,7 +373,7 @@ private void RunSystematicTestWithException(Delegate test, Configura Assert.True(exceptionType.IsSubclassOf(typeof(Exception)), "Please configure the test correctly. " + $"Type '{exceptionType}' is not an exception type."); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { using var engine = RunTestingEngine(test, configuration, logger); @@ -399,20 +392,17 @@ private void RunSystematicTestWithException(Delegate test, Configura { Assert.False(true, ex.Message + "\n" + ex.StackTrace); } - finally - { - logger.Dispose(); - } } protected void RunTest(Action test, Configuration configuration = null) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); runtime.Logger = logger; for (int i = 0; i < configuration.TestingIterations; i++) @@ -424,25 +414,25 @@ protected void RunTest(Action test, Configuration configuration = { Assert.False(true, ex.Message + "\n" + ex.StackTrace); } - finally - { - logger.Dispose(); - } } protected async Task RunAsync(Func test, Configuration configuration = null, bool handleFailures = true) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); uint iterations = Math.Max(1, configuration.TestingIterations); for (int i = 0; i < iterations; i++) { - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); - runtime.Logger = logger; + if (!configuration.IsConsoleLoggingEnabled) + { + runtime.Logger = logger; + } var errorTask = new TaskCompletionSource(); if (handleFailures) @@ -455,7 +445,7 @@ protected async Task RunAsync(Func test, Configuration conf // TODO: but is this actually letting the test complete in the case // of actors which run completely asynchronously? - await Task.WhenAny(test(runtime), errorTask.Task); + await await Task.WhenAny(test(runtime), errorTask.Task); if (handleFailures && errorTask.Task.IsCompleted) { Assert.False(true, errorTask.Task.Result.Message); @@ -466,10 +456,6 @@ protected async Task RunAsync(Func test, Configuration conf Exception e = Unwrap(ex); Assert.False(true, e.Message + "\n" + e.StackTrace); } - finally - { - logger.Dispose(); - } } } @@ -513,12 +499,13 @@ private static string ExtractErrorMessage(Exception ex) private void RunWithErrors(Action test, Configuration configuration, TestErrorChecker errorChecker) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); string errorMessage = string.Empty; - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorTask = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -541,10 +528,6 @@ private void RunWithErrors(Action test, Configuration configurati { errorMessage = ExtractErrorMessage(ex); } - finally - { - logger.Dispose(); - } if (string.IsNullOrEmpty(errorMessage)) { @@ -557,12 +540,13 @@ private void RunWithErrors(Action test, Configuration configurati private async Task RunWithErrorsAsync(Func test, Configuration configuration, TestErrorChecker errorChecker) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); string errorMessage = string.Empty; - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorCompletion = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -585,10 +569,6 @@ private async Task RunWithErrorsAsync(Func test, Configurat { errorMessage = ExtractErrorMessage(ex); } - finally - { - logger.Dispose(); - } if (string.IsNullOrEmpty(errorMessage)) { @@ -601,16 +581,17 @@ private async Task RunWithErrorsAsync(Func test, Configurat protected void RunTestWithException(Action test, Configuration configuration = null) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); Exception actualException = null; Type exceptionType = typeof(TException); Assert.True(exceptionType.IsSubclassOf(typeof(Exception)), "Please configure the test correctly. " + $"Type '{exceptionType}' is not an exception type."); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorCompletion = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -632,10 +613,6 @@ protected void RunTestWithException(Action test, Conf { actualException = ex; } - finally - { - logger.Dispose(); - } if (actualException is null) { @@ -648,16 +625,17 @@ protected void RunTestWithException(Action test, Conf protected void RunTestWithException(Action test, Configuration configuration = null) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); Exception actualException = null; Type exceptionType = typeof(TException); Assert.True(exceptionType.IsSubclassOf(typeof(Exception)), "Please configure the test correctly. " + $"Type '{exceptionType}' is not an exception type."); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorCompletion = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -679,10 +657,6 @@ protected void RunTestWithException(Action test, Configuration confi { actualException = ex; } - finally - { - logger.Dispose(); - } if (actualException is null) { @@ -695,16 +669,17 @@ protected void RunTestWithException(Action test, Configuration confi protected async Task RunTestWithExceptionAsync(Func test, Configuration configuration = null) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); Exception actualException = null; Type exceptionType = typeof(TException); Assert.True(exceptionType.IsSubclassOf(typeof(Exception)), "Please configure the test correctly. " + $"Type '{exceptionType}' is not an exception type."); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorCompletion = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -728,10 +703,6 @@ protected async Task RunTestWithExceptionAsync(Func(Func(Func test, Configuration configuration = null) { configuration ??= this.GetConfiguration(); + configuration.WithActorQuiescenceCheckingEnabledOutsideTesting(); + configuration.WithMonitoringEnabledOutsideTesting(); Exception actualException = null; Type exceptionType = typeof(TException); Assert.True(exceptionType.IsSubclassOf(typeof(Exception)), "Please configure the test correctly. " + $"Type '{exceptionType}' is not an exception type."); - var logger = new TestOutputLogger(this.TestOutput); + using var logger = new TestOutputLogger(this.TestOutput); try { - configuration.IsMonitoringEnabledInInProduction = true; var runtime = ActorRuntimeFactory.Create(configuration); var errorCompletion = new TaskCompletionSource(); runtime.OnFailure += (e) => @@ -777,10 +749,6 @@ protected async Task RunTestWithExceptionAsync(Func test, Conf { actualException = ex; } - finally - { - logger.Dispose(); - } if (actualException is null) { @@ -792,10 +760,12 @@ protected async Task RunTestWithExceptionAsync(Func test, Conf private static TestingEngine RunTestingEngine(Delegate test, Configuration configuration, TestOutputLogger logger) { - var engine = new TestingEngine(configuration, test) + var logWriter = new LogWriter(configuration); + var engine = new TestingEngine(configuration, test, logWriter); + if (!configuration.IsConsoleLoggingEnabled) { - Logger = logger - }; + engine.SetLogger(logger); + } engine.Run(); return engine; @@ -841,7 +811,7 @@ protected static void ThrowException() throw new T(); protected virtual Configuration GetConfiguration() => Configuration.Create() - .WithDebugLoggingEnabled() + .WithVerbosityEnabled(VerbosityLevel.Debug) .WithTelemetryEnabled(false) .WithPartiallyControlledConcurrencyAllowed(false) .WithSystematicFuzzingFallbackEnabled(false); diff --git a/Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLogSubclass.cs b/Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLogSubclass.cs deleted file mode 100644 index ee88e22bf..000000000 --- a/Tests/Tests.Common/Runtime/Logging/CustomActorRuntimeLogSubclass.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using Microsoft.Coyote.Actors; - -namespace Microsoft.Coyote.BugFinding.Tests.Runtime -{ - public class CustomActorRuntimeLogSubclass : ActorRuntimeLogTextFormatter - { - public override void OnCreateActor(ActorId id, string creatorName, string creatorType) - { - this.Logger.WriteLine("."); - } - - public override void OnEnqueueEvent(ActorId id, Event e) - { - } - - public override void OnSendEvent(ActorId targetActorId, string senderName, string senderType, - string senderStateName, Event e, Guid eventGroupId, bool isTargetHalted) - { - } - - public override void OnStateTransition(ActorId id, string stateName, bool isEntry) - { - this.Logger.WriteLine("."); - } - - public override void OnMonitorRaiseEvent(string monitorType, string stateName, Event e) - { - } - } -} diff --git a/Tests/Tests.Common/TestOutputLogger.cs b/Tests/Tests.Common/TestOutputLogger.cs index c38036678..a52adc9ef 100644 --- a/Tests/Tests.Common/TestOutputLogger.cs +++ b/Tests/Tests.Common/TestOutputLogger.cs @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; -using System.IO; using System.Text; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Xunit.Abstractions; namespace Microsoft.Coyote.Tests.Common @@ -12,7 +10,7 @@ namespace Microsoft.Coyote.Tests.Common /// /// Logger that writes to the test output. /// - public sealed class TestOutputLogger : TextWriter, ILogger + public sealed class TestOutputLogger : ILogger { /// /// Underlying test output. @@ -25,16 +23,10 @@ public sealed class TestOutputLogger : TextWriter, ILogger private readonly StringBuilder Log; /// - /// Serializes access to the string writer. + /// Synchronizes access to the string writer. /// private readonly object Lock; - /// - public TextWriter TextWriter => this; - - /// - public override Encoding Encoding => Encoding.Unicode; - /// /// True if this logger is disposed, else false. /// @@ -53,12 +45,23 @@ public TestOutputLogger(ITestOutputHelper output) } /// - public override void Write(string value) => - this.Write(LogSeverity.Informational, value); + public void Write(string value) => this.Write(LogSeverity.Info, value); + + /// + public void Write(string format, object arg0) => + this.Write(LogSeverity.Info, format, arg0); + + /// + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); /// - public override void Write(string format, params object[] args) => - this.Write(LogSeverity.Informational, string.Format(format, args)); + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, format, args); public void Write(LogSeverity severity, string value) { @@ -71,16 +74,40 @@ public void Write(LogSeverity severity, string value) } } + /// + public void Write(LogSeverity severity, string format, object arg0) => + this.Write(severity, string.Format(format, arg0)); + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1) => + this.Write(severity, string.Format(format, arg0, arg1)); + + /// + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) => + this.Write(severity, string.Format(format, arg0, arg1, arg2)); + /// public void Write(LogSeverity severity, string format, params object[] args) => this.Write(severity, string.Format(format, args)); /// - public override void WriteLine(string value) => this.WriteLine(LogSeverity.Informational, value); + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); /// - public override void WriteLine(string format, params object[] args) => - this.WriteLine(LogSeverity.Informational, string.Format(format, args)); + public void WriteLine(string format, object arg0) => + this.WriteLine(LogSeverity.Info, format, arg0); + + /// + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + + /// + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + + /// + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, format, args); /// public void WriteLine(LogSeverity severity, string value) @@ -94,6 +121,18 @@ public void WriteLine(LogSeverity severity, string value) } } + /// + public void WriteLine(LogSeverity severity, string format, object arg0) => + this.WriteLine(severity, string.Format(format, arg0)); + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) => + this.WriteLine(severity, string.Format(format, arg0, arg1)); + + /// + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) => + this.WriteLine(severity, string.Format(format, arg0, arg1, arg2)); + /// public void WriteLine(LogSeverity severity, string format, params object[] args) => this.WriteLine(severity, string.Format(format, args)); @@ -110,8 +149,10 @@ private void FlushLog() } } - /// - protected override void Dispose(bool disposing) + /// + /// Releases any resources held by the logger. + /// + public void Dispose() { lock (this.Lock) { @@ -121,8 +162,6 @@ protected override void Dispose(bool disposing) this.IsDisposed = true; } } - - base.Dispose(disposing); } } } diff --git a/Tests/Tests.Common/Text/ConsoleOutputInterceptor.cs b/Tests/Tests.Common/Text/ConsoleOutputInterceptor.cs new file mode 100644 index 000000000..5e5d450d6 --- /dev/null +++ b/Tests/Tests.Common/Text/ConsoleOutputInterceptor.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; + +namespace Microsoft.Coyote.Tests.Common +{ + /// + /// Intercepts all console output. + /// + internal struct ConsoleOutputInterceptor : IDisposable + { + /// + /// The original console output writer. + /// + private readonly TextWriter ConsoleOutput; + + /// + /// Used to intercepts console output. + /// + private readonly StreamWriter Writer; + + /// + /// Initializes a new instance of the struct. + /// + internal ConsoleOutputInterceptor(MemoryStream stream) + { + this.ConsoleOutput = Console.Out; + this.Writer = new StreamWriter(stream) + { + AutoFlush = true, + }; + + Console.SetOut(this.Writer); + } + + /// + /// Restores the original console color. + /// + public void Dispose() + { + Console.SetOut(this.ConsoleOutput); + } + } +} diff --git a/Tests/Tests.Common/Text/StringExtensions.cs b/Tests/Tests.Common/Text/StringExtensions.cs index cc4344967..96d853f68 100644 --- a/Tests/Tests.Common/Text/StringExtensions.cs +++ b/Tests/Tests.Common/Text/StringExtensions.cs @@ -13,17 +13,19 @@ namespace Microsoft.Coyote.Tests.Common /// public static class StringExtensions { + public static string FormatNewLine(this string text) => text + "\n"; + public static string FormatLines(params string[] args) => string.Join("\n", args) + "\n"; + public static string SortLines(this string text) { var list = new List(text.Split('\n')); list.Sort(StringComparer.Ordinal); + list.RemoveAll(string.IsNullOrEmpty); return string.Join("\n", list); } - public static string RemoveInstanceIds(this string actual) => Regex.Replace(actual, @"\([^)]*\)", "()"); - + public static string RemoveInstanceIds(this string text) => Regex.Replace(text, @"\([^)]*\)", "()"); public static string RemoveExcessiveEmptySpace(this string text) => Regex.Replace(text, @"\s+", " "); - public static string NormalizeNewLines(this string text) => Regex.Replace(text, "[\r\n]+", "\n"); public static string RemoveNonDeterministicValues(this string text) diff --git a/Tests/Tests.Runtime/Logging/ConsoleLoggerTests.cs b/Tests/Tests.Runtime/Logging/ConsoleLoggerTests.cs new file mode 100644 index 000000000..c8a2a17d1 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/ConsoleLoggerTests.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class ConsoleLoggerTests : BaseRuntimeTest + { + public ConsoleLoggerTests(ITestOutputHelper output) + : base(output) + { + } + + private string WriteAllSeverityMessages(VerbosityLevel level) + { + using var stream = new MemoryStream(); + using var logger = new ConsoleLogger(level); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + logger.WriteLine(LogSeverity.Debug, VerbosityMessages.DebugMessage); + logger.WriteLine(LogSeverity.Info, VerbosityMessages.InfoMessage); + logger.WriteLine(LogSeverity.Warning, VerbosityMessages.WarningMessage); + logger.WriteLine(LogSeverity.Error, VerbosityMessages.ErrorMessage); + logger.WriteLine(LogSeverity.Important, VerbosityMessages.ImportantMessage); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + this.TestOutput.WriteLine($"Result (length: {result.Length}):"); + this.TestOutput.WriteLine(result); + return result; + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerNoneVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.None); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerErrorVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Error); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerWarningVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Warning); + string expected = StringExtensions.FormatLines( + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerInfoVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Info); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerDebugVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Debug); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestConsoleLoggerExhaustiveVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Exhaustive); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/LogWriterTests.cs b/Tests/Tests.Runtime/Logging/LogWriterTests.cs new file mode 100644 index 000000000..6ce5287f9 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/LogWriterTests.cs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class LogWriterTests : BaseRuntimeTest + { + public LogWriterTests(ITestOutputHelper output) + : base(output) + { + } + + private string WriteAllSeverityMessages(VerbosityLevel level) + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(level); + using var logger = new MemoryLogger(config.VerbosityLevel); + using var logWriter = new LogWriter(config); + logWriter.SetLogger(logger); + Assert.IsType(logWriter.Logger); + + logWriter.LogDebug(VerbosityMessages.DebugMessage); + logWriter.LogInfo(VerbosityMessages.InfoMessage); + logWriter.LogWarning(VerbosityMessages.WarningMessage); + logWriter.LogError(VerbosityMessages.ErrorMessage); + logWriter.LogImportant(VerbosityMessages.ImportantMessage); + + string result = logger.ToString().NormalizeNewLines(); + this.TestOutput.WriteLine($"Result (length: {result.Length}):"); + this.TestOutput.WriteLine(result); + return result; + } + + [Fact(Timeout = 5000)] + public void TestLogWriterNoneVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.None); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterErrorVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Error); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterWarningVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Warning); + string expected = StringExtensions.FormatLines( + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterInfoVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Info); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterDebugVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Debug); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterExhaustiveVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Exhaustive); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + private string WriteAllSeverityMessages(LogWriter logWriter) + { + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + logWriter.LogDebug(VerbosityMessages.DebugMessage); + logWriter.LogInfo(VerbosityMessages.InfoMessage); + logWriter.LogWarning(VerbosityMessages.WarningMessage); + logWriter.LogError(VerbosityMessages.ErrorMessage); + logWriter.LogImportant(VerbosityMessages.ImportantMessage); + } + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + this.TestOutput.WriteLine($"Result (length: {result.Length}):"); + this.TestOutput.WriteLine(result); + return result; + } + + [Fact(Timeout = 5000)] + public void TestLogWriterConsoleOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled().WithConsoleLoggingEnabled(); + using var logWriter = new LogWriter(config); + Assert.IsType(logWriter.Logger); + string result = this.WriteAllSeverityMessages(logWriter); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterForceConsoleOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled().WithConsoleLoggingEnabled(false); + using var logWriter = new LogWriter(config, true); + Assert.IsType(logWriter.Logger); + string result = this.WriteAllSeverityMessages(logWriter); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterNullOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(); + using var logWriter = new LogWriter(config); + Assert.IsType(logWriter.Logger); + string result = this.WriteAllSeverityMessages(logWriter); + Assert.Equal(string.Empty, result); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/MemoryLogWriterTests.cs b/Tests/Tests.Runtime/Logging/MemoryLogWriterTests.cs new file mode 100644 index 000000000..5938c2879 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/MemoryLogWriterTests.cs @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class MemoryLogWriterTests : BaseRuntimeTest + { + public MemoryLogWriterTests(ITestOutputHelper output) + : base(output) + { + } + + private (string, string) WriteAllSeverityMessages(VerbosityLevel level) + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(level); + using var logger = new MemoryLogger(config.VerbosityLevel); + using var logWriter = new MemoryLogWriter(config); + logWriter.SetLogger(logger); + Assert.IsType(logWriter.Logger); + + logWriter.LogDebug(VerbosityMessages.DebugMessage); + logWriter.LogInfo(VerbosityMessages.InfoMessage); + logWriter.LogWarning(VerbosityMessages.WarningMessage); + logWriter.LogError(VerbosityMessages.ErrorMessage); + logWriter.LogImportant(VerbosityMessages.ImportantMessage); + + string logged = logger.ToString().NormalizeNewLines(); + string observed = logWriter.GetObservedMessages().NormalizeNewLines(); + this.TestOutput.WriteLine($"Logged (length: {logged.Length}):"); + this.TestOutput.WriteLine(logged); + this.TestOutput.WriteLine($"Observed (length: {observed.Length}):"); + this.TestOutput.WriteLine(observed); + return (logged, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterNoneVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.None); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.ImportantMessage), logged); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterErrorVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.Error); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), logged); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterWarningVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.Warning); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), logged); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterInfoVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.Info); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, logged); + Assert.Equal(expected, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterDebugVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.Debug); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, logged); + Assert.Equal(expected, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterExhaustiveVerbosity() + { + (string logged, string observed) = this.WriteAllSeverityMessages(VerbosityLevel.Exhaustive); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, logged); + Assert.Equal(expected, observed); + } + + private (string, string) WriteAllSeverityMessages(MemoryLogWriter logWriter) + { + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + logWriter.LogDebug(VerbosityMessages.DebugMessage); + logWriter.LogInfo(VerbosityMessages.InfoMessage); + logWriter.LogWarning(VerbosityMessages.WarningMessage); + logWriter.LogError(VerbosityMessages.ErrorMessage); + logWriter.LogImportant(VerbosityMessages.ImportantMessage); + } + + string logged = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + string observed = logWriter.GetObservedMessages().NormalizeNewLines(); + this.TestOutput.WriteLine($"Logged (length: {logged.Length}):"); + this.TestOutput.WriteLine(logged); + this.TestOutput.WriteLine($"Observed (length: {observed.Length}):"); + this.TestOutput.WriteLine(observed); + return (logged, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterConsoleOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled().WithConsoleLoggingEnabled(); + using var logWriter = new MemoryLogWriter(config); + Assert.IsType(logWriter.Logger); + (string logged, string observed) = this.WriteAllSeverityMessages(logWriter); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, logged); + Assert.Equal(expected, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterForceConsoleOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled().WithConsoleLoggingEnabled(false); + using var logWriter = new MemoryLogWriter(config, true); + Assert.IsType(logWriter.Logger); + (string logged, string observed) = this.WriteAllSeverityMessages(logWriter); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, logged); + Assert.Equal(expected, observed); + } + + [Fact(Timeout = 5000)] + public void TestLogWriterNullOutput() + { + Configuration config = this.GetConfiguration().WithVerbosityEnabled(); + using var logWriter = new MemoryLogWriter(config); + Assert.IsType(logWriter.Logger); + (string logged, string observed) = this.WriteAllSeverityMessages(logWriter); + Assert.Equal(string.Empty, logged); + Assert.Equal(StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage), observed); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/MemoryLoggerTests.cs b/Tests/Tests.Runtime/Logging/MemoryLoggerTests.cs new file mode 100644 index 000000000..8abae8419 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/MemoryLoggerTests.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class MemoryLoggerTests : BaseRuntimeTest + { + public MemoryLoggerTests(ITestOutputHelper output) + : base(output) + { + } + + private string WriteAllSeverityMessages(VerbosityLevel level) + { + using var logger = new MemoryLogger(level); + logger.WriteLine(LogSeverity.Debug, VerbosityMessages.DebugMessage); + logger.WriteLine(LogSeverity.Info, VerbosityMessages.InfoMessage); + logger.WriteLine(LogSeverity.Warning, VerbosityMessages.WarningMessage); + logger.WriteLine(LogSeverity.Error, VerbosityMessages.ErrorMessage); + logger.WriteLine(LogSeverity.Important, VerbosityMessages.ImportantMessage); + + string result = logger.ToString().NormalizeNewLines(); + this.TestOutput.WriteLine($"Result (length: {result.Length}):"); + this.TestOutput.WriteLine(result); + return result; + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerNoneVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.None); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerErrorVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Error); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerWarningVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Warning); + string expected = StringExtensions.FormatLines( + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerInfoVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Info); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerDebugVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Debug); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestMemoryLoggerExhaustiveVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Exhaustive); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/TestingEngineLoggingTests.cs b/Tests/Tests.Runtime/Logging/TestingEngineLoggingTests.cs new file mode 100644 index 000000000..661e71cfe --- /dev/null +++ b/Tests/Tests.Runtime/Logging/TestingEngineLoggingTests.cs @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.SystematicTesting; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class TestingEngineLoggingTests : BaseRuntimeTest + { + public TestingEngineLoggingTests(ITestOutputHelper output) + : base(output) + { + } + + private static void WriteAllSeverityMessages(LogWriter logWriter) + { + logWriter.LogDebug(VerbosityMessages.DebugMessage); + logWriter.LogInfo(VerbosityMessages.InfoMessage); + logWriter.LogWarning(VerbosityMessages.WarningMessage); + logWriter.LogError(VerbosityMessages.ErrorMessage); + logWriter.LogImportant(VerbosityMessages.ImportantMessage); + } + + [Fact(Timeout = 5000)] + public void TestDefaultTestingEngineLogger() + { + string observed = string.Empty; + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + var config = this.GetConfiguration().WithTestingIterations(2).WithRandomGeneratorSeed(0) + .WithTestIterationsRunToCompletion().WithVerbosityEnabled(VerbosityLevel.Info); + using TestingEngine engine = TestingEngine.Create(config, () => + { + var runtime = CoyoteRuntime.Current; + Assert.IsType(runtime.LogWriter.Logger); + WriteAllSeverityMessages(runtime.LogWriter); + runtime.Assert(false); + }); + + engine.Run(); + + observed = engine.ReadableTrace.RemoveNonDeterministicValues().NormalizeNewLines().FormatNewLine(); + Assert.Equal(2, engine.TestReport.NumOfFoundBugs); + } + + string logged = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + string expectedLogged = StringExtensions.FormatLines( + $"... Assembly is not rewritten for testing, see {Documentation.LearnAboutRewritingUrl}.", + "... Setting up the test:", + "..... Using the random[seed:0] exploration strategy.", + "... Running test iterations:", + "..... Iteration #1", + "..... Iteration #1 found bug #1", + "Detected an assertion failure.", + "..... Iteration #2", + "..... Iteration #2 found bug #2", + "Detected an assertion failure."); + + logged = logged.RemoveNonDeterministicValues().RemoveDebugLines(); + expectedLogged = expectedLogged.RemoveNonDeterministicValues(); + this.TestOutput.WriteLine($"Logged (length: {logged.Length}):"); + this.TestOutput.WriteLine(logged); + Assert.Equal(expectedLogged, logged); + + string expectedObserved = StringExtensions.FormatLines( + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "[coyote::report] Testing statistics:", + "[coyote::report] Found 2 bugs.", + "[coyote::report] Scheduling statistics:", + "[coyote::report] Explored 2 schedules: 2 fair and 0 unfair.", + "[coyote::report] Found 100.00% buggy schedules.", + "[coyote::report] Controlled 2 operations: 1 (), 1 (), 1 ().", + "[coyote::report] Number of scheduling decisions in fair terminating schedules: 0 (), 0 (), 0 ()."); + this.TestOutput.WriteLine($"Observed (length: {observed.Length}):"); + this.TestOutput.WriteLine(observed); + Assert.Equal(expectedObserved, observed); + } + + [Fact(Timeout = 5000)] + public void TestConsoleTestingEngineLogger() + { + string observed = string.Empty; + using var stream = new MemoryStream(); + using (var interceptor = new ConsoleOutputInterceptor(stream)) + { + var config = this.GetConfiguration().WithTestingIterations(2).WithRandomGeneratorSeed(0) + .WithTestIterationsRunToCompletion().WithVerbosityEnabled(VerbosityLevel.Info) + .WithConsoleLoggingEnabled(); + using TestingEngine engine = TestingEngine.Create(config, () => + { + var runtime = CoyoteRuntime.Current; + Assert.IsType(runtime.LogWriter.Logger); + WriteAllSeverityMessages(runtime.LogWriter); + runtime.Assert(false); + }); + + engine.Run(); + + observed = engine.ReadableTrace.RemoveNonDeterministicValues().NormalizeNewLines().FormatNewLine(); + Assert.Equal(2, engine.TestReport.NumOfFoundBugs); + } + + string logged = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + string expectedLogged = StringExtensions.FormatLines( + $"... Assembly is not rewritten for testing, see {Documentation.LearnAboutRewritingUrl}.", + "... Setting up the test:", + "..... Using the random[seed:0] exploration strategy.", + "... Running test iterations:", + "..... Iteration #1", + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "..... Iteration #1 found bug #1", + "Detected an assertion failure.", + "..... Iteration #2", + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "..... Iteration #2 found bug #2", + "Detected an assertion failure."); + + logged = logged.RemoveNonDeterministicValues().RemoveDebugLines(); + expectedLogged = expectedLogged.RemoveNonDeterministicValues(); + this.TestOutput.WriteLine($"Logged (length: {logged.Length}):"); + this.TestOutput.WriteLine(logged); + Assert.Equal(expectedLogged, logged); + + string expectedObserved = StringExtensions.FormatLines( + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "[coyote::report] Testing statistics:", + "[coyote::report] Found 2 bugs.", + "[coyote::report] Scheduling statistics:", + "[coyote::report] Explored 2 schedules: 2 fair and 0 unfair.", + "[coyote::report] Found 100.00% buggy schedules.", + "[coyote::report] Controlled 2 operations: 1 (), 1 (), 1 ().", + "[coyote::report] Number of scheduling decisions in fair terminating schedules: 0 (), 0 (), 0 ()."); + this.TestOutput.WriteLine($"Observed (length: {observed.Length}):"); + this.TestOutput.WriteLine(observed); + Assert.Equal(expectedObserved, observed); + } + + [Fact(Timeout = 5000)] + public void TestCustomTestingEngineLogger() + { + var config = this.GetConfiguration().WithTestingIterations(2).WithRandomGeneratorSeed(0) + .WithTestIterationsRunToCompletion().WithVerbosityEnabled(VerbosityLevel.Info); + using TestingEngine engine = TestingEngine.Create(config, () => + { + var runtime = CoyoteRuntime.Current; + Assert.IsType(runtime.LogWriter.Logger); + WriteAllSeverityMessages(runtime.LogWriter); + runtime.Assert(false); + }); + + using var logger = new MemoryLogger(config.VerbosityLevel); + engine.SetLogger(logger); + engine.Run(); + + string observed = engine.ReadableTrace.RemoveNonDeterministicValues().NormalizeNewLines().FormatNewLine(); + Assert.Equal(2, engine.TestReport.NumOfFoundBugs); + + string logged = logger.ToString().NormalizeNewLines(); + string expectedLogged = StringExtensions.FormatLines( + $"... Assembly is not rewritten for testing, see {Documentation.LearnAboutRewritingUrl}.", + "... Setting up the test:", + "..... Using the random[seed:0] exploration strategy.", + "... Running test iterations:", + "..... Iteration #1", + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "..... Iteration #1 found bug #1", + "Detected an assertion failure.", + "..... Iteration #2", + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "..... Iteration #2 found bug #2", + "Detected an assertion failure."); + + logged = logged.RemoveNonDeterministicValues().RemoveDebugLines(); + expectedLogged = expectedLogged.RemoveNonDeterministicValues(); + this.TestOutput.WriteLine($"Logged (length: {logged.Length}):"); + this.TestOutput.WriteLine(logged); + Assert.Equal(expectedLogged, logged); + + string expectedObserved = StringExtensions.FormatLines( + "[coyote::test] Runtime '' started test on thread ''.", + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage, + "[coyote::error] Detected an assertion failure.", + "[coyote::test] Exploration finished in runtime '' [found a bug using the 'random' strategy].", + "[coyote::report] Testing statistics:", + "[coyote::report] Found 2 bugs.", + "[coyote::report] Scheduling statistics:", + "[coyote::report] Explored 2 schedules: 2 fair and 0 unfair.", + "[coyote::report] Found 100.00% buggy schedules.", + "[coyote::report] Controlled 2 operations: 1 (), 1 (), 1 ().", + "[coyote::report] Number of scheduling decisions in fair terminating schedules: 0 (), 0 (), 0 ()."); + this.TestOutput.WriteLine($"Observed (length: {observed.Length}):"); + this.TestOutput.WriteLine(observed); + Assert.Equal(expectedObserved, observed); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/TextWriterLoggerTests.cs b/Tests/Tests.Runtime/Logging/TextWriterLoggerTests.cs new file mode 100644 index 000000000..eec56baa6 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/TextWriterLoggerTests.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Microsoft.Coyote.Logging; +using Microsoft.Coyote.Tests.Common; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + public class TextWriterLoggerTests : BaseRuntimeTest + { + public TextWriterLoggerTests(ITestOutputHelper output) + : base(output) + { + } + + private string WriteAllSeverityMessages(VerbosityLevel level) + { + using var stream = new MemoryStream(); + using var writer = new StreamWriter(stream) + { + AutoFlush = true, + }; + + using var logger = new TextWriterLogger(writer, level); + logger.WriteLine(LogSeverity.Debug, VerbosityMessages.DebugMessage); + logger.WriteLine(LogSeverity.Info, VerbosityMessages.InfoMessage); + logger.WriteLine(LogSeverity.Warning, VerbosityMessages.WarningMessage); + logger.WriteLine(LogSeverity.Error, VerbosityMessages.ErrorMessage); + logger.WriteLine(LogSeverity.Important, VerbosityMessages.ImportantMessage); + + string result = Encoding.UTF8.GetString(stream.ToArray()).NormalizeNewLines(); + this.TestOutput.WriteLine($"Result (length: {result.Length}):"); + this.TestOutput.WriteLine(result); + return result; + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerNoneVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.None); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerErrorVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Error); + string expected = StringExtensions.FormatLines( + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerWarningVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Warning); + string expected = StringExtensions.FormatLines( + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerInfoVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Info); + string expected = StringExtensions.FormatLines( + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerDebugVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Debug); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + + [Fact(Timeout = 5000)] + public void TestTextWriterLoggerExhaustiveVerbosity() + { + string result = this.WriteAllSeverityMessages(VerbosityLevel.Exhaustive); + string expected = StringExtensions.FormatLines( + VerbosityMessages.DebugMessage, + VerbosityMessages.InfoMessage, + VerbosityMessages.WarningMessage, + VerbosityMessages.ErrorMessage, + VerbosityMessages.ImportantMessage); + Assert.Equal(expected, result); + } + } +} diff --git a/Tests/Tests.Runtime/Logging/VerbosityMessages.cs b/Tests/Tests.Runtime/Logging/VerbosityMessages.cs new file mode 100644 index 000000000..e7eb168a9 --- /dev/null +++ b/Tests/Tests.Runtime/Logging/VerbosityMessages.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Coyote.Runtime.Tests.Logging +{ + internal static class VerbosityMessages + { + internal const string DebugMessage = "Message #Debug#."; + internal const string InfoMessage = "Message #Info#."; + internal const string WarningMessage = "Message #Warning#."; + internal const string ErrorMessage = "Message #Error#."; + internal const string ImportantMessage = "Message #Important#."; + } +} diff --git a/Tests/compare-rewriting-diff-logs.ps1 b/Tests/compare-rewriting-diff-logs.ps1 index 8cc366932..0d85b1bc8 100644 --- a/Tests/compare-rewriting-diff-logs.ps1 +++ b/Tests/compare-rewriting-diff-logs.ps1 @@ -15,8 +15,8 @@ $targets = [ordered]@{ $expected_hashes = [ordered]@{ "rewriting" = "2BAA1A91DBD6BC406BE564C34DE3B7B03FE1A6251C91F781880D4688F99C13DF" "rewriting-helpers" = "CC82EC742BDC1B678A89EC952257290AF8D935FA0DB1E8C5BB591D9AB3E18C77" - "testing" = "3248D1D8654620C7C2AF8ED872C87F3758074EC6E6C0CE7ED7F35BE8393D5A21" - "actors" = "70A0615EA9A7D23F53C5D57C9CDF58BBDE1B36F6C1443A117329A8B9B22AD4E5" + "testing" = "37A8AE3E92E2A07AA7DB57C3BC3D5B3FAAAE4E6D3D06673AB2BB353D28509880" + "actors" = "E6E4AF71A6AD9EAC7F558F94E4A37F499EFC259B6A58D67BA16B0D0162525928" "actors-testing" = "9392BAE6C2814A614B106C15CDD99FBFA2C29A9025F23250C7D13084FEB7572E" } diff --git a/Tools/Coyote/Cli/CommandLineParser.cs b/Tools/Coyote/Cli/CommandLineParser.cs index fe73beceb..2f1f4089a 100644 --- a/Tools/Coyote/Cli/CommandLineParser.cs +++ b/Tools/Coyote/Cli/CommandLineParser.cs @@ -9,38 +9,13 @@ using System.CommandLine.Parsing; using System.IO; using System.Linq; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting; namespace Microsoft.Coyote.Cli { internal sealed class CommandLineParser { - /// - /// Url with information on learning about coyote. - /// - private const string LearnAboutCoyoteUrl = "https://aka.ms/learn-coyote"; - - /// - /// Url with information about what is new with coyote. - /// - private const string LearnWhatIsNewUrl = "https://aka.ms/coyote-what-is-new"; - - /// - /// Url with information about the testing process. - /// - private const string LearnAboutTestUrl = "https://aka.ms/coyote-test"; - - /// - /// Url with information about the replaying process. - /// - private const string LearnAboutReplayUrl = "https://aka.ms/coyote-replay"; - - /// - /// Url with information about the rewriting process. - /// - private const string LearnAboutRewritingUrl = "https://aka.ms/coyote-rewrite"; - /// /// The Coyote runtime and testing configuration. /// @@ -98,24 +73,27 @@ internal CommandLineParser(string[] args) var allowedVerbosityLevels = new HashSet { - "quiet", - "minimal", - "normal", - "detailed" + "error", + "warning", + "info", + "debug", + "exhaustive" }; var verbosityOption = new Option( aliases: new[] { "-v", "--verbosity" }, - getDefaultValue: () => "quiet", + getDefaultValue: () => "none", description: "Enable verbosity with an optional verbosity level. " + $"Allowed values are {string.Join(", ", allowedVerbosityLevels)}. " + - "Skipping the argument sets the verbosity level to 'detailed'.") + "Skipping the argument sets the verbosity level to 'info'.") { ArgumentHelpName = "LEVEL", Arity = ArgumentArity.ZeroOrOne }; - var debugOption = new Option(aliases: new[] { "-d", "--debug" }) + var consoleLoggingOption = new Option( + name: "--console", + description: "Log all runtime messages to the console unless overridden by a custom ILogger.") { Arity = ArgumentArity.Zero }; @@ -130,9 +108,10 @@ internal CommandLineParser(string[] args) // Create the root command. var rootCommand = new RootCommand("The Coyote systematic testing tool.\n\n" + - $"Learn how to use Coyote at {LearnAboutCoyoteUrl}.\nLearn what is new at {LearnWhatIsNewUrl}."); + $"Learn how to use Coyote at {Documentation.LearnAboutCoyoteUrl}.\nLearn what is new at {Documentation.LearnWhatIsNewUrl}."); this.AddGlobalOption(rootCommand, verbosityOption); - this.AddGlobalOption(rootCommand, debugOption); + this.TestCommand.AddGlobalOption(consoleLoggingOption); + this.ReplayCommand.AddGlobalOption(consoleLoggingOption); rootCommand.AddCommand(this.TestCommand); rootCommand.AddCommand(this.ReplayCommand); rootCommand.AddCommand(this.RewriteCommand); @@ -188,7 +167,8 @@ private Command CreateTestCommand(Configuration configuration) aliases: new[] { "-m", "--method" }, description: "Suffix of the test method to execute.") { - ArgumentHelpName = "METHOD" + ArgumentHelpName = "METHOD", + Arity = ArgumentArity.ExactlyOne }; var iterationsOption = new Option( @@ -196,7 +176,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => (int)configuration.TestingIterations, description: "Number of testing iterations to run.") { - ArgumentHelpName = "ITERATIONS" + ArgumentHelpName = "ITERATIONS", + Arity = ArgumentArity.ExactlyOne }; var timeoutOption = new Option( @@ -204,7 +185,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => configuration.TestingTimeout, description: "Timeout in seconds after which no more testing iterations will run (disabled by default).") { - ArgumentHelpName = "TIMEOUT" + ArgumentHelpName = "TIMEOUT", + Arity = ArgumentArity.ExactlyOne }; var allowedStrategies = new HashSet @@ -224,7 +206,8 @@ private Command CreateTestCommand(Configuration configuration) "controls all scheduling decisions and nondeterministic choices. " + $"Allowed values are {string.Join(", ", allowedStrategies)}.") { - ArgumentHelpName = "STRATEGY" + ArgumentHelpName = "STRATEGY", + Arity = ArgumentArity.ExactlyOne }; var strategyValueOption = new Option( @@ -233,7 +216,8 @@ private Command CreateTestCommand(Configuration configuration) "(fair-)prioritization (maximum number of priority change points per iteration), " + "probabilistic (probability of deviating from a scheduled operation).") { - ArgumentHelpName = "VALUE" + ArgumentHelpName = "VALUE", + Arity = ArgumentArity.ExactlyOne }; var maxStepsOption = new Option( @@ -241,7 +225,8 @@ private Command CreateTestCommand(Configuration configuration) description: "Max scheduling steps (i.e. decisions) to be explored during testing. " + "Choosing value 'STEPS' sets 'STEPS' unfair max-steps and 'STEPS*10' fair steps.") { - ArgumentHelpName = "STEPS" + ArgumentHelpName = "STEPS", + Arity = ArgumentArity.ExactlyOne }; var maxFairStepsOption = new Option( @@ -250,7 +235,8 @@ private Command CreateTestCommand(Configuration configuration) description: "Max fair scheduling steps (i.e. decisions) to be explored during testing. " + "Used by exploration strategies that perform fair scheduling.") { - ArgumentHelpName = "STEPS" + ArgumentHelpName = "STEPS", + Arity = ArgumentArity.ExactlyOne }; var maxUnfairStepsOption = new Option( @@ -259,7 +245,8 @@ private Command CreateTestCommand(Configuration configuration) description: "Max unfair scheduling steps (i.e. decisions) to be explored during testing. " + "Used by exploration strategies that perform unfair scheduling.") { - ArgumentHelpName = "STEPS" + ArgumentHelpName = "STEPS", + Arity = ArgumentArity.ExactlyOne }; var fuzzOption = new Option( @@ -308,7 +295,8 @@ private Command CreateTestCommand(Configuration configuration) name: "--seed", description: "Specify the random value generator seed.") { - ArgumentHelpName = "VALUE" + ArgumentHelpName = "VALUE", + Arity = ArgumentArity.ExactlyOne }; var livenessTemperatureThresholdOption = new Option( @@ -316,7 +304,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => configuration.LivenessTemperatureThreshold, description: "Specify the threshold (in number of steps) that triggers a liveness bug.") { - ArgumentHelpName = "THRESHOLD" + ArgumentHelpName = "THRESHOLD", + Arity = ArgumentArity.ExactlyOne }; var timeoutDelayOption = new Option( @@ -324,7 +313,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => (int)configuration.TimeoutDelay, description: "Controls the frequency of timeouts (not a unit of time).") { - ArgumentHelpName = "DELAY" + ArgumentHelpName = "DELAY", + Arity = ArgumentArity.ExactlyOne }; var deadlockTimeoutOption = new Option( @@ -332,7 +322,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => (int)configuration.DeadlockTimeout, description: "Controls how much time (in ms) to wait before reporting a potential deadlock.") { - ArgumentHelpName = "TIMEOUT" + ArgumentHelpName = "TIMEOUT", + Arity = ArgumentArity.ExactlyOne }; var maxFuzzDelayOption = new Option( @@ -341,7 +332,8 @@ private Command CreateTestCommand(Configuration configuration) description: "Controls the maximum time (in number of busy loops) an operation might " + "get delayed during systematic fuzzing.") { - ArgumentHelpName = "DELAY" + ArgumentHelpName = "DELAY", + Arity = ArgumentArity.ExactlyOne }; var uncontrolledConcurrencyResolutionAttemptsOption = new Option( @@ -349,7 +341,8 @@ private Command CreateTestCommand(Configuration configuration) getDefaultValue: () => (int)configuration.UncontrolledConcurrencyResolutionAttempts, description: "Controls how many times to try resolve each instance of uncontrolled concurrency.") { - ArgumentHelpName = "ATTEMPTS" + ArgumentHelpName = "ATTEMPTS", + Arity = ArgumentArity.ExactlyOne }; var uncontrolledConcurrencyResolutionDelayOption = new Option( @@ -358,7 +351,8 @@ private Command CreateTestCommand(Configuration configuration) description: "Controls how much time (in number of busy loops) to wait between each attempt to " + "resolve each instance of uncontrolled concurrency.") { - ArgumentHelpName = "DELAY" + ArgumentHelpName = "DELAY", + Arity = ArgumentArity.ExactlyOne }; var skipPotentialDeadlocksOption = new Option( @@ -416,7 +410,8 @@ private Command CreateTestCommand(Configuration configuration) aliases: new[] { "-o", "--outdir" }, description: "Output directory for emitting reports. This can be an absolute path or relative to current directory.") { - ArgumentHelpName = "PATH" + ArgumentHelpName = "PATH", + Arity = ArgumentArity.ExactlyOne }; // Add validators. @@ -442,7 +437,7 @@ private Command CreateTestCommand(Configuration configuration) // Build command. var command = new Command("test", "Run tests using the Coyote systematic testing engine.\n" + - $"Learn more at {LearnAboutTestUrl}."); + $"Learn more at {Documentation.LearnAboutTestUrl}."); this.AddArgument(command, pathArg); this.AddOption(command, methodOption); this.AddOption(command, iterationsOption); @@ -496,7 +491,8 @@ private Command CreateReplayCommand() aliases: new[] { "-m", "--method" }, description: "Suffix of the test method to execute.") { - ArgumentHelpName = "METHOD" + ArgumentHelpName = "METHOD", + Arity = ArgumentArity.ExactlyOne }; var breakOption = new Option( @@ -510,7 +506,8 @@ private Command CreateReplayCommand() aliases: new[] { "-o", "--outdir" }, description: "Output directory for emitting reports. This can be an absolute path or relative to current directory.") { - ArgumentHelpName = "PATH" + ArgumentHelpName = "PATH", + Arity = ArgumentArity.ExactlyOne }; // Add validators. @@ -519,7 +516,7 @@ private Command CreateReplayCommand() // Build command. var command = new Command("replay", "Replay bugs that Coyote discovered during systematic testing.\n" + - $"Learn more at {LearnAboutReplayUrl}."); + $"Learn more at {Documentation.LearnAboutReplayUrl}."); this.AddArgument(command, pathArg); this.AddArgument(command, traceFileArg); this.AddOption(command, methodOption); @@ -597,7 +594,7 @@ private Command CreateRewriteCommand() // Build command. var command = new Command("rewrite", "Rewrite your assemblies to inject logic that allows " + "Coyote to take control of the execution during systematic testing.\n" + - $"Learn more at {LearnAboutRewritingUrl}."); + $"Learn more at {Documentation.LearnAboutRewritingUrl}."); this.AddArgument(command, pathArg); this.AddOption(command, assertDataRacesOption); this.AddOption(command, rewriteDependenciesOption); @@ -929,10 +926,6 @@ private void UpdateConfigurationsWithParsedOption(OptionResult result) case "outdir": this.Configuration.OutputFilePath = result.GetValueOrDefault(); break; - case "debug": - this.Configuration.IsDebugVerbosityEnabled = true; - Debug.IsEnabled = true; - break; case "assert-data-races": this.RewritingOptions.IsDataRaceCheckingEnabled = true; break; @@ -954,24 +947,27 @@ private void UpdateConfigurationsWithParsedOption(OptionResult result) case "verbosity": switch (result.GetValueOrDefault()) { - case "quiet": - this.Configuration.IsVerbose = false; + case "error": + this.Configuration.WithVerbosityEnabled(VerbosityLevel.Error); break; - case "minimal": - this.Configuration.LogLevel = LogSeverity.Error; - this.Configuration.IsVerbose = true; + case "warning": + this.Configuration.WithVerbosityEnabled(VerbosityLevel.Warning); break; - case "normal": - this.Configuration.LogLevel = LogSeverity.Warning; - this.Configuration.IsVerbose = true; + case "debug": + this.Configuration.WithVerbosityEnabled(VerbosityLevel.Debug); break; - case "detailed": + case "exhaustive": + this.Configuration.WithVerbosityEnabled(VerbosityLevel.Exhaustive); + break; + case "info": default: - this.Configuration.LogLevel = LogSeverity.Informational; - this.Configuration.IsVerbose = true; + this.Configuration.WithVerbosityEnabled(VerbosityLevel.Info); break; } + break; + case "console": + this.Configuration.WithConsoleLoggingEnabled(true); break; case "help": break; diff --git a/Tools/Coyote/Program.cs b/Tools/Coyote/Program.cs index 923ce1af6..7d44e2ea2 100644 --- a/Tools/Coyote/Program.cs +++ b/Tools/Coyote/Program.cs @@ -3,10 +3,9 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using Microsoft.Coyote.Cli; -using Microsoft.Coyote.IO; +using Microsoft.Coyote.Logging; using Microsoft.Coyote.Rewriting; using Microsoft.Coyote.SystematicTesting; @@ -17,19 +16,8 @@ namespace Microsoft.Coyote /// internal class Program { - private static TextWriter StdOut; - private static TextWriter StdError; - - private static readonly object ConsoleLock = new object(); - private static int Main(string[] args) { - // Save these so we can force output to happen even if they have been re-routed. - StdOut = Console.Out; - StdError = Console.Error; - - AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; - var parser = new CommandLineParser(args); if (!parser.IsSuccessful) { @@ -44,51 +32,56 @@ private static int Main(string[] args) /// private static ExitCode RunTest(Configuration configuration) { + using var logWriter = new LogWriter(configuration, true); try { - Console.WriteLine($". Testing {configuration.AssemblyToBeAnalyzed}."); - using TestingEngine engine = TestingEngine.Create(configuration); + // Load the configuration of the assembly to be tested. + LoadAssemblyConfiguration(configuration.AssemblyToBeAnalyzed, logWriter); + + logWriter.LogImportant(". Testing {0}.", configuration.AssemblyToBeAnalyzed); + using TestingEngine engine = new TestingEngine(configuration, logWriter); + AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; engine.Run(); string directory = OutputFileManager.CreateOutputDirectory(configuration); string fileName = OutputFileManager.GetResolvedFileName(configuration.AssemblyToBeAnalyzed, directory); // Emit the test reports. - Console.WriteLine($"... Emitting trace-related reports:"); + logWriter.LogImportant("... Emitting trace-related reports:"); if (engine.TryEmitReports(directory, fileName, out IEnumerable reportPaths)) { foreach (var path in reportPaths) { - Console.WriteLine($"..... Writing {path}"); + logWriter.LogImportant("..... Writing {0}", path); } } else { - Console.WriteLine($"..... No test reports available."); + logWriter.LogImportant("..... No test reports available."); } // Emit the coverage reports. - Console.WriteLine($"... Emitting coverage reports:"); + logWriter.LogImportant("... Emitting coverage reports:"); if (engine.TryEmitCoverageReports(directory, fileName, out reportPaths)) { foreach (var path in reportPaths) { - Console.WriteLine($"..... Writing {path}"); + logWriter.LogImportant("..... Writing {0}", path); } } else { - Console.WriteLine($"..... No coverage reports available."); + logWriter.LogImportant("..... No coverage reports available."); } - Console.WriteLine(engine.TestReport.GetText(configuration, "...")); - Console.WriteLine($"... Elapsed {engine.Profiler.Results()} sec."); + logWriter.LogImportant(engine.TestReport.GetText(configuration, "...")); + logWriter.LogImportant("... Elapsed {0} sec.", engine.Profiler.Results()); return GetExitCodeFromTestReport(engine.TestReport); } catch (Exception ex) { - IO.Debug.WriteLine(ex.Message); - IO.Debug.WriteLine(ex.StackTrace); + logWriter.LogError(ex.Message); + logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } } @@ -98,28 +91,30 @@ private static ExitCode RunTest(Configuration configuration) /// private static ExitCode ReplayTest(Configuration configuration) { + using var logWriter = new LogWriter(configuration, true); try { // Load the configuration of the assembly to be replayed. - LoadAssemblyConfiguration(configuration.AssemblyToBeAnalyzed); + LoadAssemblyConfiguration(configuration.AssemblyToBeAnalyzed, logWriter); - Console.WriteLine($". Reproducing trace in {configuration.AssemblyToBeAnalyzed}."); - using TestingEngine engine = TestingEngine.Create(configuration); + logWriter.LogImportant(". Reproducing trace in {0}.", configuration.AssemblyToBeAnalyzed); + using TestingEngine engine = new TestingEngine(configuration, logWriter); + AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; engine.Run(); // Emit the report. if (engine.TestReport.NumOfFoundBugs > 0) { - Console.WriteLine(engine.GetReport()); + logWriter.LogImportant(engine.GetReport()); } - Console.WriteLine($"... Elapsed {engine.Profiler.Results()} sec."); + logWriter.LogImportant("... Elapsed {0} sec.", engine.Profiler.Results()); return GetExitCodeFromTestReport(engine.TestReport); } catch (Exception ex) { - IO.Debug.WriteLine(ex.Message); - IO.Debug.WriteLine(ex.StackTrace); + logWriter.LogError(ex.Message); + logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } } @@ -129,20 +124,21 @@ private static ExitCode ReplayTest(Configuration configuration) /// private static ExitCode RewriteAssemblies(Configuration configuration, RewritingOptions options) { + using var logWriter = new LogWriter(configuration, true); try { if (options.AssemblyPaths.Count is 1) { - Console.WriteLine($". Rewriting {options.AssemblyPaths.First()}."); + logWriter.LogImportant(". Rewriting {0}.", options.AssemblyPaths.First()); } else { - Console.WriteLine($". Rewriting the assemblies specified in {options.AssembliesDirectory}."); + logWriter.LogImportant(". Rewriting the assemblies specified in {0}.", options.AssembliesDirectory); } var profiler = new Profiler(); - RewritingEngine.Run(options, configuration, profiler); - Console.WriteLine($"... Elapsed {profiler.Results()} sec."); + RewritingEngine.Run(options, configuration, logWriter, profiler); + logWriter.LogImportant("... Elapsed {0} sec.", profiler.Results()); } catch (Exception ex) { @@ -151,7 +147,8 @@ private static ExitCode RewriteAssemblies(Configuration configuration, Rewriting ex = aex.Flatten().InnerException; } - Error.Report(configuration.IsDebugVerbosityEnabled ? ex.ToString() : ex.Message); + logWriter.LogError(ex.Message); + logWriter.LogDebug(ex.StackTrace); return ExitCode.Error; } @@ -161,7 +158,7 @@ private static ExitCode RewriteAssemblies(Configuration configuration, Rewriting /// /// Loads the configuration of the specified assembly. /// - private static void LoadAssemblyConfiguration(string assemblyFile) + private static void LoadAssemblyConfiguration(string assemblyFile, LogWriter logWriter) { // Load config file and absorb its settings. try @@ -182,43 +179,20 @@ private static void LoadAssemblyConfiguration(string assemblyFile) } catch (System.Configuration.ConfigurationErrorsException ex) { - Error.Report(ex.Message); + logWriter.LogError(ex.Message); + logWriter.LogDebug(ex.StackTrace); } } /// /// Callback invoked when an unhandled exception occurs. /// - private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args) - { - ReportUnhandledException((Exception)args.ExceptionObject); + private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs args) => Environment.Exit((int)ExitCode.InternalError); - } - - private static void ReportUnhandledException(Exception ex) - { - Console.SetOut(StdOut); - Console.SetError(StdError); - - PrintException(ex); - for (var inner = ex.InnerException; inner != null; inner = inner.InnerException) - { - PrintException(inner); - } - } private static ExitCode GetExitCodeFromTestReport(TestReport report) => report.InternalErrors.Count > 0 ? ExitCode.InternalError : report.NumOfFoundBugs > 0 ? ExitCode.BugFound : ExitCode.Success; - - private static void PrintException(Exception ex) - { - lock (ConsoleLock) - { - Error.Report($"[CoyoteTester] unhandled exception: {ex}"); - StdOut.WriteLine(ex.StackTrace); - } - } } } diff --git a/docs/concepts/actors/logging.md b/docs/concepts/actors/logging.md index 5419e17d2..ea058a278 100644 --- a/docs/concepts/actors/logging.md +++ b/docs/concepts/actors/logging.md @@ -1,100 +1,200 @@ ## Logging -The Coyote runtime provides a `Microsoft.Coyote.IO.ILogger` interface for logging so that your program output can be -captured and included in `coyote` test tool output logs. A default `Logger` is provided and can be -accessed like this: +The Coyote runtime provides a `Microsoft.Coyote.Logging.ILogger` interface for logging so that your +program output can be captured and included in `coyote` test tool output logs. The installed +`ILogger` can be accessed by the `Logger` property on the `IActorRuntime` interface or the `Actor`, +`StateMachine` and `Monitor` types. -| Programming Model | Accessing the Logger | -| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | -| `Task` based program | `Microsoft.Coyote.Actors.RuntimeFactory.Create().Logger` | -| `Actor` based program | `Microsoft.Coyote.Runtime.RuntimeFactory.Create().Logger`
`Actor.Logger`, `StateMachine.Logger`, `Monitor.Logger` | +The default implementation of the `ILogger` writes to the console when setting the `--console` +option in the `coyote` tool (or the `Configuration.WithConsoleLoggingEnabled()` configuration when +using the `TestingEngine` API). -The default `Logger` is a `ConsoleLogger` which is used to write output to the `System.Console`. -You can provide your own implementation of `Microsoft.Coyote.IO.ILogger` by setting the `Logger` property on the -`ICoyoteRuntime` or `IActorRuntime`. +The Coyote logging infrastructure decides when to log messages using the specified `VerbosityLevel` +and the individual `LogSeverity` of messages getting logged with the `ILogger.Write` and +`ILogger.WriteLine` methods (by default the `LogSeverity` of messages is set to `LogSeverity.Info`). +As long as the `LogSeverity` is equal or higher than the `VerbosityLevel` then the message will be +logged. By default, the `VerbosityLevel` is set to `None`, which means that no messages are logged, +but this can be easily customized using the `--verbosity` (or `-v`) option in the `coyote` tool (or +the `Configuration.WithVerbosityEnabled()` configuration when using the `TestingEngine` API) -The `IActorRuntime` also provides a higher level logging interface called [`IActorRuntimeLog`](#iactorruntimelog) for -logging `Actor` and `StateMachine` activity. +Setting `--verbosity` in the command line will set the `VerbosityLevel` to `VerbosityLevel.Info` +which logs all messages with `LogSeverity.Info` and higher. Other allowed verbosity values are +`error`, `warning`, `info`, `debug` and `exhaustive`. For example, choosing `--verbosity debug` will +log all messages with `LogSeverity.Debug` and higher. -## Example of custom ILogger +## Installing up a custom logger -It is possible to replace the default logger with a custom one. The following example captures all log output in a `StringBuilder`: +You can easily install your own logger by implementing the `ILogger` interface and replacing the +default logger by setting the `Logger` property on the `IActorRuntime`. + +The following is an example of a custom `ILogger` implementation that captures all log output in a +`StringBuilder`: ```csharp -public class CustomLogger : ILogger +using System.Text; +using Microsoft.Coyote.Logging; + +class CustomLogger : ILogger { - private StringBuilder StringBuilder; + private readonly StringBuilder Builder; + private readonly VerbosityLevel VerbosityLevel; + private readonly object Lock; + + public MemoryLogger(VerbosityLevel level) + { + this.Builder = new StringBuilder(); + this.VerbosityLevel = level; + this.Lock = new object(); + } - public TextWriter TextWriter => throw new NotImplementedException(); + public void Write(string value) => this.Write(LogSeverity.Info, value); + public void Write(string format, object arg0) => + this.Write(LogSeverity.Info, format, arg0); + public void Write(string format, object arg0, object arg1) => + this.Write(LogSeverity.Info, format, arg0, arg1); + public void Write(string format, object arg0, object arg1, object arg2) => + this.Write(LogSeverity.Info, format, arg0, arg1, arg2); + public void Write(string format, params object[] args) => + this.Write(LogSeverity.Info, string.Format(format, args)); - public CustomLogger() + public void Write(LogSeverity severity, string value) { - this.StringBuilder = new StringBuilder(); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.Append(value); + } + } } - public void Write(string value) + public void Write(LogSeverity severity, string format, object arg0) { - this.Write(LogSeverity.Informational, value); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + } + } } - public void WriteLine(string value) + public void Write(LogSeverity severity, string format, object arg0, object arg1) { - this.WriteLine(LogSeverity.Informational, value); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + } + } } - public void Write(string format, params object[] args) + public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) { - this.Write(LogSeverity.Informational, format, args); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + } + } } - public void WriteLine(string format, params object[] args) + public void Write(LogSeverity severity, string format, params object[] args) { - this.WriteLine(LogSeverity.Informational, format, args); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + } + } } - public void Write(LogSeverity severity, string format, object[] args) + public void WriteLine(string value) => this.WriteLine(LogSeverity.Info, value); + public void WriteLine(string format, object arg0) => + this.WriteLine(LogSeverity.Info, format, arg0); + public void WriteLine(string format, object arg0, object arg1) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1); + public void WriteLine(string format, object arg0, object arg1, object arg2) => + this.WriteLine(LogSeverity.Info, format, arg0, arg1, arg2); + public void WriteLine(string format, params object[] args) => + this.WriteLine(LogSeverity.Info, string.Format(format, args)); + + public void WriteLine(LogSeverity severity, string value) { - this.Write(severity, string.Format(format, args)); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendLine(value); + } + } } - public void WriteLine(LogSeverity severity, string format, object[] args) + public void WriteLine(LogSeverity severity, string format, object arg0) { - this.WriteLine(severity, string.Format(format, args)); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0); + this.Builder.AppendLine(); + } + } } - public void Write(LogSeverity severity, string value) + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) { - switch (severity) + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) { - case LogSeverity.Informational: - this.StringBuilder.Append("" + value); - break; - case LogSeverity.Warning: - this.StringBuilder.Append("" + value); - break; - case LogSeverity.Error: - this.StringBuilder.Append("" + value); - break; - case LogSeverity.Important: - this.StringBuilder.Append("" + value); - break; + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1); + this.Builder.AppendLine(); + } } } - public void WriteLine(LogSeverity severity, string value) + public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) { - this.Write(severity, value); - this.StringBuilder.AppendLine(); + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, arg0, arg1, arg2); + this.Builder.AppendLine(); + } + } + } + + public void WriteLine(LogSeverity severity, string format, params object[] args) + { + if (LogWriter.IsVerbose(severity, this.VerbosityLevel)) + { + lock (this.Lock) + { + this.Builder.AppendFormat(format, args); + this.Builder.AppendLine(); + } + } } public override string ToString() { - return this.StringBuilder.ToString(); + lock (this.Lock) + { + return this.Builder.ToString(); + } } public void Dispose() { - // todo + lock (this.Lock) + { + this.Builder.Clear(); + } } } ``` @@ -108,7 +208,7 @@ runtime.Logger = new CustomLogger(); The above method replaces the previously installed logger with the specified one and returns the previously installed logger. -Note that the old `Logger` might be disposable, so if you care about disposing the old logger at +Note that the old `ILogger` might be disposable, so if you care about disposing the old logger at the same time you may need to write this instead: ```csharp @@ -118,74 +218,75 @@ using (var oldLogger = runtime.Logger) } ``` -You could write a custom `ILogger` to intercept all logging messages and send them to an Azure Log -table, or over a TCP socket. - -## IActorRuntimeLog +You could write a custom `ILogger` to intercept all logging messages and send them to your favorite +logging service in Azure or even over a TCP socket. -The default `IActorRuntimeLog` implementation is the `ActorRuntimeLogTextFormatter` base class which -is responsible for formatting all `Actor` and `StateMachine` activity as text and writing that out -using the installed `Logger`. +You can also use one of the built-in loggers available in the `Microsoft.Coyote.Logging` namespace, +such as the `MemoryLogger` which is a thread-safe logger that writes the log in memory (you can +access it as a `string` by invoking `MemoryLogger.ToString()`) or the `TextWriterLogger` which +allows you to run an existing `System.IO.TextWriter` into an `ILogger` implementation. -You can add your own implementation of `IActorRuntimeLog` or `ActorRuntimeLogTextFormatter` using -the `RegisterLog` method on `IActorRuntime`. This is additive so you can have the default -`ActorRuntimeLogTextFormatter` and another logger running at the same time. For example, see the -`ActorRuntimeLogGraphBuilder` class which implements `IActorRuntimeLog` and generates a directed -graph representing all activities that happened during the execution of your actors. See [activity -coverage](../../how-to/coverage.md) for an example graph output. The `coyote` test tool -sets this up for you when you specify `--graph` or `--coverage activity` command line options. +## Adding custom actor logging consumers -The `--verbosity` command line option can also affect the default logging behavior. When -`--verbosity` is specified all log output is written to the `System.Console` by default, but you -can specify different levels of output using `--verbosity quiet` to get no output, `--verbosity -minimal` to see only error messages and `--verbosity normal` to get errors and warnings. This can -produce a lot of output especially if you run many testing iterations. It is usually more useful to -only capture the output of the one failing iteration in a log file and this is done automatically -by the testing runtime when `--verbosity` is not specified. +The `IActorRuntime` also provides a logging interface called `IActorRuntimeLog` that allows +consuming `Actor` and `StateMachine` activity and processing it in some custom way. When executing +actors, the runtime will call the `IActorRuntimeLog` interface to log various actions such as a new +`Actor` or `StateMachine` getting created or sending an `Event` to some actor. -See [IActorRuntimeLog API documentation](../../ref/Microsoft.Coyote.Actors/IActorRuntimeLog.md). - -## Example of a custom IActorRuntimeLog - -You can also implement your own `IActorRuntimeLog`. The following is an example of how to do this: +You can implement your own `IActorRuntimeLog` consumer like this: ```csharp -internal class CustomLogWriter : IActorRuntimeLog +internal class CustomRuntimeLog : IActorRuntimeLog { - // Callbacks on runtime events - public void OnCreateActor(ActorId id, ActorId creator) { - // Override to change the behavior. + // Add some custom logic. } public void OnEnqueueEvent(ActorId id, Event e) { - // Override to change the behavior. + // Add some custom logic. } - // More methods to implement. + // You can optionally override more actor logging methods. } ``` -You can then register your new implementation using the following `IActorRuntime` method: +You can then register the `CustomRuntimeLog` using the following `IActorRuntime` method: ```csharp -runtime.RegisterLog(new CustomLogWriter()); +runtime.RegisterLog(new CustomRuntimeLog()); ``` -You can register multiple `IActorRuntimeLog` objects in case you have loggers that are doing very + +You can register multiple `IActorRuntimeLog` objects in case you have consumers that are doing very different things. The runtime will invoke each callback for every registered `IActorRuntimeLog`. -## Customizing the ActorRuntimeLogTextFormatter +For example, see the `ActorRuntimeLogGraphBuilder` class which implements `IActorRuntimeLog` and +generates a directed graph representing all activities that happened during the execution of your +actors. See [activity coverage](../../how-to/coverage.md) for an example graph output. The `coyote` +test tool sets this up for you when you specify `--graph` or `--coverage activity` command line +options. + +See [IActorRuntimeLog API documentation](../../ref/Microsoft.Coyote.Actors/IActorRuntimeLog.md). + +## Customizing the text formatting when logging actor activities + +You can also use the same `IActorRuntimeLog` feature to customize the text formatting when the +installed `ILogger` logs actor activity. + +The default actor text formatting implementation is provided by the `ActorRuntimeLogTextFormatter` +base class which implements the `IActorRuntimeLog` interface and is responsible for formatting all +`Actor` and `StateMachine` activity as text and logging it using the installed `Logger`. + +You can add your own subclass of `ActorRuntimeLogTextFormatter` using the `RegisterLog` method on +`IActorRuntime`. However, unlike other `IActorRuntimeLog` consumers, only a single +`ActorRuntimeLogTextFormatter` can exist and adding a new one will replace the previous text +formatter. -You can modify the format of text log messages by providing your own `ActorRuntimeLogTextFormatter`. -You can subclass the default `ActorRuntimeLogTextFormatter` implementation to override its default behavior. The following is an example of how to do this: ```csharp -internal class CustomLogFormatter : ActorRuntimeLogTextFormatter +internal class CustomActorRuntimeLogTextFormatter : ActorRuntimeLogTextFormatter { - // Methods for formatting log messages - public override void OnCreateActor(ActorId id, ActorId creator) { // Override to change the text to be logged. @@ -201,7 +302,7 @@ internal class CustomLogFormatter : ActorRuntimeLogTextFormatter } } - // More methods that can be overridden. + // You can optionally override more text formatting methods. } ``` @@ -209,7 +310,7 @@ You can then replace the default `ActorRuntimeLogTextFormatter` with your new im the following `IActorRuntime` method: ```csharp -runtime.RegisterLog(new CustomLogFormatter()); +runtime.RegisterLog(new CustomActorRuntimeLogTextFormatter()); ``` The above method replaces the previously installed `ActorRuntimeLogTextFormatter` with the specified diff --git a/docs/concepts/binary-rewriting.md b/docs/concepts/binary-rewriting.md index 075a513c4..1ec2dd366 100644 --- a/docs/concepts/binary-rewriting.md +++ b/docs/concepts/binary-rewriting.md @@ -61,13 +61,13 @@ test DLLs, as well as your production code DLLs (which means the code that you a and to not rewrite any external dependencies (which you assume are correct after all). The reason behind this recommendation is that there are certain trade-offs when rewriting DLLs -because of two issues: (1) Coyote today does not support the universe of concurrency APIs in C# -(primarily focuses on the mainstream [task-asynchronous programming -model](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)), and (2) -state (schedule) space explosion. +because of two issues: Coyote today does not support every single concurrency API in C# (instead +mostly focuses on the popular [task-asynchronous programming +model](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)); and +dealing with the infamous state (schedule) space explosion problem. -Regarding (1), Coyote is focused on [asynchronous -task-based](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)) +Regarding the 1st issue, Coyote is focused on [asynchronous +task-based](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/) concurrency (basically common things like `Task` objects and `async`/`await`). So if an external library (or some "low-level" dependency DLL) is written with "lower-level" threading APIs (such as explicitly spawning threads and waiting on synchronization primitives such as a `WaitHandle`) or @@ -78,16 +78,16 @@ regressing exploration, or (b) be able to intercept them but the state (schedule will explode (more on this below). The good news is that using these "low-level" APIs is uncommon in _most_ user applications/services, but of course some frameworks/library dependencies do use them. -Regarding (2), the more concurrent code you instrument, the more scheduling decisions Coyote has to -explore in every test iteration. This exponentially increases how much time you need to test to -cover the same code surface of your application. This is known as state space explosion. Since -Coyote explores under a test "budget" (such as number of test iterations) the bigger the state space -to explore, the less efficient Coyote will be. Ideally, you just want to focus on testing your own -concurrent code, and not the code of 3rd party frameworks/libraries (which you assume is correct!). -For this reason, its recommended instead of rewriting every single dependency, to just rewrite DLLs -that you (and your team) owns. This basically means to focus rewriting the test DLL as well as your -production code DLLs, assuming these DLLs only use tasks, `async`/`await` and these kind of -"high-level" concurrency primitives. Think about this as "component-wise" testing. +Regarding the 2nd issue, the more concurrent code you instrument, the more scheduling decisions +Coyote must explore in every test iteration. This _exponentially_ increases how much time you need +to test to cover the same code surface of your application. This is known as state space explosion. +Since Coyote explores under a test "budget" (such as number of test iterations) the bigger the state +space to explore, the less efficient Coyote will be. Ideally, you just want to focus on testing your +own concurrent code, and not the code of 3rd party frameworks/libraries (which you assume is +correct!). For this reason, its recommended instead of rewriting every single dependency, to just +rewrite DLLs that you (and your team) owns. This basically means to focus rewriting the test DLL as +well as your production code DLLs, assuming these DLLs only use tasks, `async`/`await` and these +kind of "high-level" concurrency primitives. Think about this as "component-wise" testing. Under the hood, Coyote deals with both of the above problems using a feature called _partially-controlled exploration_. In this mode, which is enabled by default when testing a diff --git a/docs/ref/Microsoft.Coyote.Actors/Actor/Logger.md b/docs/ref/Microsoft.Coyote.Actors/Actor/Logger.md index 2e9e33d12..a97797955 100644 --- a/docs/ref/Microsoft.Coyote.Actors/Actor/Logger.md +++ b/docs/ref/Microsoft.Coyote.Actors/Actor/Logger.md @@ -12,7 +12,7 @@ See [Logging](/coyote/concepts/actors/logging) for more information. ## See Also -* interface [ILogger](../../Microsoft.Coyote.IO/ILogger.md) +* interface [ILogger](../../Microsoft.Coyote.Logging/ILogger.md) * class [Actor](../Actor.md) * namespace [Microsoft.Coyote.Actors](../Actor.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter.md b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter.md index bcacb3f0e..1600589d2 100644 --- a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter.md +++ b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter.md @@ -10,14 +10,14 @@ public class ActorRuntimeLogTextFormatter : RuntimeLogTextFormatter, IActorRunti | name | description | | --- | --- | -| [ActorRuntimeLogTextFormatter](ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md)() | Initializes a new instance of the [`ActorRuntimeLogTextFormatter`](./ActorRuntimeLogTextFormatter.md) class. | +| [ActorRuntimeLogTextFormatter](ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md)() | The default constructor. | | virtual [OnCreateActor](ActorRuntimeLogTextFormatter/OnCreateActor.md)(…) | Invoked when the specified actor has been created. | -| [OnCreateStateMachine](ActorRuntimeLogTextFormatter/OnCreateStateMachine.md)(…) | Invoked when the specified state machine has been created. | +| virtual [OnCreateStateMachine](ActorRuntimeLogTextFormatter/OnCreateStateMachine.md)(…) | Invoked when the specified state machine has been created. | | virtual [OnCreateTimer](ActorRuntimeLogTextFormatter/OnCreateTimer.md)(…) | Invoked when the specified actor timer has been created. | | virtual [OnDefaultEventHandler](ActorRuntimeLogTextFormatter/OnDefaultEventHandler.md)(…) | Invoked when the specified actor is idle (there is nothing to dequeue) and the default event handler is about to be executed. | | virtual [OnDequeueEvent](ActorRuntimeLogTextFormatter/OnDequeueEvent.md)(…) | Invoked when the specified event is dequeued by an actor. | | virtual [OnEnqueueEvent](ActorRuntimeLogTextFormatter/OnEnqueueEvent.md)(…) | Invoked when the specified event is about to be enqueued to an actor. | -| [OnEventHandlerTerminated](ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md)(…) | Invoked when the event handler of the specified actor terminated. | +| virtual [OnEventHandlerTerminated](ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md)(…) | Invoked when the event handler of the specified actor terminated. | | virtual [OnExceptionHandled](ActorRuntimeLogTextFormatter/OnExceptionHandled.md)(…) | Invoked when the specified actor has handled a thrown exception. | | virtual [OnExceptionThrown](ActorRuntimeLogTextFormatter/OnExceptionThrown.md)(…) | Invoked when the specified actor throws an exception without handling it. | | virtual [OnExecuteAction](ActorRuntimeLogTextFormatter/OnExecuteAction.md)(…) | Invoked when the specified actor executes an action. | diff --git a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md index 578d0e967..8b10f46cb 100644 --- a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md +++ b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/ActorRuntimeLogTextFormatter.md @@ -1,6 +1,6 @@ # ActorRuntimeLogTextFormatter constructor -Initializes a new instance of the [`ActorRuntimeLogTextFormatter`](../ActorRuntimeLogTextFormatter.md) class. +The default constructor. ```csharp public ActorRuntimeLogTextFormatter() diff --git a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnCreateStateMachine.md b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnCreateStateMachine.md index ae7189e9e..9c51c211f 100644 --- a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnCreateStateMachine.md +++ b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnCreateStateMachine.md @@ -3,7 +3,7 @@ Invoked when the specified state machine has been created. ```csharp -public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) +public virtual void OnCreateStateMachine(ActorId id, string creatorName, string creatorType) ``` | parameter | description | diff --git a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md index ace2a84fa..8feb27a14 100644 --- a/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md +++ b/docs/ref/Microsoft.Coyote.Actors/ActorRuntimeLogTextFormatter/OnEventHandlerTerminated.md @@ -3,7 +3,8 @@ Invoked when the event handler of the specified actor terminated. ```csharp -public void OnEventHandlerTerminated(ActorId id, string stateName, DequeueStatus dequeueStatus) +public virtual void OnEventHandlerTerminated(ActorId id, string stateName, + DequeueStatus dequeueStatus) ``` | parameter | description | diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger.md deleted file mode 100644 index 4164fff92..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger.md +++ /dev/null @@ -1,32 +0,0 @@ -# ConsoleLogger class - -Logger that writes text to the console. - -```csharp -public sealed class ConsoleLogger : TextWriter, ILogger -``` - -## Public Members - -| name | description | -| --- | --- | -| [ConsoleLogger](ConsoleLogger/ConsoleLogger.md)() | Initializes a new instance of the [`ConsoleLogger`](./ConsoleLogger.md) class. | -| override [Encoding](ConsoleLogger/Encoding.md) { get; } | When overridden in a derived class, returns the character encoding in which the output is written. | -| [LogLevel](ConsoleLogger/LogLevel.md) { get; set; } | The level of detail to report. | -| [TextWriter](ConsoleLogger/TextWriter.md) { get; } | This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. | -| override [Write](ConsoleLogger/Write.md)(…) | Writes an informational string to the log. (2 methods) | -| [Write](ConsoleLogger/Write.md)(…) | Writes a string to the log. (2 methods) | -| override [WriteLine](ConsoleLogger/WriteLine.md)(…) | Writes an informational string to the log. (2 methods) | -| [WriteLine](ConsoleLogger/WriteLine.md)(…) | Writes a string followed by a line terminator to the text string or stream. (2 methods) | - -## Remarks - -See [Logging](/coyote/concepts/actors/logging) for more information. - -## See Also - -* interface [ILogger](./ILogger.md) -* namespace [Microsoft.Coyote.IO](../Microsoft.Coyote.IONamespace.md) -* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger.md deleted file mode 100644 index 4b0a799eb..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger.md +++ /dev/null @@ -1,15 +0,0 @@ -# ConsoleLogger constructor - -Initializes a new instance of the [`ConsoleLogger`](../ConsoleLogger.md) class. - -```csharp -public ConsoleLogger() -``` - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding.md deleted file mode 100644 index 35b6f5394..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding.md +++ /dev/null @@ -1,15 +0,0 @@ -# ConsoleLogger.Encoding property - -When overridden in a derived class, returns the character encoding in which the output is written. - -```csharp -public override Encoding Encoding { get; } -``` - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel.md deleted file mode 100644 index 0de28d09e..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel.md +++ /dev/null @@ -1,16 +0,0 @@ -# ConsoleLogger.LogLevel property - -The level of detail to report. - -```csharp -public LogSeverity LogLevel { get; set; } -``` - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter.md deleted file mode 100644 index b04bdd259..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter.md +++ /dev/null @@ -1,15 +0,0 @@ -# ConsoleLogger.TextWriter property - -This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. - -```csharp -public TextWriter TextWriter { get; } -``` - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Write.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Write.md deleted file mode 100644 index 74a0c3935..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/Write.md +++ /dev/null @@ -1,85 +0,0 @@ -# ConsoleLogger.Write method (1 of 4) - -Writes an informational string to the log. - -```csharp -public override void Write(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.Write method (2 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.Write method (3 of 4) - -Writes an informational string to the log. - -```csharp -public override void Write(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.Write method (4 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine.md b/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine.md deleted file mode 100644 index 7a09ff8c7..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine.md +++ /dev/null @@ -1,85 +0,0 @@ -# ConsoleLogger.WriteLine method (1 of 4) - -Writes an informational string to the log. - -```csharp -public override void WriteLine(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.WriteLine method (2 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.WriteLine method (3 of 4) - -Writes an informational string to the log. - -```csharp -public override void WriteLine(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ConsoleLogger.WriteLine method (4 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [ConsoleLogger](../ConsoleLogger.md) -* namespace [Microsoft.Coyote.IO](../ConsoleLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ILogger/TextWriter.md b/docs/ref/Microsoft.Coyote.IO/ILogger/TextWriter.md deleted file mode 100644 index c0222fd8f..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ILogger/TextWriter.md +++ /dev/null @@ -1,15 +0,0 @@ -# ILogger.TextWriter property - -This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. - -```csharp -public TextWriter TextWriter { get; } -``` - -## See Also - -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ILogger/Write.md b/docs/ref/Microsoft.Coyote.IO/ILogger/Write.md deleted file mode 100644 index 5289183b5..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ILogger/Write.md +++ /dev/null @@ -1,85 +0,0 @@ -# ILogger.Write method (1 of 4) - -Writes an informational string to the log. - -```csharp -public void Write(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.Write method (2 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.Write method (3 of 4) - -Writes an informational string to the log. - -```csharp -public void Write(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.Write method (4 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/ILogger/WriteLine.md b/docs/ref/Microsoft.Coyote.IO/ILogger/WriteLine.md deleted file mode 100644 index f617ec901..000000000 --- a/docs/ref/Microsoft.Coyote.IO/ILogger/WriteLine.md +++ /dev/null @@ -1,85 +0,0 @@ -# ILogger.WriteLine method (1 of 4) - -Writes an informational string to the log. - -```csharp -public void WriteLine(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.WriteLine method (2 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.WriteLine method (3 of 4) - -Writes an informational string to the log. - -```csharp -public void WriteLine(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# ILogger.WriteLine method (4 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* interface [ILogger](../ILogger.md) -* namespace [Microsoft.Coyote.IO](../ILogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger.md deleted file mode 100644 index 77b8f6212..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger.md +++ /dev/null @@ -1,38 +0,0 @@ -# InMemoryLogger class - -Thread safe logger that writes text to an in-memory buffer. The buffered text can be extracted using the ToString() method. - -```csharp -public sealed class InMemoryLogger : TextWriter, ILogger -``` - -## Public Members - -| name | description | -| --- | --- | -| [InMemoryLogger](InMemoryLogger/InMemoryLogger.md)() | Initializes a new instance of the [`InMemoryLogger`](./InMemoryLogger.md) class. | -| override [Encoding](InMemoryLogger/Encoding.md) { get; } | When overridden in a derived class, returns the character encoding in which the output is written. | -| [TextWriter](InMemoryLogger/TextWriter.md) { get; } | This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. | -| override [ToString](InMemoryLogger/ToString.md)() | Returns the logged text as a string. | -| override [Write](InMemoryLogger/Write.md)(…) | Writes an informational string to the log. | -| [Write](InMemoryLogger/Write.md)(…) | Writes a string to the log. (2 methods) | -| override [WriteLine](InMemoryLogger/WriteLine.md)(…) | Writes an informational string to the log. | -| [WriteLine](InMemoryLogger/WriteLine.md)(…) | Writes a string followed by a line terminator to the text string or stream. (2 methods) | - -## Protected Members - -| name | description | -| --- | --- | -| override [Dispose](InMemoryLogger/Dispose.md)(…) | Releases the resources used by the logger. | - -## Remarks - -See [Logging](/coyote/concepts/actors/logging) for more information. - -## See Also - -* interface [ILogger](./ILogger.md) -* namespace [Microsoft.Coyote.IO](../Microsoft.Coyote.IONamespace.md) -* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose.md deleted file mode 100644 index bfd0988fb..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose.md +++ /dev/null @@ -1,15 +0,0 @@ -# InMemoryLogger.Dispose method - -Releases the resources used by the logger. - -```csharp -protected override void Dispose(bool disposing) -``` - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding.md deleted file mode 100644 index bac20bf7e..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding.md +++ /dev/null @@ -1,15 +0,0 @@ -# InMemoryLogger.Encoding property - -When overridden in a derived class, returns the character encoding in which the output is written. - -```csharp -public override Encoding Encoding { get; } -``` - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger.md deleted file mode 100644 index 10d96c743..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger.md +++ /dev/null @@ -1,15 +0,0 @@ -# InMemoryLogger constructor - -Initializes a new instance of the [`InMemoryLogger`](../InMemoryLogger.md) class. - -```csharp -public InMemoryLogger() -``` - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter.md deleted file mode 100644 index 1778a5ae3..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter.md +++ /dev/null @@ -1,15 +0,0 @@ -# InMemoryLogger.TextWriter property - -This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. - -```csharp -public TextWriter TextWriter { get; } -``` - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Write.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Write.md deleted file mode 100644 index 7a6b75363..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/Write.md +++ /dev/null @@ -1,64 +0,0 @@ -# InMemoryLogger.Write method (1 of 3) - -Writes an informational string to the log. - -```csharp -public override void Write(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# InMemoryLogger.Write method (2 of 3) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# InMemoryLogger.Write method (3 of 3) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine.md b/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine.md deleted file mode 100644 index 3ab15039d..000000000 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine.md +++ /dev/null @@ -1,64 +0,0 @@ -# InMemoryLogger.WriteLine method (1 of 3) - -Writes an informational string to the log. - -```csharp -public override void WriteLine(string value) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# InMemoryLogger.WriteLine method (2 of 3) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# InMemoryLogger.WriteLine method (3 of 3) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/LogSeverity.md b/docs/ref/Microsoft.Coyote.IO/LogSeverity.md deleted file mode 100644 index a35a09278..000000000 --- a/docs/ref/Microsoft.Coyote.IO/LogSeverity.md +++ /dev/null @@ -1,23 +0,0 @@ -# LogSeverity enumeration - -Flag indicating the type of logging information being provided to the [`ILogger`](./ILogger.md). - -```csharp -public enum LogSeverity -``` - -## Values - -| name | value | description | -| --- | --- | --- | -| Informational | `0` | General information about what is happening in the program. | -| Warning | `1` | Warnings that something unusual is found and is being handled. | -| Error | `2` | Error is something unexpected that usually means program cannot proceed normally. | -| Important | `3` | Output that is not an error or warning, but is important. | - -## See Also - -* namespace [Microsoft.Coyote.IO](../Microsoft.Coyote.IONamespace.md) -* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger.md b/docs/ref/Microsoft.Coyote.IO/TextWriterLogger.md deleted file mode 100644 index a6f57630a..000000000 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger.md +++ /dev/null @@ -1,27 +0,0 @@ -# TextWriterLogger class - -Bridges custom user provided TextWriter logger so it can be passed into Coyote via the [`ILogger`](./ILogger.md) interface. - -```csharp -public class TextWriterLogger : TextWriter, ILogger -``` - -## Public Members - -| name | description | -| --- | --- | -| [TextWriterLogger](TextWriterLogger/TextWriterLogger.md)(…) | Initializes a new instance of the [`TextWriterLogger`](./TextWriterLogger.md) class. | -| override [Encoding](TextWriterLogger/Encoding.md) { get; } | | -| [TextWriter](TextWriterLogger/TextWriter.md) { get; } | This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. | -| override [Write](TextWriterLogger/Write.md)(…) | Writes an informational string to the log. (2 methods) | -| [Write](TextWriterLogger/Write.md)(…) | Writes a string to the log. (2 methods) | -| override [WriteLine](TextWriterLogger/WriteLine.md)(…) | Writes an informational string to the log. (2 methods) | -| [WriteLine](TextWriterLogger/WriteLine.md)(…) | Writes a string followed by a line terminator to the text string or stream. (2 methods) | - -## See Also - -* interface [ILogger](./ILogger.md) -* namespace [Microsoft.Coyote.IO](../Microsoft.Coyote.IONamespace.md) -* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter.md b/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter.md deleted file mode 100644 index 845f13f58..000000000 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter.md +++ /dev/null @@ -1,15 +0,0 @@ -# TextWriterLogger.TextWriter property - -This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. - -```csharp -public TextWriter TextWriter { get; } -``` - -## See Also - -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Write.md b/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Write.md deleted file mode 100644 index 5e3709dea..000000000 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Write.md +++ /dev/null @@ -1,85 +0,0 @@ -# TextWriterLogger.Write method (1 of 4) - -Writes an informational string to the log. - -```csharp -public override void Write(string message) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.Write method (2 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.Write method (3 of 4) - -Writes an informational string to the log. - -```csharp -public override void Write(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.Write method (4 of 4) - -Writes a string to the log. - -```csharp -public void Write(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine.md b/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine.md deleted file mode 100644 index 619ba2a98..000000000 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine.md +++ /dev/null @@ -1,85 +0,0 @@ -# TextWriterLogger.WriteLine method (1 of 4) - -Writes an informational string to the log. - -```csharp -public override void WriteLine(string message) -``` - -| parameter | description | -| --- | --- | -| value | The string to write. | - -## See Also - -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.WriteLine method (2 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string value) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| value | The string to write. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.WriteLine method (3 of 4) - -Writes an informational string to the log. - -```csharp -public override void WriteLine(string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - ---- - -# TextWriterLogger.WriteLine method (4 of 4) - -Writes a string followed by a line terminator to the text string or stream. - -```csharp -public void WriteLine(LogSeverity severity, string format, params object[] args) -``` - -| parameter | description | -| --- | --- | -| severity | The severity of the issue being logged. | -| format | The string format to write. | -| args | The arguments needed to format the string. | - -## See Also - -* enum [LogSeverity](../LogSeverity.md) -* class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote.IONamespace.md b/docs/ref/Microsoft.Coyote.IONamespace.md deleted file mode 100644 index 51e7dd441..000000000 --- a/docs/ref/Microsoft.Coyote.IONamespace.md +++ /dev/null @@ -1,11 +0,0 @@ -## Microsoft.Coyote.IO namespace - -| public type | description | -| --- | --- | -| class [ConsoleLogger](./Microsoft.Coyote.IO/ConsoleLogger.md) | Logger that writes text to the console. | -| interface [ILogger](./Microsoft.Coyote.IO/ILogger.md) | A logger is used to capture messages, warnings and errors. | -| class [InMemoryLogger](./Microsoft.Coyote.IO/InMemoryLogger.md) | Thread safe logger that writes text to an in-memory buffer. The buffered text can be extracted using the ToString() method. | -| enum [LogSeverity](./Microsoft.Coyote.IO/LogSeverity.md) | Flag indicating the type of logging information being provided to the [`ILogger`](./Microsoft.Coyote.IO/ILogger.md). | -| class [TextWriterLogger](./Microsoft.Coyote.IO/TextWriterLogger.md) | Bridges custom user provided TextWriter logger so it can be passed into Coyote via the [`ILogger`](./Microsoft.Coyote.IO/ILogger.md) interface. | - - diff --git a/docs/ref/Microsoft.Coyote.IO/ILogger.md b/docs/ref/Microsoft.Coyote.Logging/ILogger.md similarity index 58% rename from docs/ref/Microsoft.Coyote.IO/ILogger.md rename to docs/ref/Microsoft.Coyote.Logging/ILogger.md index 6541a277c..fb42b046c 100644 --- a/docs/ref/Microsoft.Coyote.IO/ILogger.md +++ b/docs/ref/Microsoft.Coyote.Logging/ILogger.md @@ -10,13 +10,12 @@ public interface ILogger : IDisposable | name | description | | --- | --- | -| [TextWriter](ILogger/TextWriter.md) { get; } | This property provides a TextWriter that implements ILogger which is handy if you have existing code that requires a TextWriter. | -| [Write](ILogger/Write.md)(…) | Writes an informational string to the log. (4 methods) | -| [WriteLine](ILogger/WriteLine.md)(…) | Writes an informational string to the log. (4 methods) | +| [Write](ILogger/Write.md)(…) | Writes an informational string to the log. (10 methods) | +| [WriteLine](ILogger/WriteLine.md)(…) | Writes an informational string to the log. (10 methods) | ## See Also -* namespace [Microsoft.Coyote.IO](../Microsoft.Coyote.IONamespace.md) +* namespace [Microsoft.Coyote.Logging](../Microsoft.Coyote.LoggingNamespace.md) * assembly [Microsoft.Coyote](../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Logging/ILogger/Write.md b/docs/ref/Microsoft.Coyote.Logging/ILogger/Write.md new file mode 100644 index 000000000..ca42179fa --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/ILogger/Write.md @@ -0,0 +1,223 @@ +# ILogger.Write method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (2 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (6 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.Write method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/ILogger/WriteLine.md b/docs/ref/Microsoft.Coyote.Logging/ILogger/WriteLine.md new file mode 100644 index 000000000..48c823eec --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/ILogger/WriteLine.md @@ -0,0 +1,223 @@ +# ILogger.WriteLine method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (2 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (6 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# ILogger.WriteLine method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* interface [ILogger](../ILogger.md) +* namespace [Microsoft.Coyote.Logging](../ILogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/LogSeverity.md b/docs/ref/Microsoft.Coyote.Logging/LogSeverity.md new file mode 100644 index 000000000..85eb620f9 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/LogSeverity.md @@ -0,0 +1,24 @@ +# LogSeverity enumeration + +The severity of the log message being provided to the [`ILogger`](./ILogger.md). + +```csharp +public enum LogSeverity +``` + +## Values + +| name | value | description | +| --- | --- | --- | +| Debug | `0` | Log that contains information useful for debugging purposes. | +| Info | `1` | Log that contains general information. | +| Warning | `2` | Log that contains information about a warning. | +| Error | `3` | Log that contains information about an error. | +| Important | `4` | Log that contains important information. | + +## See Also + +* namespace [Microsoft.Coyote.Logging](../Microsoft.Coyote.LoggingNamespace.md) +* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/MemoryLogger.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger.md new file mode 100644 index 000000000..282c919ec --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger.md @@ -0,0 +1,29 @@ +# MemoryLogger class + +Logger that writes all messages to memory. + +```csharp +public sealed class MemoryLogger : ILogger +``` + +## Public Members + +| name | description | +| --- | --- | +| [MemoryLogger](MemoryLogger/MemoryLogger.md)(…) | Initializes a new instance of the [`MemoryLogger`](./MemoryLogger.md) class. | +| [Dispose](MemoryLogger/Dispose.md)() | Releases any resources held by the logger. | +| override [ToString](MemoryLogger/ToString.md)() | | +| [Write](MemoryLogger/Write.md)(…) | Writes an informational string to the log. (10 methods) | +| [WriteLine](MemoryLogger/WriteLine.md)(…) | Writes an informational string to the log. (10 methods) | + +## Remarks + +This class is thread-safe. + +## See Also + +* interface [ILogger](./ILogger.md) +* namespace [Microsoft.Coyote.Logging](../Microsoft.Coyote.LoggingNamespace.md) +* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose.md new file mode 100644 index 000000000..a1d156d85 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose.md @@ -0,0 +1,15 @@ +# MemoryLogger.Dispose method + +Releases any resources held by the logger. + +```csharp +public void Dispose() +``` + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger.md new file mode 100644 index 000000000..4186fe97b --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger.md @@ -0,0 +1,16 @@ +# MemoryLogger constructor + +Initializes a new instance of the [`MemoryLogger`](../MemoryLogger.md) class. + +```csharp +public MemoryLogger(VerbosityLevel level) +``` + +## See Also + +* enum [VerbosityLevel](../VerbosityLevel.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/ToString.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/ToString.md similarity index 52% rename from docs/ref/Microsoft.Coyote.IO/InMemoryLogger/ToString.md rename to docs/ref/Microsoft.Coyote.Logging/MemoryLogger/ToString.md index bbffac6b1..2f1b6e637 100644 --- a/docs/ref/Microsoft.Coyote.IO/InMemoryLogger/ToString.md +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/ToString.md @@ -1,6 +1,4 @@ -# InMemoryLogger.ToString method - -Returns the logged text as a string. +# MemoryLogger.ToString method ```csharp public override string ToString() @@ -8,8 +6,8 @@ public override string ToString() ## See Also -* class [InMemoryLogger](../InMemoryLogger.md) -* namespace [Microsoft.Coyote.IO](../InMemoryLogger.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Write.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Write.md new file mode 100644 index 000000000..e86053365 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/Write.md @@ -0,0 +1,223 @@ +# MemoryLogger.Write method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (2 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (6 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.Write method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine.md b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine.md new file mode 100644 index 000000000..ead44118f --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine.md @@ -0,0 +1,223 @@ +# MemoryLogger.WriteLine method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (2 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (6 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# MemoryLogger.WriteLine method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [MemoryLogger](../MemoryLogger.md) +* namespace [Microsoft.Coyote.Logging](../MemoryLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger.md b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger.md new file mode 100644 index 000000000..b0310b9ce --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger.md @@ -0,0 +1,24 @@ +# TextWriterLogger class + +Logger that writes to the specified TextWriter. + +```csharp +public sealed class TextWriterLogger : ILogger +``` + +## Public Members + +| name | description | +| --- | --- | +| [TextWriterLogger](TextWriterLogger/TextWriterLogger.md)(…) | Initializes a new instance of the [`TextWriterLogger`](./TextWriterLogger.md) class. | +| [Dispose](TextWriterLogger/Dispose.md)() | Releases any resources held by the logger. | +| [Write](TextWriterLogger/Write.md)(…) | Writes an informational string to the log. (10 methods) | +| [WriteLine](TextWriterLogger/WriteLine.md)(…) | Writes an informational string to the log. (10 methods) | + +## See Also + +* interface [ILogger](./ILogger.md) +* namespace [Microsoft.Coyote.Logging](../Microsoft.Coyote.LoggingNamespace.md) +* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Encoding.md b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Dispose.md similarity index 56% rename from docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Encoding.md rename to docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Dispose.md index 2efa97ca9..ab09b11f3 100644 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/Encoding.md +++ b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Dispose.md @@ -1,13 +1,15 @@ -# TextWriterLogger.Encoding property +# TextWriterLogger.Dispose method + +Releases any resources held by the logger. ```csharp -public override Encoding Encoding { get; } +public void Dispose() ``` ## See Also * class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriterLogger.md b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/TextWriterLogger.md similarity index 62% rename from docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriterLogger.md rename to docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/TextWriterLogger.md index 63a0ccb90..4b8595e32 100644 --- a/docs/ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriterLogger.md +++ b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/TextWriterLogger.md @@ -3,17 +3,14 @@ Initializes a new instance of the [`TextWriterLogger`](../TextWriterLogger.md) class. ```csharp -public TextWriterLogger(TextWriter userLogger) +public TextWriterLogger(TextWriter logger, VerbosityLevel level) ``` -| parameter | description | -| --- | --- | -| userLogger | The TextWriter to delegate to. | - ## See Also +* enum [VerbosityLevel](../VerbosityLevel.md) * class [TextWriterLogger](../TextWriterLogger.md) -* namespace [Microsoft.Coyote.IO](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Write.md b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Write.md new file mode 100644 index 000000000..c8eb8d605 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/Write.md @@ -0,0 +1,223 @@ +# TextWriterLogger.Write method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (2 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (6 of 10) + +Writes a string to the log with the specified verbosity level. + +```csharp +public void Write(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.Write method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void Write(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine.md b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine.md new file mode 100644 index 000000000..832c7c44a --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine.md @@ -0,0 +1,223 @@ +# TextWriterLogger.WriteLine method (1 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string value) +``` + +| parameter | description | +| --- | --- | +| value | The string to write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (2 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string value) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| value | The string to write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (3 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (4 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (5 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (6 of 10) + +Writes a string followed by a line terminator to the text string or stream with the specified verbosity level. + +```csharp +public void WriteLine(LogSeverity severity, string format, params object[] args) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| args | The arguments needed to format the string. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (7 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (8 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (9 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + +--- + +# TextWriterLogger.WriteLine method (10 of 10) + +Writes an informational string to the log. + +```csharp +public void WriteLine(LogSeverity severity, string format, object arg0, object arg1, object arg2) +``` + +| parameter | description | +| --- | --- | +| severity | The severity of the message being logged. | +| format | The string format to write. | +| arg0 | The first object to format and write. | +| arg1 | The second object to format and write. | +| arg2 | The third object to format and write. | + +## See Also + +* enum [LogSeverity](../LogSeverity.md) +* class [TextWriterLogger](../TextWriterLogger.md) +* namespace [Microsoft.Coyote.Logging](../TextWriterLogger.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.Logging/VerbosityLevel.md b/docs/ref/Microsoft.Coyote.Logging/VerbosityLevel.md new file mode 100644 index 000000000..03597d181 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.Logging/VerbosityLevel.md @@ -0,0 +1,25 @@ +# VerbosityLevel enumeration + +The level of verbosity used during logging. + +```csharp +public enum VerbosityLevel +``` + +## Values + +| name | value | description | +| --- | --- | --- | +| None | `0` | Discards any log messages. | +| Error | `1` | Logs error messages that are not recoverable. | +| Warning | `2` | Logs warnings highlighting an abnormal or unexpected event. | +| Info | `3` | Logs informational messages. | +| Debug | `4` | Logs messages that are useful for debugging. | +| Exhaustive | `5` | Logs the most detailed messages. | + +## See Also + +* namespace [Microsoft.Coyote.Logging](../Microsoft.Coyote.LoggingNamespace.md) +* assembly [Microsoft.Coyote](../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote.LoggingNamespace.md b/docs/ref/Microsoft.Coyote.LoggingNamespace.md new file mode 100644 index 000000000..bb48ad4d0 --- /dev/null +++ b/docs/ref/Microsoft.Coyote.LoggingNamespace.md @@ -0,0 +1,11 @@ +## Microsoft.Coyote.Logging namespace + +| public type | description | +| --- | --- | +| interface [ILogger](./Microsoft.Coyote.Logging/ILogger.md) | A logger is used to capture messages, warnings and errors. | +| enum [LogSeverity](./Microsoft.Coyote.Logging/LogSeverity.md) | The severity of the log message being provided to the [`ILogger`](./Microsoft.Coyote.Logging/ILogger.md). | +| class [MemoryLogger](./Microsoft.Coyote.Logging/MemoryLogger.md) | Logger that writes all messages to memory. | +| class [TextWriterLogger](./Microsoft.Coyote.Logging/TextWriterLogger.md) | Logger that writes to the specified TextWriter. | +| enum [VerbosityLevel](./Microsoft.Coyote.Logging/VerbosityLevel.md) | The level of verbosity used during logging. | + + diff --git a/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime.md b/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime.md index 9009e38a6..0fc7cf6d0 100644 --- a/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime.md +++ b/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime.md @@ -10,7 +10,7 @@ public interface ICoyoteRuntime : IDisposable | name | description | | --- | --- | -| [Logger](ICoyoteRuntime/Logger.md) { get; set; } | Get or set the [`ILogger`](../Microsoft.Coyote.IO/ILogger.md) used to log messages. | +| [Logger](ICoyoteRuntime/Logger.md) { get; set; } | Gets or sets an [`ILogger`](../Microsoft.Coyote.Logging/ILogger.md) for logging runtime messages. | | event [OnFailure](ICoyoteRuntime/OnFailure.md) | Callback that is fired when an exception is thrown that includes failed assertions. | | [Assert](ICoyoteRuntime/Assert.md)(…) | Checks if the assertion holds, and if not, throws an [`AssertionFailureException`](./AssertionFailureException.md) exception. (5 methods) | | [Monitor<T>](ICoyoteRuntime/Monitor.md)(…) | Invokes the specified monitor with the specified [`Event`](../Microsoft.Coyote/Event.md). | diff --git a/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime/Logger.md b/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime/Logger.md index b99509760..ca22d9957 100644 --- a/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime/Logger.md +++ b/docs/ref/Microsoft.Coyote.Runtime/ICoyoteRuntime/Logger.md @@ -1,6 +1,6 @@ # ICoyoteRuntime.Logger property -Get or set the [`ILogger`](../../Microsoft.Coyote.IO/ILogger.md) used to log messages. +Gets or sets an [`ILogger`](../../Microsoft.Coyote.Logging/ILogger.md) for logging runtime messages. ```csharp public ILogger Logger { get; set; } @@ -12,7 +12,7 @@ See [Logging](/coyote/concepts/actors/logging) for more information. ## See Also -* interface [ILogger](../../Microsoft.Coyote.IO/ILogger.md) +* interface [ILogger](../../Microsoft.Coyote.Logging/ILogger.md) * interface [ICoyoteRuntime](../ICoyoteRuntime.md) * namespace [Microsoft.Coyote.Runtime](../ICoyoteRuntime.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter.md b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter.md index 2f29e281d..fe94e3b4b 100644 --- a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter.md +++ b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter.md @@ -10,8 +10,7 @@ public class RuntimeLogTextFormatter : IRuntimeLog | name | description | | --- | --- | -| [RuntimeLogTextFormatter](RuntimeLogTextFormatter/RuntimeLogTextFormatter.md)() | Initializes a new instance of the [`RuntimeLogTextFormatter`](./RuntimeLogTextFormatter.md) class. | -| [Logger](RuntimeLogTextFormatter/Logger.md) { get; set; } | Get or set the [`ILogger`](../Microsoft.Coyote.IO/ILogger.md) interface to the logger. | +| [RuntimeLogTextFormatter](RuntimeLogTextFormatter/RuntimeLogTextFormatter.md)() | The default constructor. | | virtual [OnAssertionFailure](RuntimeLogTextFormatter/OnAssertionFailure.md)(…) | Invoked when the specified assertion failure has occurred. | | virtual [OnCompleted](RuntimeLogTextFormatter/OnCompleted.md)() | Invoked when a log is complete (and is about to be closed). | | virtual [OnCreateMonitor](RuntimeLogTextFormatter/OnCreateMonitor.md)(…) | Invoked when the specified monitor has been created. | @@ -22,6 +21,12 @@ public class RuntimeLogTextFormatter : IRuntimeLog | virtual [OnMonitorStateTransition](RuntimeLogTextFormatter/OnMonitorStateTransition.md)(…) | Invoked when the specified monitor enters or exits a state. | | virtual [OnRandom](RuntimeLogTextFormatter/OnRandom.md)(…) | Invoked when the specified controlled nondeterministic boolean result has been obtained. (2 methods) | +## Protected Members + +| name | description | +| --- | --- | +| [Logger](RuntimeLogTextFormatter/Logger.md) { get; } | Used for logging runtime messages. | + ## Remarks See [Logging](/coyote/concepts/actors/logging) for more information. diff --git a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/Logger.md b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/Logger.md index a05b4770a..f93fcc836 100644 --- a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/Logger.md +++ b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/Logger.md @@ -1,18 +1,14 @@ # RuntimeLogTextFormatter.Logger property -Get or set the [`ILogger`](../../Microsoft.Coyote.IO/ILogger.md) interface to the logger. +Used for logging runtime messages. ```csharp -public ILogger Logger { get; set; } +protected ILogger Logger { get; } ``` -## Remarks - -If you want Coyote to log to an existing TextWriter, then use the [`TextWriterLogger`](../../Microsoft.Coyote.IO/TextWriterLogger.md) object but that will have a minor performance overhead, so it is better to use [`ILogger`](../../Microsoft.Coyote.IO/ILogger.md) directly. - ## See Also -* interface [ILogger](../../Microsoft.Coyote.IO/ILogger.md) +* interface [ILogger](../../Microsoft.Coyote.Logging/ILogger.md) * class [RuntimeLogTextFormatter](../RuntimeLogTextFormatter.md) * namespace [Microsoft.Coyote.Runtime](../RuntimeLogTextFormatter.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/RuntimeLogTextFormatter.md b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/RuntimeLogTextFormatter.md index 0c3105d34..3fcdbae22 100644 --- a/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/RuntimeLogTextFormatter.md +++ b/docs/ref/Microsoft.Coyote.Runtime/RuntimeLogTextFormatter/RuntimeLogTextFormatter.md @@ -1,6 +1,6 @@ # RuntimeLogTextFormatter constructor -Initializes a new instance of the [`RuntimeLogTextFormatter`](../RuntimeLogTextFormatter.md) class. +The default constructor. ```csharp public RuntimeLogTextFormatter() diff --git a/docs/ref/Microsoft.Coyote.Specifications/Monitor/Logger.md b/docs/ref/Microsoft.Coyote.Specifications/Monitor/Logger.md index a3ceba06d..98f49c0f1 100644 --- a/docs/ref/Microsoft.Coyote.Specifications/Monitor/Logger.md +++ b/docs/ref/Microsoft.Coyote.Specifications/Monitor/Logger.md @@ -12,7 +12,7 @@ See [Logging](/coyote/concepts/actors/logging) for more information. ## See Also -* interface [ILogger](../../Microsoft.Coyote.IO/ILogger.md) +* interface [ILogger](../../Microsoft.Coyote.Logging/ILogger.md) * class [Monitor](../Monitor.md) * namespace [Microsoft.Coyote.Specifications](../Monitor.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine.md b/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine.md index 7903d21ce..0dec4ee05 100644 --- a/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine.md +++ b/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine.md @@ -10,8 +10,7 @@ public sealed class TestingEngine : IDisposable | name | description | | --- | --- | -| static [Create](TestingEngine/Create.md)(…) | Creates a new systematic testing engine. (7 methods) | -| [Logger](TestingEngine/Logger.md) { get; set; } | Get or set the ILogger used to log messages during testing. | +| static [Create](TestingEngine/Create.md)(…) | Creates a new systematic testing engine. (6 methods) | | [ReadableTrace](TestingEngine/ReadableTrace.md) { get; } | The readable trace, if any. | | [ReproducibleTrace](TestingEngine/ReproducibleTrace.md) { get; } | The reproducible trace, if any. | | [TestReport](TestingEngine/TestReport.md) { get; set; } | Data structure containing information gathered during testing. | @@ -23,6 +22,7 @@ public sealed class TestingEngine : IDisposable | [RegisterEndIterationCallBack](TestingEngine/RegisterEndIterationCallBack.md)(…) | Registers a callback to invoke at the end of each iteration. The callback takes as a parameter an integer representing the current iteration. | | [RegisterStartIterationCallBack](TestingEngine/RegisterStartIterationCallBack.md)(…) | Registers a callback to invoke at the start of each iteration. The callback takes as a parameter an integer representing the current iteration. | | [Run](TestingEngine/Run.md)() | Runs the testing engine. | +| [SetLogger](TestingEngine/SetLogger.md)(…) | Installs the specified ILogger to log messages during testing. | | [Stop](TestingEngine/Stop.md)() | Stops the testing engine. | | [ThrowIfBugFound](TestingEngine/ThrowIfBugFound.md)() | Throws either an AssertionFailureException, if a bug was found, or an unhandled Exception, if one was thrown. | | [TryEmitCoverageReports](TestingEngine/TryEmitCoverageReports.md)(…) | Tries to emit the available coverage reports to the specified directory with the given file name, and returns the paths of all emitted coverage reports. | diff --git a/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Create.md b/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Create.md index 0d0dbeb0d..4575c51b3 100644 --- a/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Create.md +++ b/docs/ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Create.md @@ -1,20 +1,4 @@ -# TestingEngine.Create method (1 of 7) - -Creates a new systematic testing engine. - -```csharp -public static TestingEngine Create(Configuration configuration) -``` - -## See Also - -* class [TestingEngine](../TestingEngine.md) -* namespace [Microsoft.Coyote.SystematicTesting](../TestingEngine.md) -* assembly [Microsoft.Coyote.Test](../../Microsoft.Coyote.Test.md) - ---- - -# TestingEngine.Create method (2 of 7) +# TestingEngine.Create method (1 of 6) Creates a new systematic testing engine. @@ -30,7 +14,7 @@ public static TestingEngine Create(Configuration configuration, Action test) --- -# TestingEngine.Create method (3 of 7) +# TestingEngine.Create method (2 of 6) Creates a new systematic testing engine. @@ -46,7 +30,7 @@ public static TestingEngine Create(Configuration configuration, Action diff --git a/docs/ref/Microsoft.Coyote/Configuration/IsVerbose.md b/docs/ref/Microsoft.Coyote/Configuration/VerbosityLevel.md similarity index 55% rename from docs/ref/Microsoft.Coyote/Configuration/IsVerbose.md rename to docs/ref/Microsoft.Coyote/Configuration/VerbosityLevel.md index 89369f736..943d77249 100644 --- a/docs/ref/Microsoft.Coyote/Configuration/IsVerbose.md +++ b/docs/ref/Microsoft.Coyote/Configuration/VerbosityLevel.md @@ -1,13 +1,14 @@ -# Configuration.IsVerbose property +# Configuration.VerbosityLevel property -If true, then messages are logged. +The level of verbosity to use during logging. ```csharp -public bool IsVerbose { get; } +public VerbosityLevel VerbosityLevel { get; } ``` ## See Also +* enum [VerbosityLevel](../../Microsoft.Coyote.Logging/VerbosityLevel.md) * class [Configuration](../Configuration.md) * namespace [Microsoft.Coyote](../Configuration.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled.md b/docs/ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled.md new file mode 100644 index 000000000..ae38d6cf4 --- /dev/null +++ b/docs/ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled.md @@ -0,0 +1,19 @@ +# Configuration.WithConsoleLoggingEnabled method + +Updates the configuration to log all runtime messages to the console, unless overridden by a custom [`ILogger`](../../Microsoft.Coyote.Logging/ILogger.md). + +```csharp +public Configuration WithConsoleLoggingEnabled(bool isEnabled = true) +``` + +| parameter | description | +| --- | --- | +| isEnabled | If true, then logs all runtime messages to the console. | + +## See Also + +* class [Configuration](../Configuration.md) +* namespace [Microsoft.Coyote](../Configuration.md) +* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) + + diff --git a/docs/ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled.md b/docs/ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled.md deleted file mode 100644 index e451e8ac4..000000000 --- a/docs/ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled.md +++ /dev/null @@ -1,19 +0,0 @@ -# Configuration.WithDebugLoggingEnabled method - -Updates the configuration with debug logging enabled or disabled. - -```csharp -public Configuration WithDebugLoggingEnabled(bool isDebugLoggingEnabled = true) -``` - -| parameter | description | -| --- | --- | -| isDebugLoggingEnabled | If true, then debug messages are logged. | - -## See Also - -* class [Configuration](../Configuration.md) -* namespace [Microsoft.Coyote](../Configuration.md) -* assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) - - diff --git a/docs/ref/Microsoft.Coyote/Configuration/WithTelemetryEnabled.md b/docs/ref/Microsoft.Coyote/Configuration/WithTelemetryEnabled.md index a4ef7b1e3..a38deb870 100644 --- a/docs/ref/Microsoft.Coyote/Configuration/WithTelemetryEnabled.md +++ b/docs/ref/Microsoft.Coyote/Configuration/WithTelemetryEnabled.md @@ -6,6 +6,10 @@ Updates the configuration with telemetry enabled or disabled. public Configuration WithTelemetryEnabled(bool isEnabled = true) ``` +| parameter | description | +| --- | --- | +| isEnabled | If true, then enables telemetry. | + ## See Also * class [Configuration](../Configuration.md) diff --git a/docs/ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md b/docs/ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md index 90b2b1229..9cb0ea7f8 100644 --- a/docs/ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md +++ b/docs/ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md @@ -1,20 +1,18 @@ # Configuration.WithVerbosityEnabled method -Updates the configuration with verbose output enabled or disabled. +Updates the configuration to use the specified verbosity level, which by default is Info. ```csharp -public Configuration WithVerbosityEnabled(bool isVerbose = true, - LogSeverity logLevel = LogSeverity.Informational) +public Configuration WithVerbosityEnabled(VerbosityLevel level = VerbosityLevel.Info) ``` | parameter | description | | --- | --- | -| isVerbose | If true, then messages are logged. | -| logLevel | The level of detail to provide in verbose logging. | +| level | The level of verbosity used during logging. | ## See Also -* enum [LogSeverity](../../Microsoft.Coyote.IO/LogSeverity.md) +* enum [VerbosityLevel](../../Microsoft.Coyote.Logging/VerbosityLevel.md) * class [Configuration](../Configuration.md) * namespace [Microsoft.Coyote](../Configuration.md) * assembly [Microsoft.Coyote](../../Microsoft.Coyote.md) diff --git a/docs/ref/toc.yml b/docs/ref/toc.yml index 35cf3c651..6b10bcbeb 100644 --- a/docs/ref/toc.yml +++ b/docs/ref/toc.yml @@ -15,6 +15,10 @@ toc: subfolderitems: - name: Create link: ref/Microsoft.Coyote/Configuration/Create + - name: WithVerbosityEnabled + link: ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled + - name: WithConsoleLoggingEnabled + link: ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled - name: WithTestingIterations link: ref/Microsoft.Coyote/Configuration/WithTestingIterations - name: WithTestingTimeout @@ -57,10 +61,6 @@ toc: link: ref/Microsoft.Coyote/Configuration/WithUncontrolledConcurrencyResolutionTimeout - name: WithRandomGeneratorSeed link: ref/Microsoft.Coyote/Configuration/WithRandomGeneratorSeed - - name: WithVerbosityEnabled - link: ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled - - name: WithDebugLoggingEnabled - link: ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled - name: WithActivityCoverageReported link: ref/Microsoft.Coyote/Configuration/WithActivityCoverageReported - name: WithTraceVisualizationEnabled @@ -87,10 +87,8 @@ toc: link: ref/Microsoft.Coyote/Configuration/TimeoutDelay - name: DeadlockTimeout link: ref/Microsoft.Coyote/Configuration/DeadlockTimeout - - name: IsVerbose - link: ref/Microsoft.Coyote/Configuration/IsVerbose - - name: LogLevel - link: ref/Microsoft.Coyote/Configuration/LogLevel + - name: VerbosityLevel + link: ref/Microsoft.Coyote/Configuration/VerbosityLevel - name: Microsoft.Coyote.Actors link: ref/Microsoft.Coyote.ActorsNamespace subfolderitems: @@ -647,65 +645,44 @@ toc: link: ref/Microsoft.Coyote.Actors.Timers/TimerInfo/Period - name: CustomEvent link: ref/Microsoft.Coyote.Actors.Timers/TimerInfo/CustomEvent - - name: Microsoft.Coyote.IO - link: ref/Microsoft.Coyote.IONamespace + - name: Microsoft.Coyote.Logging + link: ref/Microsoft.Coyote.LoggingNamespace subfolderitems: - - name: ConsoleLogger - link: ref/Microsoft.Coyote.IO/ConsoleLogger - subfolderitems: - - name: Write - link: ref/Microsoft.Coyote.IO/ConsoleLogger/Write - - name: WriteLine - link: ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine - - name: ConsoleLogger - link: ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger - - name: TextWriter - link: ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter - - name: Encoding - link: ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding - - name: LogLevel - link: ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel - name: ILogger - link: ref/Microsoft.Coyote.IO/ILogger + link: ref/Microsoft.Coyote.Logging/ILogger subfolderitems: - name: Write - link: ref/Microsoft.Coyote.IO/ILogger/Write + link: ref/Microsoft.Coyote.Logging/ILogger/Write - name: WriteLine - link: ref/Microsoft.Coyote.IO/ILogger/WriteLine - - name: TextWriter - link: ref/Microsoft.Coyote.IO/ILogger/TextWriter - - name: InMemoryLogger - link: ref/Microsoft.Coyote.IO/InMemoryLogger + link: ref/Microsoft.Coyote.Logging/ILogger/WriteLine + - name: LogSeverity + link: ref/Microsoft.Coyote.Logging/LogSeverity + - name: MemoryLogger + link: ref/Microsoft.Coyote.Logging/MemoryLogger subfolderitems: - name: Write - link: ref/Microsoft.Coyote.IO/InMemoryLogger/Write + link: ref/Microsoft.Coyote.Logging/MemoryLogger/Write - name: WriteLine - link: ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine + link: ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine - name: ToString - link: ref/Microsoft.Coyote.IO/InMemoryLogger/ToString + link: ref/Microsoft.Coyote.Logging/MemoryLogger/ToString - name: Dispose - link: ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose - - name: InMemoryLogger - link: ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger - - name: TextWriter - link: ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter - - name: Encoding - link: ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding - - name: LogSeverity - link: ref/Microsoft.Coyote.IO/LogSeverity + link: ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose + - name: MemoryLogger + link: ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger - name: TextWriterLogger - link: ref/Microsoft.Coyote.IO/TextWriterLogger + link: ref/Microsoft.Coyote.Logging/TextWriterLogger subfolderitems: - name: Write - link: ref/Microsoft.Coyote.IO/TextWriterLogger/Write + link: ref/Microsoft.Coyote.Logging/TextWriterLogger/Write - name: WriteLine - link: ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine + link: ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine + - name: Dispose + link: ref/Microsoft.Coyote.Logging/TextWriterLogger/Dispose - name: TextWriterLogger - link: ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriterLogger - - name: TextWriter - link: ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter - - name: Encoding - link: ref/Microsoft.Coyote.IO/TextWriterLogger/Encoding + link: ref/Microsoft.Coyote.Logging/TextWriterLogger/TextWriterLogger + - name: VerbosityLevel + link: ref/Microsoft.Coyote.Logging/VerbosityLevel - name: Microsoft.Coyote.Random link: ref/Microsoft.Coyote.RandomNamespace subfolderitems: @@ -1069,12 +1046,12 @@ toc: link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/InvokeEndIterationCallBacks - name: IsTestRewritten link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/IsTestRewritten + - name: SetLogger + link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/SetLogger - name: Dispose link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Dispose - name: TestReport link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/TestReport - - name: Logger - link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Logger - name: ReadableTrace link: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/ReadableTrace - name: ReproducibleTrace diff --git a/mkdocs.yml b/mkdocs.yml index ffbc4b09a..c04bbd113 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -109,6 +109,8 @@ nav: - Configuration: - Overview: ref/Microsoft.Coyote/Configuration.md - Create: ref/Microsoft.Coyote/Configuration/Create.md + - WithVerbosityEnabled: ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md + - WithConsoleLoggingEnabled: ref/Microsoft.Coyote/Configuration/WithConsoleLoggingEnabled.md - WithTestingIterations: ref/Microsoft.Coyote/Configuration/WithTestingIterations.md - WithTestingTimeout: ref/Microsoft.Coyote/Configuration/WithTestingTimeout.md - WithReproducibleTrace: ref/Microsoft.Coyote/Configuration/WithReproducibleTrace.md @@ -130,8 +132,6 @@ nav: - WithPotentialDeadlocksReportedAsBugs: ref/Microsoft.Coyote/Configuration/WithPotentialDeadlocksReportedAsBugs.md - WithUncontrolledConcurrencyResolutionTimeout: ref/Microsoft.Coyote/Configuration/WithUncontrolledConcurrencyResolutionTimeout.md - WithRandomGeneratorSeed: ref/Microsoft.Coyote/Configuration/WithRandomGeneratorSeed.md - - WithVerbosityEnabled: ref/Microsoft.Coyote/Configuration/WithVerbosityEnabled.md - - WithDebugLoggingEnabled: ref/Microsoft.Coyote/Configuration/WithDebugLoggingEnabled.md - WithActivityCoverageReported: ref/Microsoft.Coyote/Configuration/WithActivityCoverageReported.md - WithTraceVisualizationEnabled: ref/Microsoft.Coyote/Configuration/WithTraceVisualizationEnabled.md - WithXmlLogEnabled: ref/Microsoft.Coyote/Configuration/WithXmlLogEnabled.md @@ -145,8 +145,7 @@ nav: - MaxFairSchedulingSteps: ref/Microsoft.Coyote/Configuration/MaxFairSchedulingSteps.md - TimeoutDelay: ref/Microsoft.Coyote/Configuration/TimeoutDelay.md - DeadlockTimeout: ref/Microsoft.Coyote/Configuration/DeadlockTimeout.md - - IsVerbose: ref/Microsoft.Coyote/Configuration/IsVerbose.md - - LogLevel: ref/Microsoft.Coyote/Configuration/LogLevel.md + - VerbosityLevel: ref/Microsoft.Coyote/Configuration/VerbosityLevel.md - Microsoft.Coyote.Actors: - Namespace Overview: ref/Microsoft.Coyote.ActorsNamespace.md - Actor: @@ -446,38 +445,27 @@ nav: - DueTime: ref/Microsoft.Coyote.Actors.Timers/TimerInfo/DueTime.md - Period: ref/Microsoft.Coyote.Actors.Timers/TimerInfo/Period.md - CustomEvent: ref/Microsoft.Coyote.Actors.Timers/TimerInfo/CustomEvent.md - - Microsoft.Coyote.IO: - - Namespace Overview: ref/Microsoft.Coyote.IONamespace.md - - ConsoleLogger: - - Overview: ref/Microsoft.Coyote.IO/ConsoleLogger.md - - Write: ref/Microsoft.Coyote.IO/ConsoleLogger/Write.md - - WriteLine: ref/Microsoft.Coyote.IO/ConsoleLogger/WriteLine.md - - ConsoleLogger: ref/Microsoft.Coyote.IO/ConsoleLogger/ConsoleLogger.md - - TextWriter: ref/Microsoft.Coyote.IO/ConsoleLogger/TextWriter.md - - Encoding: ref/Microsoft.Coyote.IO/ConsoleLogger/Encoding.md - - LogLevel: ref/Microsoft.Coyote.IO/ConsoleLogger/LogLevel.md + - Microsoft.Coyote.Logging: + - Namespace Overview: ref/Microsoft.Coyote.LoggingNamespace.md - ILogger: - - Overview: ref/Microsoft.Coyote.IO/ILogger.md - - Write: ref/Microsoft.Coyote.IO/ILogger/Write.md - - WriteLine: ref/Microsoft.Coyote.IO/ILogger/WriteLine.md - - TextWriter: ref/Microsoft.Coyote.IO/ILogger/TextWriter.md - - InMemoryLogger: - - Overview: ref/Microsoft.Coyote.IO/InMemoryLogger.md - - Write: ref/Microsoft.Coyote.IO/InMemoryLogger/Write.md - - WriteLine: ref/Microsoft.Coyote.IO/InMemoryLogger/WriteLine.md - - ToString: ref/Microsoft.Coyote.IO/InMemoryLogger/ToString.md - - Dispose: ref/Microsoft.Coyote.IO/InMemoryLogger/Dispose.md - - InMemoryLogger: ref/Microsoft.Coyote.IO/InMemoryLogger/InMemoryLogger.md - - TextWriter: ref/Microsoft.Coyote.IO/InMemoryLogger/TextWriter.md - - Encoding: ref/Microsoft.Coyote.IO/InMemoryLogger/Encoding.md - - LogSeverity: ref/Microsoft.Coyote.IO/LogSeverity.md + - Overview: ref/Microsoft.Coyote.Logging/ILogger.md + - Write: ref/Microsoft.Coyote.Logging/ILogger/Write.md + - WriteLine: ref/Microsoft.Coyote.Logging/ILogger/WriteLine.md + - LogSeverity: ref/Microsoft.Coyote.Logging/LogSeverity.md + - MemoryLogger: + - Overview: ref/Microsoft.Coyote.Logging/MemoryLogger.md + - Write: ref/Microsoft.Coyote.Logging/MemoryLogger/Write.md + - WriteLine: ref/Microsoft.Coyote.Logging/MemoryLogger/WriteLine.md + - ToString: ref/Microsoft.Coyote.Logging/MemoryLogger/ToString.md + - Dispose: ref/Microsoft.Coyote.Logging/MemoryLogger/Dispose.md + - MemoryLogger: ref/Microsoft.Coyote.Logging/MemoryLogger/MemoryLogger.md - TextWriterLogger: - - Overview: ref/Microsoft.Coyote.IO/TextWriterLogger.md - - Write: ref/Microsoft.Coyote.IO/TextWriterLogger/Write.md - - WriteLine: ref/Microsoft.Coyote.IO/TextWriterLogger/WriteLine.md - - TextWriterLogger: ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriterLogger.md - - TextWriter: ref/Microsoft.Coyote.IO/TextWriterLogger/TextWriter.md - - Encoding: ref/Microsoft.Coyote.IO/TextWriterLogger/Encoding.md + - Overview: ref/Microsoft.Coyote.Logging/TextWriterLogger.md + - Write: ref/Microsoft.Coyote.Logging/TextWriterLogger/Write.md + - WriteLine: ref/Microsoft.Coyote.Logging/TextWriterLogger/WriteLine.md + - Dispose: ref/Microsoft.Coyote.Logging/TextWriterLogger/Dispose.md + - TextWriterLogger: ref/Microsoft.Coyote.Logging/TextWriterLogger/TextWriterLogger.md + - VerbosityLevel: ref/Microsoft.Coyote.Logging/VerbosityLevel.md - Microsoft.Coyote.Random: - Namespace Overview: ref/Microsoft.Coyote.RandomNamespace.md - Generator: @@ -677,9 +665,9 @@ nav: - InvokeStartIterationCallBacks: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/InvokeStartIterationCallBacks.md - InvokeEndIterationCallBacks: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/InvokeEndIterationCallBacks.md - IsTestRewritten: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/IsTestRewritten.md + - SetLogger: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/SetLogger.md - Dispose: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Dispose.md - TestReport: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/TestReport.md - - Logger: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/Logger.md - ReadableTrace: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/ReadableTrace.md - ReproducibleTrace: ref/Microsoft.Coyote.SystematicTesting/TestingEngine/ReproducibleTrace.md - Microsoft.Coyote.Web: