Skip to content

Commit 8febb35

Browse files
committed
Merge pull request #1026 from libgit2/jamill/set_error
Try harder to format error messages
2 parents 7f0cc1e + 983ce49 commit 8febb35

File tree

4 files changed

+244
-2
lines changed

4 files changed

+244
-2
lines changed

LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<Compile Include="RefSpecFixture.cs" />
6464
<Compile Include="EqualityFixture.cs" />
6565
<Compile Include="RevertFixture.cs" />
66+
<Compile Include="SetErrorFixture.cs" />
6667
<Compile Include="SignatureFixture.cs" />
6768
<Compile Include="FilterBranchFixture.cs" />
6869
<Compile Include="RemoveFixture.cs" />

LibGit2Sharp.Tests/SetErrorFixture.cs

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using LibGit2Sharp.Tests.TestHelpers;
5+
using Xunit;
6+
7+
namespace LibGit2Sharp.Tests
8+
{
9+
public class SetErrorFixture : BaseFixture
10+
{
11+
12+
private const string simpleExceptionMessage = "This is a simple exception message.";
13+
private const string aggregateExceptionMessage = "This is aggregate exception.";
14+
private const string outerExceptionMessage = "This is an outer exception.";
15+
private const string innerExceptionMessage = "This is an inner exception.";
16+
private const string innerExceptionMessage2 = "This is inner exception #2.";
17+
18+
private const string expectedInnerExceptionHeaderText = "Inner Exception:";
19+
private const string expectedAggregateExceptionHeaderText = "Contained Exception:";
20+
private const string expectedAggregateExceptionsHeaderText = "Contained Exceptions:";
21+
22+
[Fact]
23+
public void FormatSimpleException()
24+
{
25+
Exception exceptionToThrow = new Exception(simpleExceptionMessage);
26+
string expectedMessage = simpleExceptionMessage;
27+
28+
AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow);
29+
}
30+
31+
[Fact]
32+
public void FormatExceptionWithInnerException()
33+
{
34+
Exception exceptionToThrow = new Exception(outerExceptionMessage, new Exception(innerExceptionMessage));
35+
36+
StringBuilder sb = new StringBuilder();
37+
sb.AppendLine(outerExceptionMessage);
38+
sb.AppendLine();
39+
AppendIndentedLine(sb, expectedInnerExceptionHeaderText, 0);
40+
AppendIndentedText(sb, innerExceptionMessage, 1);
41+
string expectedMessage = sb.ToString();
42+
43+
AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow);
44+
}
45+
46+
[Fact]
47+
public void FormatAggregateException()
48+
{
49+
Exception exceptionToThrow = new AggregateException(aggregateExceptionMessage, new Exception(innerExceptionMessage), new Exception(innerExceptionMessage2));
50+
51+
StringBuilder sb = new StringBuilder();
52+
sb.AppendLine(aggregateExceptionMessage);
53+
sb.AppendLine();
54+
55+
AppendIndentedLine(sb, expectedAggregateExceptionsHeaderText, 0);
56+
57+
AppendIndentedLine(sb, innerExceptionMessage, 1);
58+
sb.AppendLine();
59+
60+
AppendIndentedText(sb, innerExceptionMessage2, 1);
61+
62+
string expectedMessage = sb.ToString();
63+
64+
AssertExpectedExceptionMessage(expectedMessage, exceptionToThrow);
65+
}
66+
67+
private void AssertExpectedExceptionMessage(string expectedMessage, Exception exceptionToThrow)
68+
{
69+
Exception thrownException = null;
70+
71+
ObjectId id = new ObjectId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
72+
73+
string repoPath = InitNewRepository();
74+
using (var repo = new Repository(repoPath))
75+
{
76+
repo.ObjectDatabase.AddBackend(new ThrowingOdbBackend(exceptionToThrow), priority: 1);
77+
78+
try
79+
{
80+
repo.Lookup<Blob>(id);
81+
}
82+
catch (Exception ex)
83+
{
84+
thrownException = ex;
85+
}
86+
}
87+
88+
Assert.NotNull(thrownException);
89+
Assert.Equal(expectedMessage, thrownException.Message);
90+
}
91+
92+
private void AppendIndentedText(StringBuilder sb, string text, int indentLevel)
93+
{
94+
sb.AppendFormat("{0}{1}", IndentString(indentLevel), text);
95+
}
96+
97+
private void AppendIndentedLine(StringBuilder sb, string text, int indentLevel)
98+
{
99+
sb.AppendFormat("{0}{1}{2}", IndentString(indentLevel), text, Environment.NewLine);
100+
}
101+
102+
private string IndentString(int level)
103+
{
104+
return new string(' ', level * 4);
105+
}
106+
107+
#region ThrowingOdbBackend
108+
109+
private class ThrowingOdbBackend : OdbBackend
110+
{
111+
private Exception exceptionToThrow;
112+
113+
public ThrowingOdbBackend(Exception exceptionToThrow)
114+
{
115+
this.exceptionToThrow = exceptionToThrow;
116+
}
117+
118+
protected override OdbBackendOperations SupportedOperations
119+
{
120+
get
121+
{
122+
return OdbBackendOperations.Read |
123+
OdbBackendOperations.ReadPrefix |
124+
OdbBackendOperations.Write |
125+
OdbBackendOperations.WriteStream |
126+
OdbBackendOperations.Exists |
127+
OdbBackendOperations.ExistsPrefix |
128+
OdbBackendOperations.ForEach |
129+
OdbBackendOperations.ReadHeader;
130+
}
131+
}
132+
133+
public override int Read(ObjectId oid, out UnmanagedMemoryStream data, out ObjectType objectType)
134+
{
135+
throw this.exceptionToThrow;
136+
}
137+
138+
public override int ReadPrefix(string shortSha, out ObjectId id, out UnmanagedMemoryStream data, out ObjectType objectType)
139+
{
140+
throw this.exceptionToThrow;
141+
}
142+
143+
public override int Write(ObjectId oid, Stream dataStream, long length, ObjectType objectType)
144+
{
145+
throw this.exceptionToThrow;
146+
}
147+
148+
public override int WriteStream(long length, ObjectType objectType, out OdbBackendStream stream)
149+
{
150+
throw this.exceptionToThrow;
151+
}
152+
153+
public override bool Exists(ObjectId oid)
154+
{
155+
throw this.exceptionToThrow;
156+
}
157+
158+
public override int ExistsPrefix(string shortSha, out ObjectId found)
159+
{
160+
throw this.exceptionToThrow;
161+
}
162+
163+
public override int ReadHeader(ObjectId oid, out int length, out ObjectType objectType)
164+
{
165+
throw this.exceptionToThrow;
166+
}
167+
168+
public override int ReadStream(ObjectId oid, out OdbBackendStream stream)
169+
{
170+
throw this.exceptionToThrow;
171+
}
172+
173+
public override int ForEach(ForEachCallback callback)
174+
{
175+
throw this.exceptionToThrow;
176+
}
177+
}
178+
179+
#endregion
180+
181+
}
182+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ static NativeMethods()
8585
[DllImport(libgit2)]
8686
internal static extern void giterr_set_str(
8787
GitErrorCategory error_class,
88-
string errorString);
88+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string errorString);
8989

