LoggingTests.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Avalonia.Controls;
  5. using Avalonia.Controls.Shapes;
  6. using Avalonia.Logging;
  7. using Avalonia.Markup.Xaml;
  8. using Avalonia.UnitTests;
  9. using Avalonia.Utilities;
  10. using Xunit;
  11. namespace Avalonia.Base.UnitTests.Logging
  12. {
  13. public class LoggingTests
  14. {
  15. [Fact]
  16. public void Control_Should_Not_Log_Binding_Errors_When_Detached_From_Visual_Tree()
  17. {
  18. using (UnitTestApplication.Start(TestServices.StyledWindow))
  19. {
  20. var xaml = @"
  21. <Window xmlns='https://github.com/avaloniaui'
  22. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  23. xmlns:local='clr-namespace:Avalonia.Base.UnitTests.Logging;assembly=Avalonia.UnitTests'>
  24. <Panel Name='panel'>
  25. <Rectangle Name='rect' Fill='{Binding $parent[Window].Background}'/>
  26. </Panel>
  27. </Window>";
  28. var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
  29. using var logSink = new StubLogSink(LogEventLevel.Warning);
  30. var panel = window.FindControl<Panel>("panel");
  31. var rect = window.FindControl<Rectangle>("rect");
  32. window.ApplyTemplate();
  33. window.Presenter.ApplyTemplate();
  34. panel.Children.Remove(rect);
  35. Assert.Equal(0, logSink.Results.Count);
  36. }
  37. }
  38. }
  39. class StubLogSink : ILogSink, IDisposable
  40. {
  41. LogEventLevel _level;
  42. public StubLogSink(LogEventLevel level)
  43. {
  44. _level = level;
  45. Logger.Sink = this;
  46. }
  47. public void Dispose()
  48. {
  49. Logger.Sink = null;
  50. }
  51. public List<string> Results { get; set; } = new List<string>();
  52. public bool IsEnabled(LogEventLevel level, string area)
  53. {
  54. return true;
  55. }
  56. public void Log(LogEventLevel level, string area, object source, string messageTemplate)
  57. {
  58. if (level >= _level)
  59. {
  60. Results.Add(Format<object, object, object>(area, messageTemplate, source));
  61. }
  62. }
  63. public void Log<T0>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
  64. {
  65. if (level >= _level)
  66. {
  67. Results.Add(Format<T0, object, object>(area, messageTemplate, source, propertyValue0));
  68. }
  69. }
  70. public void Log<T0, T1>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
  71. {
  72. if (level >= _level)
  73. {
  74. Results.Add(Format<T0, T1, object>(area, messageTemplate, source, propertyValue0, propertyValue1));
  75. }
  76. }
  77. public void Log<T0, T1, T2>(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
  78. {
  79. if (level >= _level)
  80. {
  81. Results.Add(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2));
  82. }
  83. }
  84. public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
  85. {
  86. if (level >= _level)
  87. {
  88. Results.Add(Format(area, messageTemplate, source, propertyValues));
  89. }
  90. }
  91. #region Copy-Pasta
  92. private static string Format<T0, T1, T2>(
  93. string area,
  94. string template,
  95. object source,
  96. T0 v0 = default,
  97. T1 v1 = default,
  98. T2 v2 = default)
  99. {
  100. var result = new StringBuilder(template.Length);
  101. var r = new CharacterReader(template.AsSpan());
  102. var i = 0;
  103. result.Append('[');
  104. result.Append(area);
  105. result.Append("] ");
  106. while (!r.End)
  107. {
  108. var c = r.Take();
  109. if (c != '{')
  110. {
  111. result.Append(c);
  112. }
  113. else
  114. {
  115. if (r.Peek != '{')
  116. {
  117. result.Append('\'');
  118. result.Append(i++ switch
  119. {
  120. 0 => v0,
  121. 1 => v1,
  122. 2 => v2,
  123. _ => null
  124. });
  125. result.Append('\'');
  126. r.TakeUntil('}');
  127. r.Take();
  128. }
  129. else
  130. {
  131. result.Append('{');
  132. r.Take();
  133. }
  134. }
  135. }
  136. if (source is object)
  137. {
  138. result.Append(" (");
  139. result.Append(source.GetType().Name);
  140. result.Append(" #");
  141. result.Append(source.GetHashCode());
  142. result.Append(')');
  143. }
  144. return result.ToString();
  145. }
  146. private static string Format(
  147. string area,
  148. string template,
  149. object source,
  150. object[] v)
  151. {
  152. var result = new StringBuilder(template.Length);
  153. var r = new CharacterReader(template.AsSpan());
  154. var i = 0;
  155. result.Append('[');
  156. result.Append(area);
  157. result.Append(']');
  158. while (!r.End)
  159. {
  160. var c = r.Take();
  161. if (c != '{')
  162. {
  163. result.Append(c);
  164. }
  165. else
  166. {
  167. if (r.Peek != '{')
  168. {
  169. result.Append('\'');
  170. result.Append(i < v.Length ? v[i++] : null);
  171. result.Append('\'');
  172. r.TakeUntil('}');
  173. r.Take();
  174. }
  175. else
  176. {
  177. result.Append('{');
  178. r.Take();
  179. }
  180. }
  181. }
  182. if (source is object)
  183. {
  184. result.Append('(');
  185. result.Append(source.GetType().Name);
  186. result.Append(" #");
  187. result.Append(source.GetHashCode());
  188. result.Append(')');
  189. }
  190. return result.ToString();
  191. }
  192. #endregion
  193. }
  194. }