diff --git a/src/System.Diagnostics.Debug/tests/DebugTests.cs b/src/System.Diagnostics.Debug/tests/DebugTests.cs index 7c000ab7d6ed..da41ae897700 100644 --- a/src/System.Diagnostics.Debug/tests/DebugTests.cs +++ b/src/System.Diagnostics.Debug/tests/DebugTests.cs @@ -1,322 +1,53 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - +#define DEBUG using System.Reflection; using Xunit; namespace System.Diagnostics.Tests { - // These tests test the static Debug class. They cannot be run in parallel - [Collection("System.Diagnostics.Debug")] - public class DebugTests + public abstract class DebugTests { - [Fact] // for netcoreapp only uncomment when running this xunit method alone - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void Bug_SkipsIndentationOnFirstWrite() - { - // This test shows an existing indentation bug in Debug class - // First Debug.Write call won't respect the indentation. Desktop does not have this bug. - - Debug.Indent(); - int expectedIndentation = Debug.IndentLevel * Debug.IndentSize; - - VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); // bug: netcoreapp does not indent - - // After first WriteLine invocation we get proper indentation - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', 0) + "pizza" + Environment.NewLine); - - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); - VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', 0) + "pizza" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); - Debug.Unindent(); - } - - [Fact] // for netcoreapp only uncomment when running this xunit method alone - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void Bug_SkipsIndentationOnFirstWriteLine() - { - // This test shows an existing indentation bug in Debug class - // First Debug.WriteLine call won't respect the indentation. Desktop does not have this bug. - - Debug.Indent(); - int expectedIndentation = Debug.IndentLevel * Debug.IndentSize; - - // After first WriteLine invocation we get proper indentation - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); // bug: netcoreapp does not indent - - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); - VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', 0) + "pizza" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); - Debug.Unindent(); - } - - [Fact] // for netcoreapp only uncomment when running this xunit method alone - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void Bug_DebugSumsUpTraceAndDebugIndentation() - { - // In Core: - // - The existing indentation amount for Trace is currently: `Debug.IndentLevel * Debug.IndentSize + Trace.IndentLevel * Trace.IndentSize`. - // The Trace indentation amount is just `Trace.IndentLevel * Trace.IndentSize` - - // Set same values of IndentSize and IndentLevel for both Trace and Debug, to ignore bug in: DesktopDiscrepancy_DebugIndentationNotInSyncWithTrace - Debug.IndentSize = 4; - Trace.IndentSize = 4; - Debug.Indent(); - Trace.Indent(); - - int expected = Debug.IndentSize * Debug.IndentLevel; - - Debug.WriteLine("pizza"); // Skip first call, to ignore bug in: Bug_SkipsIndentationOnFirstWriteLine - VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expected) + "pizza" + Environment.NewLine); - Trace.WriteLine("pizza"); // Wires up Debug with TraceListeners - VerifyLogged(() => Trace.WriteLine("pizza"), new string(' ', expected) + "pizza" + Environment.NewLine); // bug: actual netcoreapp indent size is (expected + Trace.IndentLevel * Trace.IndentSize) - - // reset - Debug.Unindent(); - Trace.Unindent(); - Trace.Refresh(); - } - - [Fact] // for netcoreapp only uncomment when running this xunit method alone - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void WriteNull() - { - Debug.IndentSize = 4; - Trace.IndentSize = 4; - Debug.Indent(); - Trace.Indent(); - - int expected = Debug.IndentSize * Debug.IndentLevel; - - Debug.WriteLine(null); // Skip first call, to ignore bug in: Bug_SkipsIndentationOnFirstWriteLine - VerifyLogged(() => Debug.WriteLine(null), new string(' ', expected) + Environment.NewLine); - Trace.WriteLine(null); // Wires up Debug with TraceListeners - VerifyLogged(() => Trace.WriteLine(null), new string(' ', expected) + Environment.NewLine); // bug: actual netcoreapp indent size is (expected + Trace.IndentLevel * Trace.IndentSize) - - // reset - Debug.Unindent(); - Trace.Unindent(); - Trace.Refresh(); - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void DesktopDiscrepancy_DebugIndentationNotInSyncWithTrace() - { - // This test shows an existing indentation bug in Debug class: - // In Desktop, Debug and Trace have the same values for IndentLevel and IndentSize. - // In Core, Updating Trace IndentLevel or IndentSize won't update that for Debug and vice versa. - - int originalDebugIndentSize = Debug.IndentSize; - int originalTraceIndentSize = Trace.IndentSize; - Debug.IndentLevel = 0; - Trace.IndentLevel = 0; - - // test if Debug gets changed when Trace changes - Trace.IndentSize = 7; - Assert.Equal(7, Trace.IndentSize); - Assert.Equal(7, Debug.IndentSize); // bug: in netcoreapp is not equal to 7 - Trace.Indent(); - Assert.Equal(1, Trace.IndentLevel); - Assert.Equal(1, Debug.IndentLevel); // bug: in netcoreapp is not equal to 1 - - // reset - Trace.Unindent(); - Trace.IndentSize = originalTraceIndentSize; - - // test if Trace gets changed when Debug changes - Debug.IndentSize = 7; - Assert.Equal(7, Debug.IndentSize); - Assert.Equal(7, Trace.IndentSize); // bug: in netcoreapp is not equal to 7 - Debug.Indent(); - Assert.Equal(1, Debug.IndentLevel); - Assert.Equal(1, Trace.IndentLevel); // bug: in netcoreapp is not equal to 1 - - // reset - Debug.Unindent(); - Debug.IndentSize = originalDebugIndentSize; - Trace.Refresh(); - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "dotnet/corefx#32955")] - public void ClearTraceListeners_StopsWritingToDebugger() - { - VerifyLogged(() => Debug.Write("pizza"), "pizza"); - VerifyLogged(() => Trace.Write("pizza"), "pizza"); // Wires up Debug with TraceListeners - Trace.Listeners.Clear(); - VerifyLogged(() => Debug.Write("pizza"), string.Empty); - VerifyLogged(() => Trace.Write("pizza"), string.Empty); - Trace.Refresh(); - } - - [Fact] - public void Asserts() - { - VerifyLogged(() => Debug.Assert(true), ""); - VerifyLogged(() => Debug.Assert(true, "assert passed"), ""); - VerifyLogged(() => Debug.Assert(true, "assert passed", "nothing is wrong"), ""); - VerifyLogged(() => Debug.Assert(true, "assert passed", "nothing is wrong {0} {1}", 'a', 'b'), ""); - - VerifyAssert(() => Debug.Assert(false), ""); - VerifyAssert(() => Debug.Assert(false, "assert passed"), "assert passed"); - VerifyAssert(() => Debug.Assert(false, "assert passed", "nothing is wrong"), "assert passed", "nothing is wrong"); - VerifyAssert(() => Debug.Assert(false, "assert passed", "nothing is wrong {0} {1}", 'a', 'b'), "assert passed", "nothing is wrong a b"); - } - - [Fact] - public void Fail() - { - VerifyAssert(() => Debug.Fail("something bad happened"), "something bad happened"); - VerifyAssert(() => Debug.Fail("something bad happened", "really really bad"), "something bad happened", "really really bad"); - } - - [Fact] - public void Write() - { - VerifyLogged(() => Debug.Write(5), "5"); - VerifyLogged(() => Debug.Write((string)null), ""); - VerifyLogged(() => Debug.Write((object)null), ""); - VerifyLogged(() => Debug.Write(5, "category"), "category:5"); - VerifyLogged(() => Debug.Write((object)null, "category"), "category:"); - VerifyLogged(() => Debug.Write("logged"), "logged"); - VerifyLogged(() => Debug.Write("logged", "category"), "category:logged"); - VerifyLogged(() => Debug.Write("logged", (string)null), "logged"); - - string longString = new string('d', 8192); - VerifyLogged(() => Debug.Write(longString), longString); - } - - [Fact] - public void Print() - { - VerifyLogged(() => Debug.Print("logged"), "logged"); - VerifyLogged(() => Debug.Print("logged {0}", 5), "logged 5"); - } - - [Fact] - public void WriteLine() - { - VerifyLogged(() => Debug.WriteLine(5), "5" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine((string)null), Environment.NewLine); - VerifyLogged(() => Debug.WriteLine((object)null), Environment.NewLine); - VerifyLogged(() => Debug.WriteLine(5, "category"), "category:5" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine((object)null, "category"), "category:" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("logged"), "logged" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("logged", "category"), "category:logged" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("logged", (string)null), "logged" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLine("{0} {1}", 'a', 'b'), "a b" + Environment.NewLine); - } - - [Fact] - public void WriteIf() - { - VerifyLogged(() => Debug.WriteIf(true, 5), "5"); - VerifyLogged(() => Debug.WriteIf(false, 5), ""); - - VerifyLogged(() => Debug.WriteIf(true, 5, "category"), "category:5"); - VerifyLogged(() => Debug.WriteIf(false, 5, "category"), ""); - - VerifyLogged(() => Debug.WriteIf(true, "logged"), "logged"); - VerifyLogged(() => Debug.WriteIf(false, "logged"), ""); - - VerifyLogged(() => Debug.WriteIf(true, "logged", "category"), "category:logged"); - VerifyLogged(() => Debug.WriteIf(false, "logged", "category"), ""); - } - - [Fact] - public void WriteLineIf() - { - VerifyLogged(() => Debug.WriteLineIf(true, 5), "5" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLineIf(false, 5), ""); - - VerifyLogged(() => Debug.WriteLineIf(true, 5, "category"), "category:5" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLineIf(false, 5, "category"), ""); - - VerifyLogged(() => Debug.WriteLineIf(true, "logged"), "logged" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLineIf(false, "logged"), ""); + protected abstract bool DebugUsesTraceListeners { get; } + protected static readonly DebugProvider _debugOnlyProvider; + protected static readonly DebugProvider _debugTraceProvider; - VerifyLogged(() => Debug.WriteLineIf(true, "logged", "category"), "category:logged" + Environment.NewLine); - VerifyLogged(() => Debug.WriteLineIf(false, "logged", "category"), ""); - } - - [Theory] - [InlineData(-1, 0)] - [InlineData(0, 0)] - [InlineData(1, 1)] - [InlineData(2, 2)] - [InlineData(3, 3)] - [InlineData(4, 4)] - public void IndentSize_Set_GetReturnsExpected(int indentSize, int expectedIndentSize) + static DebugTests() { - Debug.IndentLevel = 0; - - Debug.IndentSize = indentSize; - Assert.Equal(expectedIndentSize, Debug.IndentSize); - VerifyLogged(() => Debug.WriteLine("pizza"), "pizza" + Environment.NewLine); - - // Indent once. - Debug.Indent(); - string expectedIndentOnce = new string(' ', expectedIndentSize); - VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentOnce + "pizza" + Environment.NewLine); - - // Indent again. - Debug.Indent(); - string expectedIndentTwice = new string(' ', expectedIndentSize * 2); - VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentTwice + "pizza" + Environment.NewLine); - - // Unindent. - Debug.Unindent(); - Debug.Unindent(); + FieldInfo fieldInfo = typeof(Debug).GetField("s_provider", BindingFlags.Static | BindingFlags.NonPublic); + _debugOnlyProvider = (DebugProvider)fieldInfo.GetValue(null); + // Triggers code to wire up TraceListeners with Debug + Assert.Equal(1, Trace.Listeners.Count); + _debugTraceProvider = (DebugProvider)fieldInfo.GetValue(null); + Assert.NotEqual(_debugOnlyProvider.GetType(), _debugTraceProvider.GetType()); } - [Theory] - [InlineData(-1, 0)] - [InlineData(0, 0)] - [InlineData(1, 1)] - public void IndentLevel_Set_GetReturnsExpected(int indentLevel, int expectedIndentLevel) + public DebugTests() { - Debug.IndentLevel = indentLevel; - Assert.Equal(expectedIndentLevel, Debug.IndentLevel); - string expectedIndentOnce = new string(' ', expectedIndentLevel * Debug.IndentSize); - VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentOnce + "pizza" + Environment.NewLine); - - // Indent once. - Debug.Indent(); -#if DEBUG - Assert.Equal(expectedIndentLevel + 1, Debug.IndentLevel); -#else - Assert.Equal(expectedIndentLevel, Debug.IndentLevel); -#endif - string expectedIndentTwice = new string(' ', (expectedIndentLevel + 1) * Debug.IndentSize); - VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentTwice + "pizza" + Environment.NewLine); - - // Unindent. - Debug.Unindent(); - Assert.Equal(expectedIndentLevel, Debug.IndentLevel); - Debug.Unindent(); + if (DebugUsesTraceListeners) + { + Debug.SetProvider(_debugTraceProvider); + } + else + { + Debug.SetProvider(_debugOnlyProvider); + } } - static void VerifyLogged(Action test, string expectedOutput) + protected void VerifyLogged(Action test, string expectedOutput) { FieldInfo writeCoreHook = typeof(DebugProvider).GetField("s_WriteCore", BindingFlags.Static | BindingFlags.NonPublic); // First use our test logger to verify the output var originalWriteCoreHook = writeCoreHook.GetValue(null); - writeCoreHook.SetValue(null, new Action(WriteLogger.s_instance.WriteCore)); + writeCoreHook.SetValue(null, new Action(WriteLogger.s_instance.MockWrite)); try { WriteLogger.s_instance.Clear(); test(); -#if DEBUG Assert.Equal(expectedOutput, WriteLogger.s_instance.LoggedOutput); -#else - Assert.Equal(string.Empty, WriteLogger.s_instance.LoggedOutput); -#endif } finally { @@ -328,28 +59,24 @@ static void VerifyLogged(Action test, string expectedOutput) test(); } - static void VerifyAssert(Action test, params string[] expectedOutputStrings) + protected void VerifyAssert(Action test, params string[] expectedOutputStrings) { FieldInfo writeCoreHook = typeof(DebugProvider).GetField("s_WriteCore", BindingFlags.Static | BindingFlags.NonPublic); s_defaultProvider = Debug.SetProvider(WriteLogger.s_instance); - + WriteLogger.s_instance.OriginalProvider = s_defaultProvider; + var originalWriteCoreHook = writeCoreHook.GetValue(null); - writeCoreHook.SetValue(null, new Action(WriteLogger.s_instance.WriteCore)); + writeCoreHook.SetValue(null, new Action(WriteLogger.s_instance.MockWrite)); try { WriteLogger.s_instance.Clear(); test(); -#if DEBUG for (int i = 0; i < expectedOutputStrings.Length; i++) { Assert.Contains(expectedOutputStrings[i], WriteLogger.s_instance.LoggedOutput); Assert.Contains(expectedOutputStrings[i], WriteLogger.s_instance.AssertUIOutput); } -#else - Assert.Equal(string.Empty, WriteLogger.s_instance.LoggedOutput); - Assert.Equal(string.Empty, WriteLogger.s_instance.AssertUIOutput); -#endif } finally @@ -366,6 +93,8 @@ private class WriteLogger : DebugProvider private WriteLogger() { } + public DebugProvider OriginalProvider { get; set; } + public string LoggedOutput { get; private set; } public string AssertUIOutput { get; private set; } @@ -381,7 +110,19 @@ public override void ShowDialog(string stackTrace, string message, string detail AssertUIOutput += stackTrace + message + detailMessage + errorSource; } - public void WriteCore(string message) + public override void OnIndentLevelChanged(int indentLevel) + { + OriginalProvider.OnIndentLevelChanged(indentLevel); + } + + public override void OnIndentSizeChanged(int indentSize) + { + OriginalProvider.OnIndentLevelChanged(indentSize); + } + public override void Write(string message) { OriginalProvider.Write(message); } + public override void WriteLine(string message) { OriginalProvider.WriteLine(message); } + + public void MockWrite(string message) { Assert.NotNull(message); LoggedOutput += message; diff --git a/src/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs b/src/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs new file mode 100644 index 000000000000..a74d4d27f24f --- /dev/null +++ b/src/System.Diagnostics.Debug/tests/DebugTestsNoListeners.cs @@ -0,0 +1,231 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#define DEBUG +using System.Reflection; +using Xunit; + +namespace System.Diagnostics.Tests +{ + // These tests test the static Debug class. They cannot be run in parallel + // DebugTestsNoListeners: tests Debug behavior before Debug is set with Trace Listeners. + [Collection("System.Diagnostics.Debug")] + public class DebugTestsNoListeners : DebugTests + { + protected override bool DebugUsesTraceListeners { get { return false; } } + + protected void GoToNextLine() + { + // Start from next line before running next test: refer to nameof(Debug_WriteLine_WontIndentAfterWrite) + VerifyLogged(() => Debug.WriteLine(""), Environment.NewLine); + } + + [Fact] + public void Debug_Write_Indents() + { + // This test when run alone verifies Debug.Write indentation, even on first call, is correct. + Debug.Indent(); + VerifyLogged(() => Debug.Write("pizza"), new string(' ', Debug.IndentLevel * Debug.IndentSize) + "pizza"); + Debug.Unindent(); + GoToNextLine(); + } + + [Fact] + public void Debug_WriteLine_Indents() + { + // This test when run alone verifies Debug.WriteLine indentation, even on first call, is correct. + Debug.Indent(); + VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', Debug.IndentLevel * Debug.IndentSize) + "pizza" + Environment.NewLine); + Debug.Unindent(); + } + + [Fact] + public void Debug_WriteLine_WontIndentAfterWrite() + { + Debug.Indent(); + int expectedIndentation = Debug.IndentLevel * Debug.IndentSize; + + VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + + // WriteLine wont indent after Write: + VerifyLogged(() => Debug.WriteLine("pizza"), "pizza" + Environment.NewLine); + + VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); + VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + + // WriteLine wont indent after Write: + VerifyLogged(() => Debug.WriteLine("pizza"), "pizza" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); + Debug.Unindent(); + } + + [Fact] + public void Debug_WriteNull_SkipsIndentation() + { + Debug.Indent(); + VerifyLogged(() => Debug.Write(null), ""); + Debug.Unindent(); + } + + [Fact] + public void Debug_WriteLineNull_IndentsEmptyStringProperly() + { + Debug.Indent(); + int expected = Debug.IndentSize * Debug.IndentLevel; + + VerifyLogged(() => Debug.WriteLine(null), new string(' ', expected) + Environment.NewLine); + + // reset + Debug.Unindent(); + } + + [Fact] + public void Asserts() + { + VerifyLogged(() => Debug.Assert(true), ""); + VerifyLogged(() => Debug.Assert(true, "assert passed"), ""); + VerifyLogged(() => Debug.Assert(true, "assert passed", "nothing is wrong"), ""); + VerifyLogged(() => Debug.Assert(true, "assert passed", "nothing is wrong {0} {1}", 'a', 'b'), ""); + + VerifyAssert(() => Debug.Assert(false), ""); + VerifyAssert(() => Debug.Assert(false, "assert passed"), "assert passed"); + VerifyAssert(() => Debug.Assert(false, "assert passed", "nothing is wrong"), "assert passed", "nothing is wrong"); + VerifyAssert(() => Debug.Assert(false, "assert passed", "nothing is wrong {0} {1}", 'a', 'b'), "assert passed", "nothing is wrong a b"); + } + + [Fact] + public void Fail() + { + VerifyAssert(() => Debug.Fail("something bad happened"), "something bad happened"); + VerifyAssert(() => Debug.Fail("something bad happened", "really really bad"), "something bad happened", "really really bad"); + } + + [Fact] + public void Write() + { + VerifyLogged(() => Debug.Write(5), "5"); + VerifyLogged(() => Debug.Write((string)null), ""); + VerifyLogged(() => Debug.Write((object)null), ""); + VerifyLogged(() => Debug.Write(5, "category"), "category: 5"); + VerifyLogged(() => Debug.Write((object)null, "category"), "category: "); + VerifyLogged(() => Debug.Write("logged"), "logged"); + VerifyLogged(() => Debug.Write("logged", "category"), "category: logged"); + VerifyLogged(() => Debug.Write("logged", (string)null), "logged"); + + string longString = new string('d', 8192); + VerifyLogged(() => Debug.Write(longString), longString); + + GoToNextLine(); + } + + [Fact] + public void Print() + { + VerifyLogged(() => Debug.Print("logged"), "logged"); + VerifyLogged(() => Debug.Print("logged {0}", 5), "logged 5"); + + GoToNextLine(); + } + + [Fact] + public void WriteLine() + { + VerifyLogged(() => Debug.WriteLine(5), "5" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine((string)null), Environment.NewLine); + VerifyLogged(() => Debug.WriteLine((object)null), Environment.NewLine); + VerifyLogged(() => Debug.WriteLine(5, "category"), "category: 5" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine((object)null, "category"), "category: " + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine("logged"), "logged" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine("logged", "category"), "category: logged" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine("logged", (string)null), "logged" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLine("{0} {1}", 'a', 'b'), "a b" + Environment.NewLine); + } + + [Fact] + public void WriteIf() + { + VerifyLogged(() => Debug.WriteIf(true, 5), "5"); + VerifyLogged(() => Debug.WriteIf(false, 5), ""); + + VerifyLogged(() => Debug.WriteIf(true, 5, "category"), "category: 5"); + VerifyLogged(() => Debug.WriteIf(false, 5, "category"), ""); + + VerifyLogged(() => Debug.WriteIf(true, "logged"), "logged"); + VerifyLogged(() => Debug.WriteIf(false, "logged"), ""); + + VerifyLogged(() => Debug.WriteIf(true, "logged", "category"), "category: logged"); + VerifyLogged(() => Debug.WriteIf(false, "logged", "category"), ""); + + GoToNextLine(); + } + + [Fact] + public void WriteLineIf() + { + VerifyLogged(() => Debug.WriteLineIf(true, 5), "5" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLineIf(false, 5), ""); + + VerifyLogged(() => Debug.WriteLineIf(true, 5, "category"), "category: 5" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLineIf(false, 5, "category"), ""); + + VerifyLogged(() => Debug.WriteLineIf(true, "logged"), "logged" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLineIf(false, "logged"), ""); + + VerifyLogged(() => Debug.WriteLineIf(true, "logged", "category"), "category: logged" + Environment.NewLine); + VerifyLogged(() => Debug.WriteLineIf(false, "logged", "category"), ""); + } + + [Theory] + [InlineData(-1, 0)] + [InlineData(0, 0)] + [InlineData(1, 1)] + [InlineData(2, 2)] + [InlineData(3, 3)] + [InlineData(4, 4)] + public void IndentSize_Set_GetReturnsExpected(int indentSize, int expectedIndentSize) + { + Debug.IndentLevel = 0; + + Debug.IndentSize = indentSize; + Assert.Equal(expectedIndentSize, Debug.IndentSize); + VerifyLogged(() => Debug.WriteLine("pizza"), "pizza" + Environment.NewLine); + + // Indent once. + Debug.Indent(); + string expectedIndentOnce = new string(' ', expectedIndentSize); + VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentOnce + "pizza" + Environment.NewLine); + + // Indent again. + Debug.Indent(); + string expectedIndentTwice = new string(' ', expectedIndentSize * 2); + VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentTwice + "pizza" + Environment.NewLine); + + // Unindent. + Debug.Unindent(); + Debug.Unindent(); + } + + [Theory] + [InlineData(-1, 0)] + [InlineData(0, 0)] + [InlineData(1, 1)] + public void IndentLevel_Set_GetReturnsExpected(int indentLevel, int expectedIndentLevel) + { + Debug.IndentLevel = indentLevel; + Assert.Equal(expectedIndentLevel, Debug.IndentLevel); + string expectedIndentOnce = new string(' ', expectedIndentLevel * Debug.IndentSize); + VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentOnce + "pizza" + Environment.NewLine); + + // Indent once. + Debug.Indent(); + Assert.Equal(expectedIndentLevel + 1, Debug.IndentLevel); + string expectedIndentTwice = new string(' ', (expectedIndentLevel + 1) * Debug.IndentSize); + VerifyLogged(() => Debug.WriteLine("pizza"), expectedIndentTwice + "pizza" + Environment.NewLine); + + // Unindent. + Debug.Unindent(); + Assert.Equal(expectedIndentLevel, Debug.IndentLevel); + Debug.Unindent(); + } + } +} diff --git a/src/System.Diagnostics.Debug/tests/DebugTestsUsingListeners.cs b/src/System.Diagnostics.Debug/tests/DebugTestsUsingListeners.cs new file mode 100644 index 000000000000..992a861d741b --- /dev/null +++ b/src/System.Diagnostics.Debug/tests/DebugTestsUsingListeners.cs @@ -0,0 +1,248 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#define DEBUG +using System.Reflection; +using Xunit; + +namespace System.Diagnostics.Tests +{ + // These tests test the static Debug class. They cannot be run in parallel + // Shows that Debug behaves identically on all of the test cases in DebugTestsNoListeners even when DebugUsesTraceListeners is true. + [Collection("System.Diagnostics.Debug")] + public class DebugTestsUsingListeners : DebugTestsNoListeners + { + protected override bool DebugUsesTraceListeners { get { return true; } } + + [Fact] + public void Trace_Write_TraceListenerAlwaysIndentsOnFirstCall() + { + Trace.Indent(); + int expectedIndentation = Trace.IndentLevel * Trace.IndentSize; + + VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + VerifyLogged(() => Debug.Write("pizza"), "pizza"); + VerifyLogged(() => Trace.Write("pizza"), "pizza"); + + Trace.Listeners.Clear(); + // New TraceListener indents on first Debug call + Trace.Listeners.Add(new DefaultTraceListener()); + VerifyLogged(() => Trace.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + VerifyLogged(() => Debug.Write("pizza"), "pizza"); + + Trace.Listeners.Clear(); + // New TraceListener indents on first Trace call + Trace.Listeners.Add(new DefaultTraceListener()); + VerifyLogged(() => Debug.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + VerifyLogged(() => Trace.Write("pizza"), "pizza"); + + TraceListener secondListener = new DefaultTraceListener(); + Trace.Listeners.Add(secondListener); + // Only new TraceListener will indent on its first Trace/Debug call: + VerifyLogged(() => Debug.Write("pizza"), "pizza" + new string(' ', expectedIndentation) + "pizza"); + VerifyLogged(() => Trace.Write("pizza"), "pizza" + "pizza"); + + TraceListener thirdListener = new DefaultTraceListener(); + Trace.Listeners.Add(thirdListener); + // Only new TraceListener will indent on its first Trace/Debug call: + VerifyLogged(() => Trace.Write("pizza"), "pizza" + "pizza" + new string(' ', expectedIndentation) + "pizza"); + VerifyLogged(() => Debug.Write("pizza"), "pizza" + "pizza" + "pizza"); + + Trace.Listeners.Remove(secondListener); + Trace.Listeners.Remove(thirdListener); + Trace.Unindent(); + GoToNextLine(); + } + + [Fact] + public void Trace_Write_Indents() + { + // This test when run alone verifies Trace.Write indentation, even on first call, is correct. + Trace.Indent(); + VerifyLogged(() => Trace.Write("pizza"), new string(' ', Trace.IndentLevel * Trace.IndentSize) + "pizza"); + Trace.Unindent(); + + GoToNextLine(); + } + + [Fact] + public void Trace_WriteLine_Indents() + { + // This test when run alone verifies Debug.WriteLine indentation, even on first call, is correct. + Debug.Indent(); + VerifyLogged(() => Trace.WriteLine("pizza"), new string(' ', Trace.IndentLevel * Trace.IndentSize) + "pizza" + Environment.NewLine); + Debug.Unindent(); + } + + [Fact] + public void Trace_WriteLine_WontIndentAfterWrite() + { + Trace.Indent(); + int expectedIndentation = Trace.IndentLevel * Trace.IndentSize; + + VerifyLogged(() => Trace.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + + // WriteLine wont indent after Write: + VerifyLogged(() => Trace.WriteLine("pizza"), "pizza" + Environment.NewLine); + + VerifyLogged(() => Trace.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); + VerifyLogged(() => Trace.Write("pizza"), new string(' ', expectedIndentation) + "pizza"); + + // WriteLine wont indent after Write: + VerifyLogged(() => Trace.WriteLine("pizza"), "pizza" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLine("pizza"), new string(' ', expectedIndentation) + "pizza" + Environment.NewLine); + Trace.Unindent(); + } + + [Fact] + public void Debug_WriteLine_SameIndentationForBothTraceAndDebug() + { + Trace.Indent(); + int expected = Debug.IndentSize * Debug.IndentLevel; + + VerifyLogged(() => Debug.WriteLine("pizza"), new string(' ', expected) + "pizza" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLine("pizza"), new string(' ', expected) + "pizza" + Environment.NewLine); + + // reset + Trace.Unindent(); + } + + [Fact] + public void Trace_WriteNull_SkipsIndentation() + { + Trace.Indent(); + VerifyLogged(() => Debug.Write(null), ""); + VerifyLogged(() => Trace.Write(null), ""); + Trace.Unindent(); + } + + [Fact] + public void Trace_WriteLineNull_IndentsEmptyStringProperly() + { + Trace.Indent(); + int expected = Debug.IndentSize * Debug.IndentLevel; + + VerifyLogged(() => Debug.WriteLine(null), new string(' ', expected) + Environment.NewLine); + VerifyLogged(() => Trace.WriteLine(null), new string(' ', expected) + Environment.NewLine); + + // reset + Trace.Unindent(); + } + + [Fact] + public void Trace_UpdatingDebugIndentation_UpdatesTraceIndentation_AndViceVersa() + { + int before = Debug.IndentSize * Debug.IndentLevel; + Assert.Equal(Debug.IndentSize, Trace.IndentSize); + Assert.Equal(Debug.IndentSize, Trace.Listeners[0].IndentSize); + Assert.Equal(Debug.IndentLevel, Trace.IndentLevel); + Assert.Equal(Debug.IndentLevel, Trace.Listeners[0].IndentLevel); + + Debug.IndentLevel = 3; + Assert.Equal(3, Trace.IndentLevel); + Assert.Equal(3, Debug.IndentLevel); + Assert.Equal(3, Trace.Listeners[0].IndentLevel); + + Debug.IndentLevel = 0; + Assert.Equal(0, Trace.IndentLevel); + Assert.Equal(0, Debug.IndentLevel); + Assert.Equal(0, Trace.Listeners[0].IndentLevel); + + Debug.Indent(); + Assert.Equal(1, Trace.IndentLevel); + Assert.Equal(1, Debug.IndentLevel); + Assert.Equal(1, Trace.Listeners[0].IndentLevel); + + Trace.Indent(); + Assert.Equal(2, Trace.IndentLevel); + Assert.Equal(2, Debug.IndentLevel); + Assert.Equal(2, Trace.Listeners[0].IndentLevel); + + Debug.Unindent(); + Trace.Unindent(); + Assert.Equal(0, Trace.IndentLevel); + Assert.Equal(0, Debug.IndentLevel); + Assert.Equal(0, Trace.Listeners[0].IndentLevel); + + Debug.Unindent(); + Assert.Equal(0, Trace.IndentLevel); + Assert.Equal(0, Debug.IndentLevel); + Assert.Equal(0, Trace.Listeners[0].IndentLevel); + + Trace.IndentSize = 7; + Assert.Equal(7, Debug.IndentSize); + Assert.Equal(7, Trace.Listeners[0].IndentSize); + + Debug.IndentSize = 4; + Assert.Equal(4, Trace.IndentSize); + Assert.Equal(4, Trace.Listeners[0].IndentSize); + + Debug.IndentLevel = 0; // reset + Assert.Equal(before, Debug.IndentSize * Debug.IndentLevel); + } + + [Fact] + public void Trace_Refresh_ResetsIndentSize() + { + int before = Debug.IndentSize * Debug.IndentLevel; + Debug.IndentSize = 5; + Debug.IndentLevel = 3; + Trace.Refresh(); + + Assert.Equal(4, Debug.IndentSize); + Assert.Equal(3, Debug.IndentLevel); + + Debug.IndentLevel = 0; // reset + Assert.Equal(before, Debug.IndentSize * Debug.IndentLevel); + } + + [Fact] + public void Trace_ClearTraceListeners_StopsWritingToDebugger() + { + VerifyLogged(() => Debug.Write("pizza"), "pizza"); + VerifyLogged(() => Trace.Write("pizza"), "pizza"); + Trace.Listeners.Clear(); + VerifyLogged(() => Debug.Write("pizza"), string.Empty); + VerifyLogged(() => Trace.Write("pizza"), string.Empty); + Trace.Refresh(); + VerifyLogged(() => Debug.Write("pizza"), "pizza"); + VerifyLogged(() => Trace.Write("pizza"), "pizza"); + + GoToNextLine(); + } + + [Fact] + public void TraceWriteIf() + { + VerifyLogged(() => Trace.WriteIf(true, 5), "5"); + VerifyLogged(() => Trace.WriteIf(false, 5), ""); + + VerifyLogged(() => Trace.WriteIf(true, 5, "category"), "category: 5"); + VerifyLogged(() => Trace.WriteIf(false, 5, "category"), ""); + + VerifyLogged(() => Trace.WriteIf(true, "logged"), "logged"); + VerifyLogged(() => Trace.WriteIf(false, "logged"), ""); + + VerifyLogged(() => Trace.WriteIf(true, "logged", "category"), "category: logged"); + VerifyLogged(() => Trace.WriteIf(false, "logged", "category"), ""); + + GoToNextLine(); + } + + [Fact] + public void TraceWriteLineIf() + { + VerifyLogged(() => Trace.WriteLineIf(true, 5), "5" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLineIf(false, 5), ""); + + VerifyLogged(() => Trace.WriteLineIf(true, 5, "category"), "category: 5" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLineIf(false, 5, "category"), ""); + + VerifyLogged(() => Trace.WriteLineIf(true, "logged"), "logged" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLineIf(false, "logged"), ""); + + VerifyLogged(() => Trace.WriteLineIf(true, "logged", "category"), "category: logged" + Environment.NewLine); + VerifyLogged(() => Trace.WriteLineIf(false, "logged", "category"), ""); + } + } +} diff --git a/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj b/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj index 01cdceb90d46..ef77ea42d71b 100644 --- a/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj +++ b/src/System.Diagnostics.Debug/tests/System.Diagnostics.Debug.Tests.csproj @@ -18,12 +18,16 @@ - - + + + + + + diff --git a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs index 9b1ed127d8e9..0c653310c02b 100644 --- a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs +++ b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs @@ -19,31 +19,6 @@ namespace System.Diagnostics /// public class DefaultTraceListener : TraceListener { - private class DefaultTraceDebugProvider : DebugProvider - { - private const int InternalWriteSize = 16384; - - public override void Write(string message) - { - // really huge messages mess up both VS and dbmon, so we chop it up into - // reasonable chunks if it's too big - if (message == null || message.Length <= InternalWriteSize) - { - base.Write(message); - } - else - { - int offset; - for (offset = 0; offset < message.Length - InternalWriteSize; offset += InternalWriteSize) - { - base.Write(message.Substring(offset, InternalWriteSize)); - } - base.Write(message.Substring(offset)); - } - } - } - - private static readonly DebugProvider s_provider = new DefaultTraceDebugProvider(); private bool _assertUIEnabled; private bool _settingsInitialized; private string _logFileName; @@ -137,14 +112,12 @@ private void InitializeSettings() private void WriteAssert(string stackTrace, string message, string detailMessage) { - // Tracked by #32955: WriteAssert should indent "assertMessage" same way Debug.Fail does. - string assertMessage = SR.DebugAssertBanner + Environment.NewLine - + SR.DebugAssertShortMessage + Environment.NewLine - + message + Environment.NewLine - + SR.DebugAssertLongMessage + Environment.NewLine + - detailMessage + Environment.NewLine - + stackTrace; - WriteLine(assertMessage); + WriteLine(SR.DebugAssertBanner + Environment.NewLine + + SR.DebugAssertShortMessage + Environment.NewLine + + message + Environment.NewLine + + SR.DebugAssertLongMessage + Environment.NewLine + + detailMessage + Environment.NewLine + + stackTrace); } /// @@ -179,13 +152,22 @@ private void WriteLine(string message, bool useLogFile) private void Write(string message, bool useLogFile) { - if (NeedIndent) + if (message == null) + { + message = string.Empty; + } + + if (NeedIndent && message.Length != 0) + { WriteIndent(); + } - s_provider.Write(message); + DebugProvider.WriteCore(message); if (useLogFile && !string.IsNullOrEmpty(LogFileName)) + { WriteToLogFile(message); + } } private void WriteToLogFile(string message) diff --git a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs index 973d7cbc370a..832701d669ae 100644 --- a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs +++ b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs @@ -13,17 +13,35 @@ internal static class TraceInternal { private class TraceProvider : DebugProvider { + public override void OnIndentLevelChanged(int indentLevel) + { + lock (TraceInternal.critSec) + { + foreach (TraceListener listener in Listeners) + { + listener.IndentLevel = indentLevel; + } + } + } + + public override void OnIndentSizeChanged(int indentSize) + { + lock (TraceInternal.critSec) + { + foreach (TraceListener listener in Listeners) + { + listener.IndentSize = indentSize; + } + } + } public override void Write(string message) { TraceInternal.Write(message); } + public override void WriteLine(string message) { TraceInternal.WriteLine(message); } } - private static readonly DebugProvider s_provider = new TraceProvider(); private static volatile string s_appName = null; private static volatile TraceListenerCollection s_listeners; private static volatile bool s_autoFlush; private static volatile bool s_useGlobalLock; - [ThreadStatic] - private static int t_indentLevel; - private static volatile int s_indentSize; private static volatile bool s_settingsInitialized; @@ -42,16 +60,16 @@ public static TraceListenerCollection Listeners { if (s_listeners == null) { + // This is where we override default DebugProvider because we know + // for sure that we have some Listeners to write to. + Debug.SetProvider(new TraceProvider()); // In the absence of config support, the listeners by default add // DefaultTraceListener to the listener collection. s_listeners = new TraceListenerCollection(); TraceListener defaultListener = new DefaultTraceListener(); - defaultListener.IndentLevel = t_indentLevel; - defaultListener.IndentSize = s_indentSize; + defaultListener.IndentLevel = Debug.IndentLevel; + defaultListener.IndentSize = Debug.IndentSize; s_listeners.Add(defaultListener); - // This is where we override default DebugProvider because we know - // for sure that we have some Listeners to write to. - Debug.SetProvider(s_provider); } } } @@ -103,29 +121,11 @@ public static bool UseGlobalLock public static int IndentLevel { - get { return t_indentLevel; } + get { return Debug.IndentLevel; } set { - // Use global lock - lock (critSec) - { - // We don't want to throw here -- it is very bad form to have debug or trace - // code throw exceptions! - if (value < 0) - { - value = 0; - } - t_indentLevel = value; - - if (s_listeners != null) - { - foreach (TraceListener listener in Listeners) - { - listener.IndentLevel = t_indentLevel; - } - } - } + Debug.IndentLevel = value; } } @@ -133,73 +133,23 @@ public static int IndentSize { get { - InitializeSettings(); - return s_indentSize; + return Debug.IndentSize; } set { - InitializeSettings(); - SetIndentSize(value); - } - } - - private static void SetIndentSize(int value) - { - // Use global lock - lock (critSec) - { - // We don't want to throw here -- it is very bad form to have debug or trace - // code throw exceptions! - if (value < 0) - { - value = 0; - } - - s_indentSize = value; - - if (s_listeners != null) - { - foreach (TraceListener listener in Listeners) - { - listener.IndentSize = s_indentSize; - } - } + Debug.IndentSize = value; } } public static void Indent() { - // Use global lock - lock (critSec) - { - InitializeSettings(); - if (t_indentLevel < int.MaxValue) - { - t_indentLevel++; - } - foreach (TraceListener listener in Listeners) - { - listener.IndentLevel = t_indentLevel; - } - } + Debug.IndentLevel++; } public static void Unindent() { - // Use global lock - lock (critSec) - { - InitializeSettings(); - if (t_indentLevel > 0) - { - t_indentLevel--; - } - foreach (TraceListener listener in Listeners) - { - listener.IndentLevel = t_indentLevel; - } - } + Debug.IndentLevel--; } public static void Flush() @@ -347,7 +297,6 @@ private static void InitializeSettings() { if (!s_settingsInitialized) { - SetIndentSize(DiagnosticsConfiguration.IndentSize); s_autoFlush = DiagnosticsConfiguration.AutoFlush; s_useGlobalLock = DiagnosticsConfiguration.UseGlobalLock; s_settingsInitialized = true; @@ -364,6 +313,7 @@ internal static void Refresh() { s_settingsInitialized = false; s_listeners = null; + Debug.IndentSize = DiagnosticsConfiguration.IndentSize; } InitializeSettings(); }