public void appending_multi_lines_to_empty_lines()
 {
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be("|");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be("|" + Environment.NewLine + "|");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine + Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be("|" + Environment.NewLine + "|" + Environment.NewLine + "|");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine + Environment.NewLine + "a";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be("|" + Environment.NewLine + "|" + Environment.NewLine + "|" + Environment.NewLine + "|a");
     }
 }
        public void appending_multi_lines_with_prefixLastEmptyLine()
        {
            string text = @"First line.
Second line.


";
            {
                StringBuilder b = new StringBuilder();
                string        t = b.AppendMultiLine("|", text, true, prefixLastEmptyLine: false).ToString();
                t.Should().Be(@"|First line.
|Second line.
|
|".NormalizeEOL());
            }

            {
                StringBuilder b = new StringBuilder();
                string        t = b.AppendMultiLine("|", text, true, prefixLastEmptyLine: true).ToString();
                t.Should().Be(@"|First line.
|Second line.
|
|
|".NormalizeEOL());
            }
        }
 public void appending_multi_lines_to_empty_lines()
 {
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo("|"));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo("|" + Environment.NewLine + "|"));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine + Environment.NewLine;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo("|" + Environment.NewLine + "|" + Environment.NewLine + "|"));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = Environment.NewLine + Environment.NewLine + Environment.NewLine + "a";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo("|" + Environment.NewLine + "|" + Environment.NewLine + "|" + Environment.NewLine + "|a"));
     }
 }
