diff options
author | nulltoken <emeric.fermas@gmail.com> | 2015-04-29 00:09:47 +0300 |
---|---|---|
committer | nulltoken <emeric.fermas@gmail.com> | 2015-04-29 00:09:47 +0300 |
commit | 8febb35461b3391851df7ec9ca25c34313e70a9c (patch) | |
tree | 77a24b6a6ecda62c3c8858edfaaf85409447e7c4 | |
parent | 7f0cc1e3d4eb99999991de06831c5b7d0a25cf03 (diff) | |
parent | 983ce4928390d89b77aba1d9ccb043699652d282 (diff) |
Merge pull request #1026 from libgit2/jamill/set_error
Try harder to format error messages
-rw-r--r-- | LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj | 1 | ||||
-rw-r--r-- | LibGit2Sharp.Tests/SetErrorFixture.cs | 182 | ||||
-rw-r--r-- | LibGit2Sharp/Core/NativeMethods.cs | 2 | ||||
-rw-r--r-- | LibGit2Sharp/Core/Proxy.cs | 61 |
4 files changed, 244 insertions, 2 deletions
diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index c477d4f8..904c427d 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -63,6 +63,7 @@ <Compile Include="RefSpecFixture.cs" /> <Compile Include="EqualityFixture.cs" /> <Compile Include="RevertFixture.cs" /> + <Compile Include="SetErrorFixture.cs" /> <Compile Include="SignatureFixture.cs" /> <Compile Include="FilterBranchFixture.cs" /> <Compile Include="RemoveFixture.cs" /> diff --git a/LibGit2Sharp.Tests/SetErrorFixture.cs b/LibGit2Sharp.Tests/SetErrorFixture.cs new file mode 100644 index 00000000..62bdeab8 --- /dev/null +++ b/LibGit2Sharp.Tests/SetErrorFixture.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using System.Text; +using LibGit2Sharp.Tests.TestHelpers; +using Xunit; + +namespace LibGit2Sharp.Tests +{ + public class SetErrorFixture : BaseFixture + { + + private const string simpleExceptionMessage = "This is a simple exception message."; + private const string aggregateExceptionMessage = "This is aggregate exception."; + private const string outerExceptionMessage = "This is an outer exception."; + private const string innerExceptionMessage = "This is an inner exception."; + private const string innerExceptionMessage2 = "This is inner exception #2."; + + private const string expectedInnerExceptionHeaderText = "Inner Exception:"; + private const string expectedAggregateExceptionHeaderText = "Contained Exception:"; + private const string expectedAggregateExceptionsHeaderText = "Contained Exceptions:"; + + [Fact] + public void FormatSimpleException() + { + Exception exceptionToThrow = new Exception(simpleExceptionMessage); + string expectedMessage = simpleExceptionMessage; + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + [Fact] + public void FormatExceptionWithInnerException() + { + Exception exceptionToThrow = new Exception(outerExceptionMessage, new Exception(innerExceptionMessage)); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(outerExceptionMessage); + sb.AppendLine(); + AppendIndentedLine(sb, expectedInnerExceptionHeaderText, 0); + AppendIndentedText(sb, innerExceptionMessage, 1); + string expectedMessage = sb.ToString(); + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + [Fact] + public void FormatAggregateException() + { + Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2)); + + StringBuilder sb = new StringBuilder(); + sb.AppendLine(aggregateExceptionMessage); + sb.AppendLine(); + + AppendIndentedLine(sb, expectedAggregateExceptionsHeaderText, 0); + + AppendIndentedLine(sb, innerExceptionMessage, 1); + sb.AppendLine(); + + AppendIndentedText(sb, innerExceptionMessage2, 1); + + string expectedMessage = sb.ToString(); + + AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow); + } + + private void AssertExpectedExceptionMessage(string expectedMessage, Exception exceptionToThrow) + { + Exception thrownException = null; + + ObjectId id = new ObjectId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + + string repoPath = InitNewRepository(); + using (var repo = new Repository(repoPath)) + { + repo.ObjectDatabase.AddBackend(new ThrowingOdbBackend(exceptionToThrow), priority: 1); + + try + { + repo.Lookup<Blob>(id); + } + catch (Exception ex) + { + thrownException = ex; + } + } + + Assert.NotNull(thrownException); + Assert.Equal(expectedMessage, thrownException.Message); + } + + private void AppendIndentedText(StringBuilder sb, string text, int indentLevel) + { + sb.AppendFormat("{0}{1}", IndentString(indentLevel), text); + } + + private void AppendIndentedLine(StringBuilder sb, string text, int indentLevel) + { + sb.AppendFormat("{0}{1}{2}", IndentString(indentLevel), text, Environment.NewLine); + } + + private string IndentString(int level) + { + return new string(' ', level * 4); + } + + #region ThrowingOdbBackend + + private class ThrowingOdbBackend : OdbBackend + { + private Exception exceptionToThrow; + + public ThrowingOdbBackend(Exception exceptionToThrow) + { + this.exceptionToThrow = exceptionToThrow; + } + + protected override OdbBackendOperations SupportedOperations + { + get + { + return OdbBackendOperations.Read | + OdbBackendOperations.ReadPrefix | + OdbBackendOperations.Write | + OdbBackendOperations.WriteStream | + OdbBackendOperations.Exists | + OdbBackendOperations.ExistsPrefix | + OdbBackendOperations.ForEach | + OdbBackendOperations.ReadHeader; + } + } + + public override int Read(ObjectId oid, out UnmanagedMemoryStream data, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int ReadPrefix(string shortSha, out ObjectId id, out UnmanagedMemoryStream data, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int Write(ObjectId oid, Stream dataStream, long length, ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int WriteStream(long length, ObjectType objectType, out OdbBackendStream stream) + { + throw this.exceptionToThrow; + } + + public override bool Exists(ObjectId oid) + { + throw this.exceptionToThrow; + } + + public override int ExistsPrefix(string shortSha, out ObjectId found) + { + throw this.exceptionToThrow; + } + + public override int ReadHeader(ObjectId oid, out int length, out ObjectType objectType) + { + throw this.exceptionToThrow; + } + + public override int ReadStream(ObjectId oid, out OdbBackendStream stream) + { + throw this.exceptionToThrow; + } + + public override int ForEach(ForEachCallback callback) + { + throw this.exceptionToThrow; + } + } + + #endregion + + } +} diff --git a/LibGit2Sharp/Core/NativeMethods.cs b/LibGit2Sharp/Core/NativeMethods.cs index e74e250b..b71d4708 100644 --- a/LibGit2Sharp/Core/NativeMethods.cs +++ b/LibGit2Sharp/Core/NativeMethods.cs @@ -85,7 +85,7 @@ namespace LibGit2Sharp.Core [DllImport(libgit2)] internal static extern void giterr_set_str( GitErrorCategory error_class, - string errorString); + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string errorString); [DllImport(libgit2)] internal static extern void giterr_set_oom(); diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs index 2ba1be49..a7b4d6ef 100644 --- a/LibGit2Sharp/Core/Proxy.cs +++ b/LibGit2Sharp/Core/Proxy.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using LibGit2Sharp.Core.Handles; using LibGit2Sharp.Handlers; @@ -23,7 +24,7 @@ namespace LibGit2Sharp.Core } else { - NativeMethods.giterr_set_str(error_class, exception.Message); + NativeMethods.giterr_set_str(error_class, ErrorMessageFromException(exception)); } } @@ -32,6 +33,64 @@ namespace LibGit2Sharp.Core NativeMethods.giterr_set_str(error_class, errorString); } + /// <summary> + /// This method will take an exception and try to generate an error message + /// that captures the important messages of the error. + /// The formatting is a bit subjective. + /// </summary> + /// <param name="ex"></param> + /// <returns></returns> + public static string ErrorMessageFromException(Exception ex) + { + StringBuilder sb = new StringBuilder(); + BuildErrorMessageFromException(sb, 0, ex); + return sb.ToString(); + } + + private static void BuildErrorMessageFromException(StringBuilder sb, int level, Exception ex) + { + string indent = new string(' ', level * 4); + sb.AppendFormat("{0}{1}", indent, ex.Message); + + if (ex is AggregateException) + { + AggregateException aggregateException = ((AggregateException)ex).Flatten(); + + if (aggregateException.InnerExceptions.Count == 1) + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerException); + } + else + { + sb.AppendLine(); + sb.AppendLine(); + + sb.AppendFormat("{0}Contained Exceptions:{1}", indent, Environment.NewLine); + for (int i = 0; i < aggregateException.InnerExceptions.Count; i++) + { + if (i != 0) + { + sb.AppendLine(); + sb.AppendLine(); + } + + BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerExceptions[i]); + } + } + } + else if (ex.InnerException != null) + { + sb.AppendLine(); + sb.AppendLine(); + sb.AppendFormat("{0}Inner Exception:{1}", indent, Environment.NewLine); + BuildErrorMessageFromException(sb, level + 1, ex.InnerException); + } + } + #endregion #region git_blame_ |