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