9090
[DllImport(libgit2)]
9191
internal static extern void giterr_set_oom();

LibGit2Sharp/Core/Proxy.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Runtime.InteropServices;
7+
using System.Text;
78
using System.Threading;
89
using LibGit2Sharp.Core.Handles;
910
using LibGit2Sharp.Handlers;
@@ -23,7 +24,7 @@ public static void giterr_set_str(GitErrorCategory error_class, Exception except
2324
}
2425
else
2526
{
26-
NativeMethods.giterr_set_str(error_class, exception.Message);
27+
NativeMethods.giterr_set_str(error_class, ErrorMessageFromException(exception));
2728
}
2829
}
2930

@@ -32,6 +33,64 @@ public static void giterr_set_str(GitErrorCategory error_class, String errorStri
3233
NativeMethods.giterr_set_str(error_class, errorString);
3334
}
3435

36+
/// <summary>
37+
/// This method will take an exception and try to generate an error message
38+
/// that captures the important messages of the error.
39+
/// The formatting is a bit subjective.
40+
/// </summary>
41+
/// <param name="ex"></param>
42+
/// <returns></returns>
43+
public static string ErrorMessageFromException(Exception ex)
44+
{
45+
StringBuilder sb = new StringBuilder();
46+
BuildErrorMessageFromException(sb, 0, ex);
47+
return sb.ToString();
48+
}
49+
50+
private static void BuildErrorMessageFromException(StringBuilder sb, int level, Exception ex)
51+
{
52+
string indent = new string(' ', level * 4);
53+
sb.AppendFormat("{0}{1}", indent, ex.Message);
54+
55+
if (ex is AggregateException)
56+
{
57+
AggregateException aggregateException = ((AggregateException)ex).Flatten();
58+
59+
if (aggregateException.InnerExceptions.Count == 1)
60+
{
61+
sb.AppendLine();
62+
sb.AppendLine();
63+
64+
sb.AppendFormat("{0}Contained Exception:{1}", indent, Environment.NewLine);
65+
BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerException);
66+
}
67+
else
68+
{
69+
sb.AppendLine();
70+
sb.AppendLine();
71+
72+
sb.AppendFormat("{0}Contained Exceptions:{1}", indent, Environment.NewLine);
73+
for (int i = 0; i < aggregateException.InnerExceptions.Count; i++)
74+
{
75+
if (i != 0)
76+
{
77+
sb.AppendLine();
78+
sb.AppendLine();
79+
}
80+
81+
BuildErrorMessageFromException(sb, level + 1, aggregateException.InnerExceptions[i]);
82+
}
83+
}
84+
}
85+
else if (ex.InnerException != null)
86+
{
87+
sb.AppendLine();
88+
sb.AppendLine();
89+
sb.AppendFormat("{0}Inner Exception:{1}", indent, Environment.NewLine);
90+
BuildErrorMessageFromException(sb, level + 1, ex.InnerException);
91+
}
92+
}
93+
3594
#endregion
3695

3796
#region git_blame_

0 commit comments

Comments
 (0)