Exemplo n.º 4
0
 static StringBuilder AppendField(StringBuilder b, string prefix, string label, string text)
 {
     b.Append(prefix).Append(label).Append(": ").Append(' ', 10 - label.Length);
     prefix += new string( ' ', 12 );
     b.AppendMultiLine(prefix, text, false).AppendLine();
     return(b);
 }
        long PrefixWithOurExtension(Stopwatch w, string f, string[] results)
        {
            GC.Collect();
            w.Restart();
            StringBuilder b = new StringBuilder();

            for (int i = 0; i < results.Length; ++i)
            {
                // We must use the prefixLastEmptyLine to match the way the naive implementation works.
                results[i] = b.AppendMultiLine(prefix, f, false, prefixLastEmptyLine: true).ToString();
                b.Clear();
            }
            w.Stop();
            return(w.ElapsedTicks);
        }
        public void appending_multi_lines_with_a_prefix_with_null_or_empty_or_one_line()
        {
            {
                StringBuilder b = new StringBuilder();
                string text = @"One line.";
                string t = b.AppendMultiLine( "|", text, true ).ToString();
                Assert.That( t, Is.EqualTo( @"|One line." ) );
            }
            {
                StringBuilder b = new StringBuilder();
                string text = @"";
                string t = b.AppendMultiLine( "|", text, true ).ToString();
                Assert.That( t, Is.EqualTo( @"|" ) );
            }
            {
                StringBuilder b = new StringBuilder();
                string text = null;
                string t = b.AppendMultiLine( "|", text, true ).ToString();
                Assert.That( t, Is.EqualTo( @"|" ) );
            }
            {
                StringBuilder b = new StringBuilder();
                string text = @"One line.";
                string t = b.AppendMultiLine( "|", text, false ).ToString();
                Assert.That( t, Is.EqualTo( @"One line." ) );
            }
            {
                StringBuilder b = new StringBuilder();
                string text = @"";
                string t = b.AppendMultiLine( "|", text, false ).ToString();
                Assert.That( t, Is.EqualTo( @"" ) );
            }
            {
                StringBuilder b = new StringBuilder();
                string text = null;
                string t = b.AppendMultiLine( "|", text, false ).ToString();
                Assert.That( t, Is.EqualTo( @"" ) );
            }

        }
 public void appending_multi_lines_with_a_prefix_with_null_or_empty_or_one_line()
 {
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"One line.";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo(@"|One line."));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo(@"|"));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = null;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         Assert.That(t, Is.EqualTo(@"|"));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"One line.";
         string        t    = b.AppendMultiLine("|", text, false).ToString();
         Assert.That(t, Is.EqualTo(@"One line."));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"";
         string        t    = b.AppendMultiLine("|", text, false).ToString();
         Assert.That(t, Is.EqualTo(@""));
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = null;
         string        t    = b.AppendMultiLine("|", text, false).ToString();
         Assert.That(t, Is.EqualTo(@""));
     }
 }
        public void appending_multi_lines_with_a_prefix()
        {
            {
                StringBuilder b    = new StringBuilder();
                string        text = @"First line.
Second line.
    Indented.

    Also indented.
Last line.";
                // Here, normalizing the source embedded string is to support
                // git clone with LF in files instead of CRLF.
                // Our (slow) AppendMultiLine normalizes the end of lines to Environment.NewLine.
                string t = b.AppendMultiLine("|", text, true).ToString();
                t.Should().Be(@"|First line.
|Second line.
|    Indented.
|
|    Also indented.
|Last line.".NormalizeEOL());
            }

            {
                StringBuilder b    = new StringBuilder();
                string        text = @"First line.
Second line.
    Indented.

    Also indented.
Last line.";
                string        t    = b.AppendMultiLine("|", text, false).ToString();
                t.Should().Be(@"First line.
|Second line.
|    Indented.
|
|    Also indented.
|Last line.".NormalizeEOL());
            }
        }
 public void appending_multi_lines_with_a_prefix_with_null_or_empty_or_one_line()
 {
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"One line.";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be(@"|One line.");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"";
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be(@"|");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = null;
         string        t    = b.AppendMultiLine("|", text, true).ToString();
         t.Should().Be(@"|");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"One line.";
         string        t    = b.AppendMultiLine("|", text, false).ToString();
         t.Should().Be(@"One line.");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = @"";
         string        t    = b.AppendMultiLine("|", text, false).ToString();;
         t.Should().Be(@"");
     }
     {
         StringBuilder b    = new StringBuilder();
         string        text = null;
         string        t    = b.AppendMultiLine("|", text, false).ToString();
         t.Should().Be(@"");
     }
 }
Exemplo n.º 10
0
        static void DumpSecrets(UserKeyVault v)
        {
            string FirstPadding(bool missing)
            {
                return(missing ? "[Missing]" : "         ");
            }

            string PaddingByDepth(int depth) => new string( ' ', depth * 5 );

            void DoesThingWithGray(Action action)
            {
                var prev = Console.ForegroundColor;

                Console.ForegroundColor = ConsoleColor.Gray;
                action();
                Console.ForegroundColor = prev;
            }

            void WhitePipe() => DoesThingWithGray(() => Console.Write("│"));

            void RightArrow()
            {
                DoesThingWithGray(() => Console.Write("└────┬> "));
            }

            foreach (var k in v.KeyStore.Infos)
            {
                if (k.SuperKey != null)
                {
                    continue;
                }
                Console.ForegroundColor = k.IsSecretAvailable ? ConsoleColor.Green : ConsoleColor.Red;
                Console.Write(FirstPadding(!k.IsSecretAvailable));
                WhitePipe();
                Console.WriteLine(k.Name);
                Console.ForegroundColor = ConsoleColor.Gray;
                StringBuilder b = new StringBuilder();
                b.AppendMultiLine(FirstPadding(false) + "│", k.Description, true, false);
                b.AppendLine();
                Console.Write(b);
                var  sub = k.SubKey;
                bool displayedAvailable = k.IsSecretAvailable;
                int  depth = 0;
                while (sub != null)
                {
                    if (sub.IsSecretAvailable)
                    {
                        Console.ForegroundColor = displayedAvailable ? ConsoleColor.DarkGreen : ConsoleColor.Green;
                        displayedAvailable      = true;
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                    }
                    Console.Write(FirstPadding(!sub.IsSecretAvailable));
                    Console.Write(PaddingByDepth(depth));
                    RightArrow();
                    Console.WriteLine(sub.Name);
                    Console.ForegroundColor = ConsoleColor.Gray;
                    depth++;
                    b.Clear();
                    b.AppendMultiLine(FirstPadding(false) + PaddingByDepth(depth) + "│ ", sub.Description, true, false);
                    b.AppendLine();
                    Console.Write(b.ToString());
                    sub = sub.SubKey;
                }
                Console.WriteLine(FirstPadding(false));
            }
        }
        /// <summary>
        /// Recursively dumps an <see cref="Exception"/> as readable text.
        /// </summary>
        /// <param name="w">The TextWriter to write to.</param>
        /// <param name="prefix">Prefix that will start all lines.</param>
        /// <param name="displayMessage">Whether the exception message must be displayed or skip.</param>
        /// <param name="ex">The exception to display.</param>
        static public void DumpException( StringBuilder w, string prefix, bool displayMessage, Exception ex )
        {
            CKException ckEx = ex as CKException;
            if( ckEx != null && ckEx.ExceptionData != null )
            {
                ckEx.ExceptionData.ToStringBuilder( w, prefix );
                return;
            }

            string header = String.Format( " ┌──────────────────────────■ Exception : {0} ■──────────────────────────", ex.GetType().Name );

            string p;
            w.AppendLine( prefix + header );
            string localPrefix = prefix + " | ";
            if( displayMessage && ex.Message != null )
            {
                w.Append( localPrefix + "Message: " );
                w.AppendMultiLine( localPrefix + "         ", ex.Message, false );
                w.AppendLine();
            }
            if( ex.StackTrace != null )
            {
                w.Append( localPrefix + "Stack: " );
                w.AppendMultiLine( localPrefix + "       ", ex.StackTrace, false );
                w.AppendLine();
            }
            var fileNFEx = ex as System.IO.FileNotFoundException;
            if( fileNFEx != null )
            {
                if( !String.IsNullOrEmpty( fileNFEx.FileName ) ) w.AppendLine( localPrefix + "FileName: " + fileNFEx.FileName );
                #if NET451 || NET46
                if( fileNFEx.FusionLog != null )
                {
                    w.Append( localPrefix + "FusionLog: " );
                    w.AppendMultiLine( localPrefix + "         ", fileNFEx.FusionLog, false );
                    w.AppendLine();
                }
                #endif
            }
            else
            {
                var loadFileEx = ex as System.IO.FileLoadException;
                if( loadFileEx != null )
                {
                    if( !String.IsNullOrEmpty( loadFileEx.FileName ) ) w.AppendLine( localPrefix + "FileName: " + loadFileEx.FileName );
                    #if NET451 || NET46
                    if( loadFileEx.FusionLog != null )
                    {
                        w.Append( localPrefix + "FusionLog: " );
                        w.AppendMultiLine( localPrefix + "         ", loadFileEx.FusionLog, false );
                        w.AppendLine();
                    }
                    #endif
                }
                else
                {
                    var typeLoadEx = ex as ReflectionTypeLoadException;
                    if( typeLoadEx != null )
                    {
                        w.AppendLine( localPrefix + " ┌──────────────────────────■ [Loader Exceptions] ■──────────────────────────" );
                        p = localPrefix + " | ";
                        foreach( var item in typeLoadEx.LoaderExceptions )
                        {
                            DumpException( w, p, true, item );
                        }
                        w.AppendLine( localPrefix + " └─────────────────────────────────────────────────────────────────────────" );
                    }
                    #if NET451 || NET46
                    else
                    {
                        var configEx = ex as System.Configuration.ConfigurationException;
                        if( configEx != null )
                        {
                            if( !String.IsNullOrEmpty( configEx.Filename ) ) w.AppendLine( localPrefix + "FileName: " + configEx.Filename );
                        }
                    }
                    #endif
                }
            }
            // The InnerException of an aggregated exception is the same as the first of it InnerExceptionS.
            // (The InnerExceptionS are the contained/aggregated exceptions of the AggregatedException object.)
            // This is why, if we are on an AggregatedException we do not follow its InnerException.
            var aggrex = ex as AggregateException;
            if( aggrex != null && aggrex.InnerExceptions.Count > 0 )
            {
                w.AppendLine( localPrefix + " ┌──────────────────────────■ [Aggregated Exceptions] ■──────────────────────────" );
                p = localPrefix + " | ";
                foreach( var item in aggrex.InnerExceptions )
                {
                    DumpException( w, p, true, item );
                }
                w.AppendLine( localPrefix + " └─────────────────────────────────────────────────────────────────────────" );
            }
            else if( ex.InnerException != null )
            {
                w.AppendLine( localPrefix + " ┌──────────────────────────■ [Inner Exception] ■──────────────────────────" );
                p = localPrefix + " | ";
                DumpException( w, p, true, ex.InnerException );
                w.AppendLine( localPrefix + " └─────────────────────────────────────────────────────────────────────────" );
            }
            w.AppendLine( prefix + " └" + new string( '─', header.Length - 2 ) );
        }
Exemplo n.º 12
0
        /// <summary>
        /// Format the <paramref name="logEntry"/>
        /// </summary>
        /// <param name="logEntry"></param>
        /// <returns>A possible first entry - for monitor numbering - and the entry itself.</returns>
        public (FormattedEntry Before, FormattedEntry Entry) FormatEntry(IMulticastLogEntry logEntry)
        {
            FormattedEntry before        = default;
            string         formattedDate = GetFormattedDate(logEntry);

            char   logLevel          = logEntry.LogLevel.ToChar();
            string indentationPrefix = ActivityMonitorTextHelperClient.GetMultilinePrefixWithDepth(logEntry.Text != null ? logEntry.GroupDepth : logEntry.GroupDepth - 1);

            if (!_monitorNames.TryGetValue(logEntry.MonitorId, out string?monitorId))
            {
                string _monitorResetLog = "";
                if (_monitorNames.Count - 1 == _maxMonitorCount)
                {
                    ClearMonitorNames();
                    _monitorResetLog = $" Monitor reset count {_maxMonitorCount}.";
                }
                monitorId = B64ConvertInt(_monitorNames.Count);
                _monitorNames.Add(logEntry.MonitorId, monitorId);
                Debug.Assert(LogLevel.Info.ToChar() == 'i');
                before = new FormattedEntry('i',
                                            indentationPrefix,
                                            monitorId,
                                            formattedDate,
                                            $" [] Monitor: ~{logEntry.MonitorId}. {_monitorResetLog}");
            }
            string multiLinePrefix = _blankSpacePrefix + indentationPrefix;

            if (logEntry.Text != null)
            {
                Debug.Assert(logEntry.LogType != LogEntryType.CloseGroup);
                if (logEntry.LogType == LogEntryType.OpenGroup)
                {
                    _builder.Append("> ");
                }
                _builder.Append(" [").Append(logEntry.Tags).Append("] ");
                multiLinePrefix += "   ";
                _builder.AppendMultiLine(multiLinePrefix, logEntry.Text, false);
                if (logEntry.Exception != null)
                {
                    _builder.AppendLine();
                    logEntry.Exception.ToStringBuilder(_builder, multiLinePrefix, false);
                }
            }
            else
            {
                Debug.Assert(logEntry.Conclusions != null);
                _builder.Append("< ");
                if (logEntry.Conclusions.Count > 0)
                {
                    if (logEntry.Conclusions.Count == 1)
                    {
                        _builder.AppendMultiLine(multiLinePrefix + ' ', logEntry.Conclusions.Single().Text, false);
                    }
                    else
                    {
                        _builder.Append(logEntry.Conclusions.Count).Append(" conclusion");
                        if (logEntry.Conclusions.Count > 1)
                        {
                            _builder.Append('s');
                        }
                        _builder.Append(':').AppendLine();
                        multiLinePrefix += ' ';
                        bool first = true;
                        foreach (var c in logEntry.Conclusions)
                        {
                            if (!first)
                            {
                                _builder.AppendLine();
                            }
                            first = false;
                            _builder.AppendMultiLine(multiLinePrefix + ' ', c.Text, true);
                        }
                    }
                }
            }
            string outputLine = _builder.ToString();

            _builder.Clear();
            return(before, new FormattedEntry(logLevel,
                                              indentationPrefix,
                                              monitorId,
                                              formattedDate,
                                              outputLine));
        }
        public void appending_multi_lines_with_a_prefix()
        {
            {
                StringBuilder b = new StringBuilder();
                string text = @"First line.
Second line.
    Indented.

    Also indented.
Last line.";
                // Here, normalizing the source embedded string is to support 
                // git clone with LF in files instead of CRLF. 
                // Our (slow) AppendMultiLine normalizes the end of lines to Environment.NewLine.
                string t = b.AppendMultiLine( "|", text, true ).ToString();
                Assert.That( t, Is.EqualTo( @"|First line.
|Second line.
|    Indented.
|
|    Also indented.
|Last line.".NormalizeEOL() ) );
            }

            {
                StringBuilder b = new StringBuilder();
                string text = @"First line.
Second line.
    Indented.

    Also indented.
Last line.";
                string t = b.AppendMultiLine( "|", text, false ).ToString();
                Assert.That( t, Is.EqualTo( @"First line.
|Second line.
|    Indented.
|
|    Also indented.
|Last line.".NormalizeEOL() ) );
            }

        }
        public void appending_multi_lines_with_prefixLastEmptyLine()
        {
            string text = @"First line.
Second line.


";
            {
                StringBuilder b = new StringBuilder();
                string t = b.AppendMultiLine( "|", text, true, prefixLastEmptyLine: false ).ToString();
                Assert.That( t, Is.EqualTo( @"|First line.
|Second line.
|
|".NormalizeEOL() ) );
            }

            {
                StringBuilder b = new StringBuilder();
                string t = b.AppendMultiLine( "|", text, true, prefixLastEmptyLine: true ).ToString();
                Assert.That( t, Is.EqualTo( @"|First line.
|Second line.
|
|
|".NormalizeEOL() ) );
            }
        }
Exemplo n.º 15
0
        /// <summary>
        /// Writes a log entry (that can actually be a <see cref="IMulticastLogEntry"/>).
        /// </summary>
        /// <param name="e">The log entry.</param>
        public void Write(IMulticastLogEntry e)
        {
            Debug.Assert(DateTimeStamp.MaxValue.ToString().Length == 32,
                         "DateTimeStamp FileNameUniqueTimeUtcFormat and the uniquifier: max => 32 characters long.");
            Debug.Assert(Guid.NewGuid().ToString().Length == 36,
                         "Guid => 18 characters long.");

            BeforeWrite();
            _builder.Append(' ', _nameLen + 32);
            _builder.Append("| ", e.Text != null ? e.GroupDepth : e.GroupDepth - 1);
            string prefix = _builder.ToString();

            _builder.Clear();
            // MonitorId (if needed) on one line.
            if (_currentMonitorId == e.MonitorId)
            {
                _builder.Append(' ', _nameLen + 1);
            }
            else
            {
                _currentMonitorId = e.MonitorId;
                if (!_monitorNames.TryGetValue(_currentMonitorId, out _currentMonitorName))
                {
                    _currentMonitorName = _monitorNames.Count.ToString("X" + _nameLen);
                    int len = _currentMonitorName.Length;
                    if (_nameLen < len)
                    {
                        prefix   = " " + prefix;
                        _nameLen = len;
                    }
                    _monitorNames.Add(_currentMonitorId, _currentMonitorName);
                    _builder.Append(_currentMonitorName)
                    .Append("~~~~")
                    .Append(' ', 28)
                    .Append("~~ Monitor: ")
                    .AppendLine(_currentMonitorId.ToString());
                    _builder.Append(' ', _nameLen + 1);
                }
                else
                {
                    _builder.Append(_currentMonitorName).Append('~');
                    _builder.Append(' ', _nameLen - _currentMonitorName.Length);
                }
            }
            // Log time prefixes the first line only.
            TimeSpan delta = e.LogTime.TimeUtc - _lastLogTime;

            if (delta >= TimeSpan.FromMinutes(1))
            {
                string logTime = e.LogTime.TimeUtc.ToString(FileUtil.FileNameUniqueTimeUtcFormat);
                _builder.Append(' ');
                _builder.Append(logTime);
                _builder.Append(' ');
                _lastLogTime = e.LogTime.TimeUtc;
            }
            else
            {
                _builder.Append(' ', 17);
                _builder.Append('+');
                _builder.Append(delta.ToString(@"ss\.fffffff"));
                _builder.Append(' ');
            }

            // Level is one char.
            char level;

            switch (e.LogLevel & LogLevel.Mask)
            {
            case LogLevel.Trace: level = ' '; break;

            case LogLevel.Info: level = 'i'; break;

            case LogLevel.Warn: level = 'W'; break;

            case LogLevel.Error: level = 'E'; break;

            default: level = 'F'; break;
            }
            _builder.Append(level);
            _builder.Append(' ');
            _builder.Append("| ", e.Text != null ? e.GroupDepth : e.GroupDepth - 1);

            if (e.Text != null)
            {
                if (e.LogType == LogEntryType.OpenGroup)
                {
                    _builder.Append("> ");
                }
                prefix += "  ";
                _builder.AppendMultiLine(prefix, e.Text, false).AppendLine();
                if (e.Exception != null)
                {
                    e.Exception.ToStringBuilder(_builder, prefix);
                }
            }
            else
            {
                Debug.Assert(e.Conclusions != null);
                _builder.Append("< ");
                if (e.Conclusions.Count > 0)
                {
                    _builder.Append(" | ").Append(e.Conclusions.Count).Append(" conclusion");
                    if (e.Conclusions.Count > 1)
                    {
                        _builder.Append('s');
                    }
                    _builder.Append(':').AppendLine();
                    prefix += "   | ";
                    foreach (var c in e.Conclusions)
                    {
                        _builder.AppendMultiLine(prefix, c.Text, true).AppendLine();
                    }
                }
                else
                {
                    _builder.AppendLine();
                }
            }
            _writer.Write(_builder.ToString());
            AfterWrite();
            _builder.Clear();
        }
Exemplo n.º 16
0
 static StringBuilder AppendField( StringBuilder b, string prefix, string label, string text )
 {
     b.Append( prefix ).Append( label ).Append( ": " ).Append( ' ', 10 - label.Length );
     prefix += new string( ' ', 12 );
     b.AppendMultiLine( prefix, text, false ).AppendLine();
     return b;
 }
 public void appending_multi_lines_to_empty_lines()
 {
     {
         StringBuilder b = new StringBuilder();
         string text = Environment.NewLine;
         string t = b.AppendMultiLine( "|", text, true ).ToString();
         Assert.That( t, Is.EqualTo( "|" ) );
     }
     {
         StringBuilder b = new StringBuilder();
         string text = Environment.NewLine + Environment.NewLine;
         string t = b.AppendMultiLine( "|", text, true ).ToString();
         Assert.That( t, Is.EqualTo( "|" + Environment.NewLine + "|" ) );
     }
     {
         StringBuilder b = new StringBuilder();
         string text = Environment.NewLine + Environment.NewLine + Environment.NewLine;
         string t = b.AppendMultiLine( "|", text, true ).ToString();
         Assert.That( t, Is.EqualTo( "|" + Environment.NewLine + "|" + Environment.NewLine + "|" ) );
     }
     {
         StringBuilder b = new StringBuilder();
         string text = Environment.NewLine + Environment.NewLine + Environment.NewLine + "a";
         string t = b.AppendMultiLine( "|", text, true ).ToString();
         Assert.That( t, Is.EqualTo( "|" + Environment.NewLine + "|" + Environment.NewLine + "|" + Environment.NewLine + "|a" ) );
     }
 }
 long PrefixWithOurExtension( Stopwatch w, string f, string[] results )
 {
     GC.Collect();
     w.Restart();
     StringBuilder b = new StringBuilder();
     for( int i = 0; i < results.Length; ++i )
     {
         // We must use the prefixLastEmptyLine to match the way the naive implementation works.
         results[i] = b.AppendMultiLine( prefix, f, false, prefixLastEmptyLine: true ).ToString();
         b.Clear();
     }
     w.Stop();
     return w.ElapsedTicks;
 }
        /// <summary>
        /// Recursively dumps an <see cref="Exception"/> as readable text.
        /// </summary>
        /// <param name="w">The TextWriter to write to.</param>
        /// <param name="prefix">Prefix that will start all lines.</param>
        /// <param name="displayMessage">Whether the exception message must be displayed or skip.</param>
        /// <param name="ex">The exception to display.</param>
        static public void DumpException(StringBuilder w, string prefix, bool displayMessage, Exception ex)
        {
            CKException ckEx = ex as CKException;

            if (ckEx != null && ckEx.ExceptionData != null)
            {
                ckEx.ExceptionData.ToStringBuilder(w, prefix);
                return;
            }

            string header = String.Format(" ┌──────────────────────────■ Exception : {0} ■──────────────────────────", ex.GetType().Name);

            string p;

            w.AppendLine(prefix + header);
            string localPrefix = prefix + " | ";

            if (displayMessage && ex.Message != null)
            {
                w.Append(localPrefix + "Message: ");
                w.AppendMultiLine(localPrefix + "         ", ex.Message, false);
                w.AppendLine();
            }
            if (ex.StackTrace != null)
            {
                w.Append(localPrefix + "Stack: ");
                w.AppendMultiLine(localPrefix + "       ", ex.StackTrace, false);
                w.AppendLine();
            }
            var fileNFEx = ex as System.IO.FileNotFoundException;

            if (fileNFEx != null)
            {
                if (!String.IsNullOrEmpty(fileNFEx.FileName))
                {
                    w.AppendLine(localPrefix + "FileName: " + fileNFEx.FileName);
                }
                #if NET451 || NET46
                if (fileNFEx.FusionLog != null)
                {
                    w.Append(localPrefix + "FusionLog: ");
                    w.AppendMultiLine(localPrefix + "         ", fileNFEx.FusionLog, false);
                    w.AppendLine();
                }
                #endif
            }
            else
            {
                var loadFileEx = ex as System.IO.FileLoadException;
                if (loadFileEx != null)
                {
                    if (!String.IsNullOrEmpty(loadFileEx.FileName))
                    {
                        w.AppendLine(localPrefix + "FileName: " + loadFileEx.FileName);
                    }
                    #if NET451 || NET46
                    if (loadFileEx.FusionLog != null)
                    {
                        w.Append(localPrefix + "FusionLog: ");
                        w.AppendMultiLine(localPrefix + "         ", loadFileEx.FusionLog, false);
                        w.AppendLine();
                    }
                    #endif
                }
                else
                {
                    var typeLoadEx = ex as ReflectionTypeLoadException;
                    if (typeLoadEx != null)
                    {
                        w.AppendLine(localPrefix + " ┌──────────────────────────■ [Loader Exceptions] ■──────────────────────────");
                        p = localPrefix + " | ";
                        foreach (var item in typeLoadEx.LoaderExceptions)
                        {
                            DumpException(w, p, true, item);
                        }
                        w.AppendLine(localPrefix + " └─────────────────────────────────────────────────────────────────────────");
                    }
                    #if NET451 || NET46
                    else
                    {
                        var configEx = ex as System.Configuration.ConfigurationException;
                        if (configEx != null)
                        {
                            if (!String.IsNullOrEmpty(configEx.Filename))
                            {
                                w.AppendLine(localPrefix + "FileName: " + configEx.Filename);
                            }
                        }
                    }
                    #endif
                }
            }
            // The InnerException of an aggregated exception is the same as the first of it InnerExceptionS.
            // (The InnerExceptionS are the contained/aggregated exceptions of the AggregatedException object.)
            // This is why, if we are on an AggregatedException we do not follow its InnerException.
            var aggrex = ex as AggregateException;
            if (aggrex != null && aggrex.InnerExceptions.Count > 0)
            {
                w.AppendLine(localPrefix + " ┌──────────────────────────■ [Aggregated Exceptions] ■──────────────────────────");
                p = localPrefix + " | ";
                foreach (var item in aggrex.InnerExceptions)
                {
                    DumpException(w, p, true, item);
                }
                w.AppendLine(localPrefix + " └─────────────────────────────────────────────────────────────────────────");
            }
            else if (ex.InnerException != null)
            {
                w.AppendLine(localPrefix + " ┌──────────────────────────■ [Inner Exception] ■──────────────────────────");
                p = localPrefix + " | ";
                DumpException(w, p, true, ex.InnerException);
                w.AppendLine(localPrefix + " └─────────────────────────────────────────────────────────────────────────");
            }
            w.AppendLine(prefix + " └" + new string( '─', header.Length - 2 ));
        }