StyleIncludeTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Controls;
  5. using Avalonia.Markup.Xaml.Styling;
  6. using Avalonia.Markup.Xaml.XamlIl.Runtime;
  7. using Avalonia.Media;
  8. using Avalonia.Platform;
  9. using Avalonia.Styling;
  10. using Avalonia.Themes.Simple;
  11. using Avalonia.UnitTests;
  12. using Xunit;
  13. namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
  14. public class StyleIncludeTests
  15. {
  16. [Fact]
  17. public void StyleInclude_Is_Built()
  18. {
  19. using (UnitTestApplication.Start(TestServices.StyledWindow
  20. .With(theme: () => new Styles())))
  21. {
  22. var xaml = @"
  23. <ContentControl xmlns='https://github.com/avaloniaui'>
  24. <ContentControl.Styles>
  25. <StyleInclude Source='avares://Avalonia.Markup.Xaml.UnitTests/Xaml/Style1.xaml'/>
  26. </ContentControl.Styles>
  27. </ContentControl>";
  28. var window = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
  29. Assert.IsType<Style>(window.Styles[0]);
  30. }
  31. }
  32. [Fact]
  33. public void StyleInclude_Is_Built_Resources()
  34. {
  35. using (UnitTestApplication.Start(TestServices.StyledWindow
  36. .With(theme: () => new Styles())))
  37. {
  38. var xaml = @"
  39. <ContentControl xmlns='https://github.com/avaloniaui'
  40. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  41. <ContentControl.Resources>
  42. <StyleInclude x:Key='Include' Source='avares://Avalonia.Markup.Xaml.UnitTests/Xaml/Style1.xaml'/>
  43. </ContentControl.Resources>
  44. </ContentControl>";
  45. var contentControl = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
  46. Assert.IsType<Style>(contentControl.Resources["Include"]);
  47. }
  48. }
  49. [Fact]
  50. public void StyleInclude_Is_Resolved_With_Two_Files()
  51. {
  52. var documents = new[]
  53. {
  54. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Style.xaml"), @"
  55. <Style xmlns='https://github.com/avaloniaui'
  56. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  57. <Style.Resources>
  58. <Color x:Key='Red'>Red</Color>
  59. </Style.Resources>
  60. </Style>"),
  61. new RuntimeXamlLoaderDocument(@"
  62. <ContentControl xmlns='https://github.com/avaloniaui'
  63. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  64. <ContentControl.Resources>
  65. <StyleInclude x:Key='Include' Source='avares://Tests/Style.xaml'/>
  66. </ContentControl.Resources>
  67. </ContentControl>")
  68. };
  69. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  70. var style = Assert.IsType<Style>(objects[0]);
  71. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  72. Assert.IsType<Style>(contentControl.Resources["Include"]);
  73. }
  74. [Fact]
  75. public void Relative_Back_StyleInclude_Is_Resolved_With_Two_Files()
  76. {
  77. var documents = new[]
  78. {
  79. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Subfolder/Style.xaml"), @"
  80. <Style xmlns='https://github.com/avaloniaui'
  81. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  82. <Style.Resources>
  83. <Color x:Key='Red'>Red</Color>
  84. </Style.Resources>
  85. </Style>"),
  86. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Subfolder/Folder/Root.xaml"), @"
  87. <ContentControl xmlns='https://github.com/avaloniaui'
  88. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  89. <ContentControl.Resources>
  90. <StyleInclude x:Key='Include' Source='../Style.xaml'/>
  91. </ContentControl.Resources>
  92. </ContentControl>")
  93. };
  94. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  95. var style = Assert.IsType<Style>(objects[0]);
  96. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  97. Assert.IsType<Style>(contentControl.Resources["Include"]);
  98. }
  99. [Fact]
  100. public void Relative_Root_StyleInclude_Is_Resolved_With_Two_Files()
  101. {
  102. var documents = new[]
  103. {
  104. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Style.xaml"), @"
  105. <Style xmlns='https://github.com/avaloniaui'
  106. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  107. <Style.Resources>
  108. <Color x:Key='Red'>Red</Color>
  109. </Style.Resources>
  110. </Style>"),
  111. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Root.xaml"), @"
  112. <ContentControl xmlns='https://github.com/avaloniaui'
  113. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  114. <ContentControl.Resources>
  115. <StyleInclude x:Key='Include' Source='/Style.xaml'/>
  116. </ContentControl.Resources>
  117. </ContentControl>")
  118. };
  119. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  120. var style = Assert.IsType<Style>(objects[0]);
  121. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  122. Assert.IsType<Style>(contentControl.Resources["Include"]);
  123. }
  124. [Fact]
  125. public void Relative_StyleInclude_Is_Resolved_With_Two_Files()
  126. {
  127. var documents = new[]
  128. {
  129. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Style.xaml"), @"
  130. <Style xmlns='https://github.com/avaloniaui'
  131. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  132. <Style.Resources>
  133. <Color x:Key='Red'>Red</Color>
  134. </Style.Resources>
  135. </Style>"),
  136. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Root.xaml"), @"
  137. <ContentControl xmlns='https://github.com/avaloniaui'
  138. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  139. <ContentControl.Resources>
  140. <StyleInclude x:Key='Include' Source='Style.xaml'/>
  141. </ContentControl.Resources>
  142. </ContentControl>")
  143. };
  144. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  145. var style = Assert.IsType<Style>(objects[0]);
  146. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  147. Assert.IsType<Style>(contentControl.Resources["Include"]);
  148. }
  149. [Fact]
  150. public void Relative_Dot_Syntax__StyleInclude_Is_Resolved_With_Two_Files()
  151. {
  152. var documents = new[]
  153. {
  154. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Style.xaml"), @"
  155. <Style xmlns='https://github.com/avaloniaui'
  156. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  157. <Style.Resources>
  158. <Color x:Key='Red'>Red</Color>
  159. </Style.Resources>
  160. </Style>"),
  161. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Folder/Root.xaml"), @"
  162. <ContentControl xmlns='https://github.com/avaloniaui'
  163. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  164. <ContentControl.Resources>
  165. <StyleInclude x:Key='Include' Source='./Style.xaml'/>
  166. </ContentControl.Resources>
  167. </ContentControl>")
  168. };
  169. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  170. var style = Assert.IsType<Style>(objects[0]);
  171. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  172. Assert.IsType<Style>(contentControl.Resources["Include"]);
  173. }
  174. [Fact]
  175. public void NonLatin_StyleInclude_Is_Resolved_With_Two_Files()
  176. {
  177. var documents = new[]
  178. {
  179. new RuntimeXamlLoaderDocument(new Uri("avares://アセンブリ/スタイル.xaml"), @"
  180. <Style xmlns='https://github.com/avaloniaui'
  181. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  182. <Style.Resources>
  183. <Color x:Key='Red'>Red</Color>
  184. </Style.Resources>
  185. </Style>"),
  186. new RuntimeXamlLoaderDocument(@"
  187. <ContentControl xmlns='https://github.com/avaloniaui'
  188. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  189. <ContentControl.Resources>
  190. <StyleInclude x:Key='Include' Source='avares://アセンブリ/スタイル.xaml'/>
  191. </ContentControl.Resources>
  192. </ContentControl>")
  193. };
  194. var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  195. var style = Assert.IsType<Style>(objects[0]);
  196. var contentControl = Assert.IsType<ContentControl>(objects[1]);
  197. Assert.IsType<Style>(contentControl.Resources["Include"]);
  198. }
  199. [Fact]
  200. public void Missing_ResourceKey_In_StyleInclude_Does_Not_Cause_StackOverflow()
  201. {
  202. var documents = new[]
  203. {
  204. new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Style.xaml"), @"
  205. <Style xmlns='https://github.com/avaloniaui'
  206. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  207. <Style.Resources>
  208. <StaticResource x:Key='brush' ResourceKey='missing' />
  209. </Style.Resources>
  210. </Style>"),
  211. new RuntimeXamlLoaderDocument(@"
  212. <ContentControl xmlns='https://github.com/avaloniaui'
  213. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
  214. <ContentControl.Styles>
  215. <StyleInclude Source='avares://Tests/Style.xaml'/>
  216. </ContentControl.Styles>
  217. </ContentControl>")
  218. };
  219. try
  220. {
  221. _ = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
  222. }
  223. catch (KeyNotFoundException)
  224. {
  225. }
  226. }
  227. [Fact]
  228. public void StyleInclude_Should_Be_Replaced_With_Direct_Call()
  229. {
  230. var control = (ContentControl)AvaloniaRuntimeXamlLoader.Load(@"
  231. <ContentControl xmlns='https://github.com/avaloniaui'
  232. xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
  233. xmlns:themes='clr-namespace:Avalonia.Themes.Simple;assembly=Avalonia.Themes.Simple'>
  234. <ContentControl.Styles>
  235. <themes:SimpleTheme />
  236. <StyleInclude Source='avares://Avalonia.Themes.Simple/SimpleTheme.xaml'/>
  237. </ContentControl.Styles>
  238. </ContentControl>");
  239. Assert.IsType<SimpleTheme>(control.Styles[0]);
  240. Assert.IsType<SimpleTheme>(control.Styles[1]);
  241. }
  242. [Fact]
  243. public void StyleInclude_From_CodeBehind_Resolves_Compiled()
  244. {
  245. using var locatorScope = AvaloniaLocator.EnterScope();
  246. AvaloniaLocator.CurrentMutable.BindToSelf<IAssetLoader>(new AssetLoader(GetType().Assembly));
  247. var sp = new TestServiceProvider();
  248. var styleInclude = new StyleInclude(sp)
  249. {
  250. Source = new Uri("avares://Avalonia.Markup.Xaml.UnitTests/Xaml/StyleWithServiceLocator.xaml")
  251. };
  252. var loaded = Assert.IsType<StyleWithServiceLocator>(styleInclude.Loaded);
  253. Assert.Equal(
  254. sp.GetService<IAvaloniaXamlIlParentStackProvider>().Parents,
  255. loaded.ServiceProvider.GetService<IAvaloniaXamlIlParentStackProvider>().Parents);
  256. }
  257. private class TestServiceProvider : IServiceProvider, IUriContext, IAvaloniaXamlIlParentStackProvider
  258. {
  259. private IServiceProvider _root = XamlIlRuntimeHelpers.CreateRootServiceProviderV2();
  260. public object GetService(Type serviceType)
  261. {
  262. if (serviceType == typeof(IUriContext))
  263. {
  264. return this;
  265. }
  266. if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider))
  267. {
  268. return this;
  269. }
  270. return _root.GetService(serviceType);
  271. }
  272. public Uri BaseUri { get; set; }
  273. public IEnumerable<object> Parents { get; } = new[] { new ContentControl() };
  274. }
  275. }