2 changed files with 215 additions and 0 deletions
@ -0,0 +1,214 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Shapes; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using Avalonia.UnitTests; |
||||
|
using Avalonia.Utilities; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Base.UnitTests.Logging |
||||
|
{ |
||||
|
public class LoggingTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Control_Should_Not_Log_Binding_Errors_When_Detached_From_Visual_Tree() |
||||
|
{ |
||||
|
using (UnitTestApplication.Start(TestServices.StyledWindow)) |
||||
|
{ |
||||
|
var xaml = @"
|
||||
|
<Window xmlns='https://github.com/avaloniaui'
|
||||
|
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
|
||||
|
xmlns:local='clr-namespace:Avalonia.Base.UnitTests.Logging;assembly=Avalonia.UnitTests'> |
||||
|
<Panel Name='panel'> |
||||
|
<Rectangle Name='rect' Fill='{Binding $parent[Window].Background}'/> |
||||
|
</Panel> |
||||
|
</Window>";
|
||||
|
|
||||
|
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); |
||||
|
using var logSink = new StubLogSink(LogEventLevel.Warning); |
||||
|
var panel = window.FindControl<Panel>("panel"); |
||||
|
var rect = window.FindControl<Rectangle>("rect"); |
||||
|
window.ApplyTemplate(); |
||||
|
window.Presenter.ApplyTemplate(); |
||||
|
panel.Children.Remove(rect); |
||||
|
Assert.Equal(0, logSink.Results.Count); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class StubLogSink : ILogSink, IDisposable |
||||
|
{ |
||||
|
LogEventLevel _level; |
||||
|
public StubLogSink(LogEventLevel level) |
||||
|
{ |
||||
|
_level = level; |
||||
|
Logger.Sink = this; |
||||
|
} |
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Logger.Sink = null; |
||||
|
} |
||||
|
public List<string> Results { get; set; } = new List<string>(); |
||||
|
|
||||
|
public bool IsEnabled(LogEventLevel level, string area) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public void Log(LogEventLevel level, string area, object source, string messageTemplate) |
||||
|
{ |
||||
|
if (level >= _level) |
||||
|
{ |
||||
|
Results.Add(Format<object, object, object>(area, messageTemplate, source)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) |
||||
|
{ |
||||
|
if (level >= _level) |
||||
|
{ |
||||
|
Results.Add(Format<T0, object, object>(area, messageTemplate, source, propertyValue0)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) |
||||
|
{ |
||||
|
if (level >= _level) |
||||
|
{ |
||||
|
Results.Add(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) |
||||
|
{ |
||||
|
if (level >= _level) |
||||
|
{ |
||||
|
Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) |
||||
|
{ |
||||
|
if (level >= _level) |
||||
|
{ |
||||
|
Results.Add(Format(area, messageTemplate, source, propertyValues)); |
||||
|
} |
||||
|
} |
||||
|
#region Copy-Pasta
|
||||
|
private static string Format<T0, T1, T2>( |
||||
|
string area, |
||||
|
string template, |
||||
|
object source, |
||||
|
T0 v0 = default, |
||||
|
T1 v1 = default, |
||||
|
T2 v2 = default) |
||||
|
{ |
||||
|
var result = new StringBuilder(template.Length); |
||||
|
var r = new CharacterReader(template.AsSpan()); |
||||
|
var i = 0; |
||||
|
|
||||
|
result.Append('['); |
||||
|
result.Append(area); |
||||
|
result.Append("] "); |
||||
|
|
||||
|
while (!r.End) |
||||
|
{ |
||||
|
var c = r.Take(); |
||||
|
|
||||
|
if (c != '{') |
||||
|
{ |
||||
|
result.Append(c); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (r.Peek != '{') |
||||
|
{ |
||||
|
result.Append('\''); |
||||
|
result.Append(i++ switch |
||||
|
{ |
||||
|
0 => v0, |
||||
|
1 => v1, |
||||
|
2 => v2, |
||||
|
_ => null |
||||
|
}); |
||||
|
result.Append('\''); |
||||
|
r.TakeUntil('}'); |
||||
|
r.Take(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result.Append('{'); |
||||
|
r.Take(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (source is object) |
||||
|
{ |
||||
|
result.Append(" ("); |
||||
|
result.Append(source.GetType().Name); |
||||
|
result.Append(" #"); |
||||
|
result.Append(source.GetHashCode()); |
||||
|
result.Append(')'); |
||||
|
} |
||||
|
|
||||
|
return result.ToString(); |
||||
|
} |
||||
|
|
||||
|
private static string Format( |
||||
|
string area, |
||||
|
string template, |
||||
|
object source, |
||||
|
object[] v) |
||||
|
{ |
||||
|
var result = new StringBuilder(template.Length); |
||||
|
var r = new CharacterReader(template.AsSpan()); |
||||
|
var i = 0; |
||||
|
|
||||
|
result.Append('['); |
||||
|
result.Append(area); |
||||
|
result.Append(']'); |
||||
|
|
||||
|
while (!r.End) |
||||
|
{ |
||||
|
var c = r.Take(); |
||||
|
|
||||
|
if (c != '{') |
||||
|
{ |
||||
|
result.Append(c); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (r.Peek != '{') |
||||
|
{ |
||||
|
result.Append('\''); |
||||
|
result.Append(i < v.Length ? v[i++] : null); |
||||
|
result.Append('\''); |
||||
|
r.TakeUntil('}'); |
||||
|
r.Take(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result.Append('{'); |
||||
|
r.Take(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (source is object) |
||||
|
{ |
||||
|
result.Append('('); |
||||
|
result.Append(source.GetType().Name); |
||||
|
result.Append(" #"); |
||||
|
result.Append(source.GetHashCode()); |
||||
|
result.Append(')'); |
||||
|
} |
||||
|
|
||||
|
return result.ToString(); |
||||
|
} |
||||
|
#endregion
|
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue