diff --git a/test/Dapr.E2E.Test/DaprCommand.cs b/test/Dapr.E2E.Test/DaprCommand.cs index 768e81960..1206cb163 100644 --- a/test/Dapr.E2E.Test/DaprCommand.cs +++ b/test/Dapr.E2E.Test/DaprCommand.cs @@ -16,6 +16,7 @@ namespace Dapr.E2E.Test using System; using System.Collections.Generic; using System.Diagnostics; + using System.Drawing; using System.Linq; using System.Threading; using Xunit.Abstractions; @@ -23,6 +24,7 @@ namespace Dapr.E2E.Test public class DaprCommand { private readonly ITestOutputHelper output; + private readonly CircularBuffer logBuffer = new CircularBuffer(1000); public DaprCommand(ITestOutputHelper output) { @@ -66,7 +68,11 @@ public void Run() var done = outputReceived.WaitOne(this.Timeout); if (!done) { - throw new Exception($"Command: \"{this.Command}\" timed out while waiting for output: \"{this.OutputToMatch}\""); + var ex = new Exception($"Command: \"{this.Command}\" timed out while waiting for output: \"{this.OutputToMatch}\""); + // we add here the log buffer of the last 1000 lines, of the application log + // to make it easier to debug failing tests + ex.Data.Add("log", this.logBuffer.ToArray()); + throw ex; } } @@ -79,8 +85,7 @@ private void CheckOutput(object sendingProcess, DataReceivedEventArgs e) try { - // see: https://github.com/xunit/xunit/issues/2146 - this.output.WriteLine(e.Data.TrimEnd(Environment.NewLine.ToCharArray())); + WriteLine(e.Data); } catch (InvalidOperationException) { @@ -101,12 +106,81 @@ private void OnErrorOutput(object sender, DataReceivedEventArgs e) try { - // see: https://github.com/xunit/xunit/issues/2146 - this.output.WriteLine(e.Data.TrimEnd(Environment.NewLine.ToCharArray())); + WriteLine(e.Data); } catch (InvalidOperationException) { } } + + private void WriteLine(string message) + { + // see: https://github.com/xunit/xunit/issues/2146 + var formattedMessage = message.TrimEnd(Environment.NewLine.ToCharArray()); + this.output.WriteLine(formattedMessage); + this.logBuffer.Add(formattedMessage); + } + } + + /// + /// A circular buffer that can be used to store a fixed number of items. + /// When the buffer is full, the oldest item is overwritten. + /// The buffer can be read in the same order as the items were added. + /// More information can be found here. + /// + /// + /// The buffer gets initialized by the call to the constructor and will allocate, + /// the memory for the buffer. The buffer is not resizable. + /// That means be carefull with , because it can cause an . + /// + /// The type of what the cicular buffer is off. + internal class CircularBuffer{ + private readonly int size; + private readonly T[] buffer; + private int readPosition = 0; + private int writePosition = 0; + /// + /// Initialize the buffer with the buffer size of . + /// + /// + /// The size the buffer will have + /// + public CircularBuffer(int size) + { + this.size = size; + buffer = new T[size]; + } + /// + /// Adds an item and move the write position to the next value + /// + /// The item that should be written. + public void Add(T item) + { + buffer[writePosition] = item; + writePosition = (writePosition + 1) % size; + } + /// + /// Reads on value and move the position to the next value + /// + /// + public T Read(){ + var value = buffer[readPosition]; + readPosition = (readPosition + 1) % size; + return value; + } + /// + /// Read the full buffer. + /// While the buffer is read, the read position is moved to the next value + /// + /// + public T[] ToArray() + { + var result = new T[size]; + for (int i = 0; i < size; i++) + { + result[i] = Read(); + } + return result; + } } }