StyleIncludeTests.cs 11 KB

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